aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/commit.go93
-rw-r--r--cmd/podman/import.go2
-rw-r--r--cmd/podman/load.go2
-rw-r--r--cmd/podman/main.go1
-rw-r--r--completions/bash/podman28
-rw-r--r--docs/podman-commit.1.md96
-rw-r--r--libpod/container.go36
-rw-r--r--libpod/image_inspect.go2
-rw-r--r--test/podman_commit.bats122
9 files changed, 377 insertions, 5 deletions
diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go
new file mode 100644
index 000000000..5d37458ea
--- /dev/null
+++ b/cmd/podman/commit.go
@@ -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)
+}
diff --git a/cmd/podman/import.go b/cmd/podman/import.go
index 2e8702c3d..7b380b500 100644
--- a/cmd/podman/import.go
+++ b/cmd/podman/import.go
@@ -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",
diff --git a/cmd/podman/load.go b/cmd/podman/load.go
index 2f3d9c56d..ee8a79756 100644
--- a/cmd/podman/load.go
+++ b/cmd/podman/load.go
@@ -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 {
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index cc6d26992..d3cb86798 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -35,6 +35,7 @@ func main() {
app.Commands = []cli.Command{
attachCommand,
+ commitCommand,
createCommand,
diffCommand,
execCommand,
diff --git a/completions/bash/podman b/completions/bash/podman
index ecf752de9..fd7fa99e2 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -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
diff --git a/docs/podman-commit.1.md b/docs/podman-commit.1.md
new file mode 100644
index 000000000..10680fcb8
--- /dev/null
+++ b/docs/podman-commit.1.md
@@ -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>
diff --git a/libpod/container.go b/libpod/container.go
index 15ad1f49a..dc22c9c61 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -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
diff --git a/libpod/image_inspect.go b/libpod/image_inspect.go
index a08665434..3d904e64b 100644
--- a/libpod/image_inspect.go
+++ b/libpod/image_inspect.go
@@ -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,
diff --git a/test/podman_commit.bats b/test/podman_commit.bats
new file mode 100644
index 000000000..46eaf32a4
--- /dev/null
+++ b/test/podman_commit.bats
@@ -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 ]
+}