mirror of
https://github.com/containers/podman.git
synced 2025-05-31 15:42:48 +08:00
podman remote-client commit
add the ability to commit a container to an image using the remote client. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
6
API.md
6
API.md
@ -11,7 +11,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[func BuildImageHierarchyMap(name: string) string](#BuildImageHierarchyMap)
|
||||
|
||||
[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) string](#Commit)
|
||||
[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) MoreResponse](#Commit)
|
||||
|
||||
[func ContainerArtifacts(name: string, artifactName: string) string](#ContainerArtifacts)
|
||||
|
||||
@ -308,14 +308,14 @@ BuildImageHierarchyMap is for the development of Podman and should not be used.
|
||||
### <a name="Commit"></a>func Commit
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method Commit(name: [string](https://godoc.org/builtin#string), image_name: [string](https://godoc.org/builtin#string), changes: [[]string](#[]string), author: [string](https://godoc.org/builtin#string), message: [string](https://godoc.org/builtin#string), pause: [bool](https://godoc.org/builtin#bool), manifestType: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
|
||||
method Commit(name: [string](https://godoc.org/builtin#string), image_name: [string](https://godoc.org/builtin#string), changes: [[]string](#[]string), author: [string](https://godoc.org/builtin#string), message: [string](https://godoc.org/builtin#string), pause: [bool](https://godoc.org/builtin#bool), manifestType: [string](https://godoc.org/builtin#string)) [MoreResponse](#MoreResponse)</div>
|
||||
Commit, creates an image from an existing container. It requires the name or
|
||||
ID of the container as well as the resulting image name. Optionally, you can define an author and message
|
||||
to be added to the resulting image. You can also define changes to the resulting image for the following
|
||||
attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOLUME, and WORKDIR_. To pause the
|
||||
container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot
|
||||
be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise,
|
||||
the resulting image's ID will be returned as a string.
|
||||
the resulting image's ID will be returned as a string inside a MoreResponse.
|
||||
### <a name="ContainerArtifacts"></a>func ContainerArtifacts
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
|
@ -11,7 +11,6 @@ const remoteclient = false
|
||||
// Commands that the local client implements
|
||||
func getMainCommands() []*cobra.Command {
|
||||
rootCommands := []*cobra.Command{
|
||||
_commitCommand,
|
||||
_execCommand,
|
||||
_playCommand,
|
||||
_loginCommand,
|
||||
@ -41,7 +40,6 @@ func getContainerSubCommands() []*cobra.Command {
|
||||
|
||||
return []*cobra.Command{
|
||||
_cleanupCommand,
|
||||
_commitCommand,
|
||||
_execCommand,
|
||||
_mountCommand,
|
||||
_refreshCommand,
|
||||
|
@ -2,16 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/adapter"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@ -52,32 +47,17 @@ func init() {
|
||||
}
|
||||
|
||||
func commitCmd(c *cliconfig.CommitValues) error {
|
||||
runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand)
|
||||
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
var (
|
||||
writer io.Writer
|
||||
mimeType string
|
||||
)
|
||||
args := c.InputArgs
|
||||
if len(args) != 2 {
|
||||
return errors.Errorf("you must provide a container name or ID and a target image name")
|
||||
}
|
||||
|
||||
switch c.Format {
|
||||
case "oci":
|
||||
mimeType = buildah.OCIv1ImageManifest
|
||||
if c.Flag("message").Changed {
|
||||
return errors.Errorf("messages are only compatible with the docker image format (-f docker)")
|
||||
}
|
||||
case "docker":
|
||||
mimeType = manifest.DockerV2Schema2MediaType
|
||||
default:
|
||||
return errors.Errorf("unrecognized image format %q", c.Format)
|
||||
}
|
||||
container := args[0]
|
||||
reference := args[1]
|
||||
if c.Flag("change").Changed {
|
||||
@ -92,38 +72,10 @@ func commitCmd(c *cliconfig.CommitValues) error {
|
||||
}
|
||||
}
|
||||
|
||||
if !c.Quiet {
|
||||
writer = os.Stderr
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(container)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error looking up container %q", container)
|
||||
}
|
||||
|
||||
rtc, err := runtime.GetConfig()
|
||||
iid, err := runtime.Commit(getContext(), c, container, reference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
|
||||
coptions := buildah.CommitOptions{
|
||||
SignaturePolicyPath: rtc.SignaturePolicyPath,
|
||||
ReportWriter: writer,
|
||||
SystemContext: sc,
|
||||
PreferredManifestType: mimeType,
|
||||
}
|
||||
options := libpod.ContainerCommitOptions{
|
||||
CommitOptions: coptions,
|
||||
Pause: c.Pause,
|
||||
IncludeVolumes: c.IncludeVolumes,
|
||||
Message: c.Message,
|
||||
Changes: c.Change,
|
||||
Author: c.Author,
|
||||
}
|
||||
newImage, err := ctr.Commit(getContext(), reference, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(newImage.ID())
|
||||
fmt.Println(iid)
|
||||
return nil
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ var (
|
||||
containerCommands = []*cobra.Command{
|
||||
_attachCommand,
|
||||
_checkpointCommand,
|
||||
_commitCommand,
|
||||
_containerExistsCommand,
|
||||
_contInspectSubCommand,
|
||||
_cpCommand,
|
||||
|
@ -30,6 +30,7 @@ var (
|
||||
var mainCommands = []*cobra.Command{
|
||||
_attachCommand,
|
||||
_buildCommand,
|
||||
_commitCommand,
|
||||
_diffCommand,
|
||||
_createCommand,
|
||||
_eventsCommand,
|
||||
|
@ -802,8 +802,8 @@ method DeleteUnusedImages() -> (images: []string)
|
||||
# attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOLUME, and WORKDIR_. To pause the
|
||||
# container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot
|
||||
# be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise,
|
||||
# the resulting image's ID will be returned as a string.
|
||||
method Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) -> (image: string)
|
||||
# the resulting image's ID will be returned as a string inside a MoreResponse.
|
||||
method Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) -> (reply: MoreResponse)
|
||||
|
||||
# ImportImage imports an image from a source (like tarball) into local storage. The image can have additional
|
||||
# descriptions added to it using the message and changes options. See also [ExportImage](ExportImage).
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -15,9 +16,12 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containers/buildah"
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/adapter/shortcuts"
|
||||
"github.com/containers/libpod/pkg/systemdgen"
|
||||
"github.com/containers/psgo"
|
||||
@ -1030,3 +1034,55 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri
|
||||
func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace {
|
||||
return shared.GetNamespaces(container.Pid)
|
||||
}
|
||||
|
||||
// Commit creates a local image from a container
|
||||
func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, container, imageName string) (string, error) {
|
||||
var (
|
||||
writer io.Writer
|
||||
mimeType string
|
||||
)
|
||||
switch c.Format {
|
||||
case "oci":
|
||||
mimeType = buildah.OCIv1ImageManifest
|
||||
if c.Flag("message").Changed {
|
||||
return "", errors.Errorf("messages are only compatible with the docker image format (-f docker)")
|
||||
}
|
||||
case "docker":
|
||||
mimeType = manifest.DockerV2Schema2MediaType
|
||||
default:
|
||||
return "", errors.Errorf("unrecognized image format %q", c.Format)
|
||||
}
|
||||
if !c.Quiet {
|
||||
writer = os.Stderr
|
||||
}
|
||||
ctr, err := r.Runtime.LookupContainer(container)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error looking up container %q", container)
|
||||
}
|
||||
|
||||
rtc, err := r.Runtime.GetConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
|
||||
coptions := buildah.CommitOptions{
|
||||
SignaturePolicyPath: rtc.SignaturePolicyPath,
|
||||
ReportWriter: writer,
|
||||
SystemContext: sc,
|
||||
PreferredManifestType: mimeType,
|
||||
}
|
||||
options := libpod.ContainerCommitOptions{
|
||||
CommitOptions: coptions,
|
||||
Pause: c.Pause,
|
||||
IncludeVolumes: c.IncludeVolumes,
|
||||
Message: c.Message,
|
||||
Changes: c.Change,
|
||||
Author: c.Author,
|
||||
}
|
||||
newImage, err := ctr.Commit(ctx, imageName, options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return newImage.ID(), nil
|
||||
}
|
||||
|
@ -986,3 +986,26 @@ func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared
|
||||
}
|
||||
return &ns
|
||||
}
|
||||
|
||||
// Commit creates a local image from a container
|
||||
func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, container, imageName string) (string, error) {
|
||||
var iid string
|
||||
reply, err := iopodman.Commit().Send(r.Conn, varlink.More, container, imageName, c.Change, c.Author, c.Message, c.Pause, c.Format)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for {
|
||||
responses, flags, err := reply()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, line := range responses.Logs {
|
||||
fmt.Fprintln(os.Stderr, line)
|
||||
}
|
||||
iid = responses.Id
|
||||
if flags&varlink.Continues == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iid, nil
|
||||
}
|
||||
|
@ -371,7 +371,6 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr
|
||||
done = true
|
||||
default:
|
||||
if !call.WantsMore() {
|
||||
time.Sleep(1 * time.Second)
|
||||
break
|
||||
}
|
||||
br := iopodman.MoreResponse{
|
||||
@ -495,6 +494,9 @@ func (i *LibpodAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error {
|
||||
|
||||
// Commit ...
|
||||
func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, changes []string, author, message string, pause bool, manifestType string) error {
|
||||
var newImage *image.Image
|
||||
|
||||
output := bytes.NewBuffer([]byte{})
|
||||
ctr, err := i.Runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name, err.Error())
|
||||
@ -515,7 +517,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch
|
||||
}
|
||||
coptions := buildah.CommitOptions{
|
||||
SignaturePolicyPath: rtc.SignaturePolicyPath,
|
||||
ReportWriter: nil,
|
||||
ReportWriter: output,
|
||||
SystemContext: sc,
|
||||
PreferredManifestType: mimeType,
|
||||
}
|
||||
@ -527,11 +529,61 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch
|
||||
Author: author,
|
||||
}
|
||||
|
||||
newImage, err := ctr.Commit(getContext(), imageName, options)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
if call.WantsMore() {
|
||||
call.Continues = true
|
||||
}
|
||||
return call.ReplyCommit(newImage.ID())
|
||||
|
||||
c := make(chan error)
|
||||
|
||||
go func() {
|
||||
newImage, err = ctr.Commit(getContext(), imageName, options)
|
||||
if err != nil {
|
||||
c <- err
|
||||
}
|
||||
c <- nil
|
||||
close(c)
|
||||
}()
|
||||
|
||||
var log []string
|
||||
done := false
|
||||
for {
|
||||
line, err := output.ReadString('\n')
|
||||
if err == nil {
|
||||
log = append(log, line)
|
||||
continue
|
||||
} else if err == io.EOF {
|
||||
select {
|
||||
case err := <-c:
|
||||
if err != nil {
|
||||
logrus.Errorf("reading of output during commit failed for %s", name)
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
done = true
|
||||
default:
|
||||
if !call.WantsMore() {
|
||||
break
|
||||
}
|
||||
br := iopodman.MoreResponse{
|
||||
Logs: log,
|
||||
}
|
||||
call.ReplyCommit(br)
|
||||
log = []string{}
|
||||
}
|
||||
} else {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
}
|
||||
call.Continues = false
|
||||
|
||||
br := iopodman.MoreResponse{
|
||||
Logs: log,
|
||||
Id: newImage.ID(),
|
||||
}
|
||||
|
||||
return call.ReplyCommit(br)
|
||||
}
|
||||
|
||||
// ImportImage imports an image from a tarball to the image store
|
||||
@ -633,7 +685,6 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string) error {
|
||||
done = true
|
||||
default:
|
||||
if !call.WantsMore() {
|
||||
time.Sleep(1 * time.Second)
|
||||
break
|
||||
}
|
||||
br := iopodman.MoreResponse{
|
||||
@ -764,7 +815,6 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS
|
||||
done = true
|
||||
default:
|
||||
if !call.WantsMore() {
|
||||
time.Sleep(1 * time.Second)
|
||||
break
|
||||
}
|
||||
br := iopodman.MoreResponse{
|
||||
@ -844,7 +894,6 @@ func (i *LibpodAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string,
|
||||
done = true
|
||||
default:
|
||||
if !call.WantsMore() {
|
||||
time.Sleep(1 * time.Second)
|
||||
break
|
||||
}
|
||||
br := iopodman.MoreResponse{
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build !remoteclient
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
@ -174,6 +172,9 @@ var _ = Describe("Podman commit", func() {
|
||||
})
|
||||
|
||||
It("podman commit with volume mounts and --include-volumes", func() {
|
||||
// We need to figure out how volumes are going to work correctly with the remote
|
||||
// client. This does not currently work.
|
||||
SkipIfRemote()
|
||||
s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"})
|
||||
s.WaitWithDefaultTimeout()
|
||||
Expect(s.ExitCode()).To(Equal(0))
|
||||
|
Reference in New Issue
Block a user