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:
umohnani8
2017-12-15 16:14:52 -05:00
committed by Atomic Bot
parent 5da9fd4953
commit 26a6e0de46
9 changed files with 377 additions and 5 deletions

93
cmd/podman/commit.go Normal file
View 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)
}

View File

@ -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",

View File

@ -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 {

View File

@ -35,6 +35,7 @@ func main() {
app.Commands = []cli.Command{
attachCommand,
commitCommand,
createCommand,
diffCommand,
execCommand,

View File

@ -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
View 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>

View File

@ -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

View File

@ -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
View 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 ]
}