mirror of
https://github.com/containers/podman.git
synced 2025-05-20 16:47:39 +08:00
Add podman commit command
podman commit allows the user to commit containers as images with options of tagging th image, setting a commit message, setting the auther, and making changes to the instructions. Signed-off-by: umohnani8 <umohnani@redhat.com> Closes: #143 Approved by: rhatdan
This commit is contained in:
93
cmd/podman/commit.go
Normal file
93
cmd/podman/commit.go
Normal file
@ -0,0 +1,93 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
commitFlags = []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "change, c",
|
||||
Usage: "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "message, m",
|
||||
Usage: "Set commit message for imported image",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "author, a",
|
||||
Usage: "Set the author for the image comitted",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "pause, p",
|
||||
Usage: "Pause container during commit",
|
||||
},
|
||||
}
|
||||
commitDescription = `Create an image from a container's changes.
|
||||
Optionally tag the image created, set the author with the --author flag,
|
||||
set the commit message with the --message flag,
|
||||
and make changes to the instructions with the --change flag.`
|
||||
commitCommand = cli.Command{
|
||||
Name: "commit",
|
||||
Usage: "Create new image based on the changed container",
|
||||
Description: commitDescription,
|
||||
Flags: commitFlags,
|
||||
Action: commitCmd,
|
||||
ArgsUsage: "CONTAINER [REPOSITORY[:TAG]]",
|
||||
}
|
||||
)
|
||||
|
||||
func commitCmd(c *cli.Context) error {
|
||||
if err := validateFlags(c, commitFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runtime, err := getRuntime(c)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
var opts libpod.CopyOptions
|
||||
var container string
|
||||
args := c.Args()
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return errors.Errorf("need to give container name or id")
|
||||
case 1:
|
||||
container = args[0]
|
||||
case 2:
|
||||
container = args[0]
|
||||
opts.Reference = args[1]
|
||||
default:
|
||||
return errors.Errorf("too many arguments. Usage CONTAINER [REFERENCE]")
|
||||
}
|
||||
|
||||
changes := v1.ImageConfig{}
|
||||
if c.IsSet("change") {
|
||||
changes, err = getImageConfig(c.StringSlice("change"))
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error adding config changes to image %q", container)
|
||||
}
|
||||
}
|
||||
|
||||
history := []v1.History{
|
||||
{Comment: c.String("message")},
|
||||
}
|
||||
|
||||
config := v1.Image{
|
||||
Config: changes,
|
||||
History: history,
|
||||
Author: c.String("author"),
|
||||
}
|
||||
opts.ImageConfig = config
|
||||
|
||||
ctr, err := runtime.LookupContainer(container)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error looking up container %q", container)
|
||||
}
|
||||
return ctr.Commit(c.BoolT("pause"), opts)
|
||||
}
|
@ -28,7 +28,7 @@ var (
|
||||
}
|
||||
importDescription = `Create a container image from the contents of the specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).
|
||||
Note remote tar balls can be specified, via web address.
|
||||
Optionally tag the image. You can specify the Dockerfile instructions using the --change option.
|
||||
Optionally tag the image. You can specify the instructions using the --change option.
|
||||
`
|
||||
importCommand = cli.Command{
|
||||
Name: "import",
|
||||
|
@ -73,8 +73,8 @@ func loadCmd(c *cli.Context) error {
|
||||
if err != nil {
|
||||
return errors.Errorf("error creating file %v", err)
|
||||
}
|
||||
defer outFile.Close()
|
||||
defer os.Remove(outFile.Name())
|
||||
defer outFile.Close()
|
||||
|
||||
inFile, err := os.OpenFile(input, 0, 0666)
|
||||
if err != nil {
|
||||
|
@ -35,6 +35,7 @@ func main() {
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
attachCommand,
|
||||
commitCommand,
|
||||
createCommand,
|
||||
diffCommand,
|
||||
execCommand,
|
||||
|
@ -657,6 +657,33 @@ _podman_attach() {
|
||||
_complete_ "$options_with_args" "$boolean_options"
|
||||
}
|
||||
|
||||
_podman_commit() {
|
||||
local options_with_args="
|
||||
--author
|
||||
-a
|
||||
--change
|
||||
-c
|
||||
--message
|
||||
-m
|
||||
"
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
--pause
|
||||
-p
|
||||
"
|
||||
_complete_ "$options_with_args" "$boolean_options"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__podman_list_images
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_podman_diff() {
|
||||
local options_with_args="
|
||||
--format
|
||||
@ -1454,6 +1481,7 @@ _podman_podman() {
|
||||
"
|
||||
commands="
|
||||
attach
|
||||
commit
|
||||
create
|
||||
diff
|
||||
exec
|
||||
|
96
docs/podman-commit.1.md
Normal file
96
docs/podman-commit.1.md
Normal file
@ -0,0 +1,96 @@
|
||||
% podman(1) podman-commit - Tool to create new image based on the changed container
|
||||
% Urvashi Mohnani
|
||||
# podman-commit "1" "December 2017" "podman"
|
||||
|
||||
## NAME
|
||||
podman-commit - Create new image based on the changed container
|
||||
|
||||
## SYNOPSIS
|
||||
**podman commit**
|
||||
**TARBALL**
|
||||
[**--author**|**-a**]
|
||||
[**--change**|**-c**]
|
||||
[**--message**|**-m**]
|
||||
[**--help**|**-h**]
|
||||
|
||||
## DESCRIPTION
|
||||
**podman commit** creates an image based on a changed container. The author of the
|
||||
image can be set using the **--author** flag. Various image instructions can be
|
||||
configured with the **--change** flag and a commit message can be set using the
|
||||
**--message** flag. The container and its processes are paused while the image is
|
||||
committed. This minimizes the likelihood of data corruption when creating the new
|
||||
image. If this is not desired, the **--pause** flag can be set to false.
|
||||
|
||||
**podman [GLOBAL OPTIONS]**
|
||||
|
||||
**podman commit [GLOBAL OPTIONS]**
|
||||
|
||||
**podman commit [OPTIONS] CONTAINER**
|
||||
|
||||
## OPTIONS
|
||||
|
||||
**--author, -a**
|
||||
Set the author for the committed image
|
||||
|
||||
**--change, -c**
|
||||
Apply the following possible instructions to the created image:
|
||||
**CMD** | **ENTRYPOINT** | **ENV** | **EXPOSE** | **LABEL** | **STOPSIGNAL** | **USER** | **VOLUME** | **WORKDIR**
|
||||
Can be set multiple times
|
||||
|
||||
**--message, -m**
|
||||
Set commit message for committed image
|
||||
|
||||
**--pause, -p**
|
||||
Pause the container when creating an image
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
```
|
||||
# podman commit --change CMD=/bin/bash --change ENTRYPOINT=/bin/sh --change LABEL=blue=image reverent_golick image-commited
|
||||
Getting image source signatures
|
||||
Copying blob sha256:b41deda5a2feb1f03a5c1bb38c598cbc12c9ccd675f438edc6acd815f7585b86
|
||||
25.80 MB / 25.80 MB [======================================================] 0s
|
||||
Copying config sha256:c16a6d30f3782288ec4e7521c754acc29d37155629cb39149756f486dae2d4cd
|
||||
448 B / 448 B [============================================================] 0s
|
||||
Writing manifest to image destination
|
||||
Storing signatures
|
||||
```
|
||||
|
||||
```
|
||||
# podman commit --message "commiting container to image" reverent_golick image-commited
|
||||
Getting image source signatures
|
||||
Copying blob sha256:b41deda5a2feb1f03a5c1bb38c598cbc12c9ccd675f438edc6acd815f7585b86
|
||||
25.80 MB / 25.80 MB [======================================================] 0s
|
||||
Copying config sha256:af376cdda5c0ac1d9592bf56567253d203f8de6a8edf356c683a645d75221540
|
||||
376 B / 376 B [============================================================] 0s
|
||||
Writing manifest to image destination
|
||||
Storing signatures
|
||||
```
|
||||
|
||||
```
|
||||
# podman commit --author "firstName lastName" reverent_golick
|
||||
Getting image source signatures
|
||||
Copying blob sha256:b41deda5a2feb1f03a5c1bb38c598cbc12c9ccd675f438edc6acd815f7585b86
|
||||
25.80 MB / 25.80 MB [======================================================] 0s
|
||||
Copying config sha256:d61387b4d5edf65edee5353e2340783703074ffeaaac529cde97a8357eea7645
|
||||
378 B / 378 B [============================================================] 0s
|
||||
Writing manifest to image destination
|
||||
Storing signatures
|
||||
```
|
||||
|
||||
```
|
||||
# podman commit --pause=false reverent_golick image-commited
|
||||
Getting image source signatures
|
||||
Copying blob sha256:b41deda5a2feb1f03a5c1bb38c598cbc12c9ccd675f438edc6acd815f7585b86
|
||||
25.80 MB / 25.80 MB [======================================================] 0s
|
||||
Copying config sha256:5813fe8a3b18696089fd09957a12e88bda43dc1745b5240879ffffe93240d29a
|
||||
419 B / 419 B [============================================================] 0s
|
||||
Writing manifest to image destination
|
||||
Storing signatures
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-run(1), podman-create(1)
|
||||
|
||||
## HISTORY
|
||||
December 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
|
@ -890,6 +890,10 @@ func (c *Container) Export(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.export(path)
|
||||
}
|
||||
|
||||
func (c *Container) export(path string) error {
|
||||
mountPoint := c.state.Mountpoint
|
||||
if !c.state.Mounted {
|
||||
mount, err := c.runtime.store.Mount(c.ID(), c.config.MountLabel)
|
||||
@ -965,8 +969,36 @@ func (c *Container) Inspect(size bool) (*ContainerInspectData, error) {
|
||||
|
||||
// Commit commits the changes between a container and its image, creating a new
|
||||
// image
|
||||
func (c *Container) Commit() (*storage.Image, error) {
|
||||
return nil, ErrNotImplemented
|
||||
func (c *Container) Commit(pause bool, options CopyOptions) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if err := c.syncContainer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.state.State == ContainerStateRunning && pause {
|
||||
if err := c.runtime.ociRuntime.pauseContainer(c); err != nil {
|
||||
return errors.Wrapf(err, "error pausing container %q", c.ID())
|
||||
}
|
||||
defer func() {
|
||||
if err := c.runtime.ociRuntime.unpauseContainer(c); err != nil {
|
||||
logrus.Errorf("error unpausing container %q: %v", c.ID(), err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
tempFile, err := ioutil.TempFile(c.runtime.config.TmpDir, "podman-commit")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating temp file")
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
defer tempFile.Close()
|
||||
|
||||
if err := c.export(tempFile.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.runtime.ImportImage(tempFile.Name(), options)
|
||||
}
|
||||
|
||||
// Wait blocks on a container to exit and returns its exit code
|
||||
|
@ -47,7 +47,7 @@ func getImageData(img storage.Image, imgRef types.Image, size int64, driver *dri
|
||||
RepoDigests: repoDigests,
|
||||
Comment: ociv1Img.History[0].Comment,
|
||||
Created: ociv1Img.Created,
|
||||
Author: ociv1Img.History[0].Author,
|
||||
Author: ociv1Img.Author,
|
||||
Architecture: ociv1Img.Architecture,
|
||||
Os: ociv1Img.OS,
|
||||
Config: &ociv1Img.Config,
|
||||
|
122
test/podman_commit.bats
Normal file
122
test/podman_commit.bats
Normal file
@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
IMAGE="redis:alpine"
|
||||
|
||||
function teardown() {
|
||||
cleanup_test
|
||||
}
|
||||
|
||||
function setup() {
|
||||
prepare_network_conf
|
||||
copy_images
|
||||
}
|
||||
|
||||
@test "podman commit default" {
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d --name my_ctr ${FEDORA_MINIMAL} sleep 6000"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} commit my_ctr image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} images | grep image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} rmi image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} stop my_ctr"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "podman commit with message flag" {
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d --name my_ctr ${FEDORA_MINIMAL} sleep 6000"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} commit --message testing-commit my_ctr image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} inspect image-committed | grep testing-commit"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} rmi image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} stop my_ctr"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "podman commit with author flag" {
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d --name my_ctr ${FEDORA_MINIMAL} sleep 6000"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} commit --author author-name my_ctr image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} inspect image-committed | grep author-name"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} rmi image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} stop my_ctr"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "podman commit with change flag" {
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d --name my_ctr ${FEDORA_MINIMAL} sleep 6000"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} commit --change LABEL=image=blue my_ctr image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} inspect image-committed | grep blue"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} rmi image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} stop my_ctr"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "podman commit with pause flag" {
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} run -d --name my_ctr ${FEDORA_MINIMAL} sleep 6000"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} commit --pause=false my_ctr image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} images | grep image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} rmi image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} stop my_ctr"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "podman commit non-running container" {
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} create --name my_ctr ${FEDORA_MINIMAL} ls"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} commit my_ctr image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} images | grep image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} rmi image-committed"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
run bash -c "${PODMAN_BINARY} ${PODMAN_OPTIONS} rm my_ctr"
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
Reference in New Issue
Block a user