From 3ff1583814b4e69d2d85ee2b1d342d0d5974b9de Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Fri, 27 Mar 2020 11:06:16 -0500 Subject: podmanv2 commit add commit of a container to a container-image Signed-off-by: Brent Baude --- cmd/podmanV2/containers/commit.go | 79 +++++++++++++++++++++++++++++++++ pkg/domain/entities/containers.go | 17 +++++++ pkg/domain/entities/engine_container.go | 1 + pkg/domain/infra/abi/containers.go | 48 ++++++++++++++++++++ pkg/domain/infra/tunnel/containers.go | 38 ++++++++++++++++ 5 files changed, 183 insertions(+) create mode 100644 cmd/podmanV2/containers/commit.go diff --git a/cmd/podmanV2/containers/commit.go b/cmd/podmanV2/containers/commit.go new file mode 100644 index 000000000..28eb42f33 --- /dev/null +++ b/cmd/podmanV2/containers/commit.go @@ -0,0 +1,79 @@ +package containers + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + 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 = &cobra.Command{ + Use: "commit [flags] CONTAINER [IMAGE]", + Short: "Create new image based on the changed container", + Long: commitDescription, + RunE: commit, + PreRunE: preRunE, + Args: cobra.MinimumNArgs(1), + Example: `podman commit -q --message "committing container to image" reverent_golick image-committed + podman commit -q --author "firstName lastName" reverent_golick image-committed + podman commit -q --pause=false containerID image-committed + podman commit containerID`, + } + + // ChangeCmds is the list of valid Changes commands to passed to the Commit call + ChangeCmds = []string{"CMD", "ENTRYPOINT", "ENV", "EXPOSE", "LABEL", "ONBUILD", "STOPSIGNAL", "USER", "VOLUME", "WORKDIR"} +) + +var ( + commitOptions = entities.CommitOptions{ + ImageName: "", + } + iidFile string +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: commitCommand, + }) + flags := commitCommand.Flags() + flags.StringArrayVarP(&commitOptions.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): "+strings.Join(ChangeCmds, " | ")) + flags.StringVarP(&commitOptions.Format, "format", "f", "oci", "`Format` of the image manifest and metadata") + flags.StringVarP(&iidFile, "iidfile", "", "", "`file` to write the image ID to") + flags.StringVarP(&commitOptions.Message, "message", "m", "", "Set commit message for imported image") + flags.StringVarP(&commitOptions.Author, "author", "a", "", "Set the author for the image committed") + flags.BoolVarP(&commitOptions.Pause, "pause", "p", false, "Pause container during commit") + flags.BoolVarP(&commitOptions.Quiet, "quiet", "q", false, "Suppress output") + flags.BoolVar(&commitOptions.IncludeVolumes, "include-volumes", false, "Include container volumes as image volumes") + +} +func commit(cmd *cobra.Command, args []string) error { + container := args[0] + if len(args) > 1 { + commitOptions.ImageName = args[1] + } + if !commitOptions.Quiet { + commitOptions.Writer = os.Stderr + } + + response, err := registry.ContainerEngine().ContainerCommit(context.Background(), container, commitOptions) + if err != nil { + return err + } + if len(iidFile) > 0 { + if err = ioutil.WriteFile(iidFile, []byte(response.Id), 0644); err != nil { + return errors.Wrapf(err, "failed to write image ID to file %q", iidFile) + } + } + fmt.Println(response.Id) + return nil +} diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index fbc0247ab..3389e4db5 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -1,6 +1,7 @@ package entities import ( + "io" "time" "github.com/containers/libpod/libpod/define" @@ -106,3 +107,19 @@ type ContainerInspectOptions struct { type ContainerInspectReport struct { *define.InspectContainerData } + +type CommitOptions struct { + Author string + Changes []string + Format string + ImageName string + IncludeVolumes bool + Message string + Pause bool + Quiet bool + Writer io.Writer +} + +type CommitReport struct { + Id string +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index fceed1003..28be8b225 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -5,6 +5,7 @@ import ( ) type ContainerEngine interface { + ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error) ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) ContainerInspect(ctx context.Context, namesOrIds []string, options ContainerInspectOptions) ([]*ContainerInspectReport, error) ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 3965c5f75..d25af24c5 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -7,8 +7,11 @@ import ( "io/ioutil" "strings" + "github.com/containers/buildah" + "github.com/containers/image/v5/manifest" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter/shortcuts" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/signal" @@ -277,3 +280,48 @@ func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.To report.Value, err = container.Top(options.Descriptors) return report, err } + +func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, options entities.CommitOptions) (*entities.CommitReport, error) { + var ( + mimeType string + ) + ctr, err := ic.Libpod.LookupContainer(nameOrId) + if err != nil { + return nil, err + } + rtc, err := ic.Libpod.GetConfig() + if err != nil { + return nil, err + } + switch options.Format { + case "oci": + mimeType = buildah.OCIv1ImageManifest + if len(options.Message) > 0 { + return nil, errors.Errorf("messages are only compatible with the docker image format (-f docker)") + } + case "docker": + mimeType = manifest.DockerV2Schema2MediaType + default: + return nil, errors.Errorf("unrecognized image format %q", options.Format) + } + sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) + coptions := buildah.CommitOptions{ + SignaturePolicyPath: rtc.Engine.SignaturePolicyPath, + ReportWriter: options.Writer, + SystemContext: sc, + PreferredManifestType: mimeType, + } + opts := libpod.ContainerCommitOptions{ + CommitOptions: coptions, + Pause: options.Pause, + IncludeVolumes: options.IncludeVolumes, + Message: options.Message, + Changes: options.Changes, + Author: options.Author, + } + newImage, err := ctr.Commit(ctx, options.ImageName, opts) + if err != nil { + return nil, err + } + return &entities.CommitReport{Id: newImage.ID()}, nil +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 3db38ea5c..3c8be90dc 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -3,6 +3,8 @@ package tunnel import ( "context" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/libpod/pkg/bindings/containers" "github.com/containers/libpod/pkg/domain/entities" "github.com/pkg/errors" @@ -172,3 +174,39 @@ func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.To } return &entities.StringSliceReport{Value: topOutput}, nil } + +func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, options entities.CommitOptions) (*entities.CommitReport, error) { + var ( + repo string + tag string = "latest" + ) + if len(options.ImageName) > 0 { + ref, err := reference.Parse(options.ImageName) + if err != nil { + return nil, err + } + if t, ok := ref.(reference.Tagged); ok { + tag = t.Tag() + } + if r, ok := ref.(reference.Named); ok { + repo = r.Name() + } + if len(repo) < 1 { + return nil, errors.Errorf("invalid image name %q", options.ImageName) + } + } + commitOpts := containers.CommitOptions{ + Author: &options.Author, + Changes: options.Changes, + Comment: &options.Message, + Format: &options.Format, + Pause: &options.Pause, + Repo: &repo, + Tag: &tag, + } + response, err := containers.Commit(ic.ClientCxt, nameOrId, commitOpts) + if err != nil { + return nil, err + } + return &entities.CommitReport{Id: response.ID}, nil +} -- cgit v1.2.3-54-g00ecf