diff options
author | baude <bbaude@redhat.com> | 2019-02-19 13:36:42 -0600 |
---|---|---|
committer | baude <bbaude@redhat.com> | 2019-02-21 10:11:19 -0600 |
commit | 71db80ddb15addb4197693bc056c35dd8ff0d6ac (patch) | |
tree | 9b3614c1c806dca4bf4745448d9e57c82d6fe2d3 | |
parent | af922fb2c6baceb89cc8e4acd6d84a6474b32dda (diff) | |
download | podman-71db80ddb15addb4197693bc056c35dd8ff0d6ac.tar.gz podman-71db80ddb15addb4197693bc056c35dd8ff0d6ac.tar.bz2 podman-71db80ddb15addb4197693bc056c35dd8ff0d6ac.zip |
podman-remote load image
enable the ability to load an image into remote storage
using the remote client.
Signed-off-by: baude <bbaude@redhat.com>
-rw-r--r-- | cmd/podman/commands.go | 1 | ||||
-rw-r--r-- | cmd/podman/image.go | 1 | ||||
-rw-r--r-- | cmd/podman/load.go | 54 | ||||
-rw-r--r-- | cmd/podman/main.go | 1 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 3 | ||||
-rw-r--r-- | libpod/adapter/runtime.go | 11 | ||||
-rw-r--r-- | libpod/adapter/runtime_remote.go | 37 | ||||
-rw-r--r-- | libpod/image/image.go | 2 | ||||
-rw-r--r-- | libpod/runtime_img.go | 44 | ||||
-rw-r--r-- | pkg/varlinkapi/images.go | 73 | ||||
-rw-r--r-- | test/e2e/load_test.go | 6 |
11 files changed, 175 insertions, 58 deletions
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 466ffa22f..0dc241373 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -17,7 +17,6 @@ func getMainCommands() []*cobra.Command { generateCommand.Command, _containerKubeCommand, _psCommand, - _loadCommand, _loginCommand, _logoutCommand, _logsCommand, diff --git a/cmd/podman/image.go b/cmd/podman/image.go index 3c8942ef5..14053cb0d 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -24,6 +24,7 @@ var imageSubCommands = []*cobra.Command{ _imagesCommand, _importCommand, _inspectCommand, + _loadCommand, _pruneImagesCommand, _pullCommand, _pushCommand, diff --git a/cmd/podman/load.go b/cmd/podman/load.go index 34a51cd0d..514c9f1e9 100644 --- a/cmd/podman/load.go +++ b/cmd/podman/load.go @@ -6,12 +6,8 @@ import ( "io/ioutil" "os" - "github.com/containers/image/directory" - dockerarchive "github.com/containers/image/docker/archive" - ociarchive "github.com/containers/image/oci/archive" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -56,14 +52,16 @@ func loadCmd(c *cliconfig.LoadValues) error { return errors.New("too many arguments. Requires exactly 1") } - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) input := c.Input - + if runtime.Remote && len(input) == 0 { + return errors.New("the remote client requires you to load via -i and a tarball") + } if input == "/dev/stdin" { fi, err := os.Stdin.Stat() if err != nil { @@ -96,46 +94,10 @@ func loadCmd(c *cliconfig.LoadValues) error { return err } - var writer io.Writer - if !c.Quiet { - writer = os.Stderr - } - - ctx := getContext() - - var newImages []*image.Image - src, err := dockerarchive.ParseReference(input) // FIXME? We should add dockerarchive.NewReference() - if err == nil { - newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer) - } + names, err := runtime.LoadImage(getContext(), imageName, c) if err != nil { - // generate full src name with specified image:tag - src, err := ociarchive.NewReference(input, imageName) // imageName may be "" - if err == nil { - newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer) - } - if err != nil { - src, err := directory.NewReference(input) - if err == nil { - newImages, err = runtime.ImageRuntime().LoadFromArchiveReference(ctx, src, c.SignaturePolicy, writer) - } - if err != nil { - return errors.Wrapf(err, "error pulling %q", input) - } - } + return err } - fmt.Println("Loaded image(s): " + getImageNames(newImages)) + fmt.Println("Loaded image(s): " + names) return nil } - -func getImageNames(images []*image.Image) string { - var names string - for i := range images { - if i == 0 { - names = images[i].InputName - } else { - names += ", " + images[i].InputName - } - } - return names -} diff --git a/cmd/podman/main.go b/cmd/podman/main.go index b60f3e1e1..990e55a8c 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -45,6 +45,7 @@ var mainCommands = []*cobra.Command{ _infoCommand, _inspectCommand, _killCommand, + _loadCommand, podCommand.Command, _pullCommand, _pushCommand, diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 53c21cb21..09d178760 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -1102,8 +1102,11 @@ method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string) method ImageSave(options: ImageSaveOptions) -> (reply: MoreResponse) + method GetPodsByContext(all: bool, latest: bool, args: []string) -> (pods: []string) +method LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) -> (reply: MoreResponse) + # ImageNotFound means the image could not be found by the provided name or ID in local storage. error ImageNotFound (id: string) diff --git a/libpod/adapter/runtime.go b/libpod/adapter/runtime.go index b12f63cdc..4f5b98dbb 100644 --- a/libpod/adapter/runtime.go +++ b/libpod/adapter/runtime.go @@ -322,3 +322,14 @@ func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) e } return newImage.Save(ctx, source, c.Format, c.Output, additionalTags, c.Quiet, c.Compress) } + +// LoadImage is a wrapper function for libpod PruneVolumes +func (r *LocalRuntime) LoadImage(ctx context.Context, name string, cli *cliconfig.LoadValues) (string, error) { + var ( + writer io.Writer + ) + if !cli.Quiet { + writer = os.Stderr + } + return r.Runtime.LoadImage(ctx, name, cli.Input, writer, cli.SignaturePolicy) +} diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go index a79f93079..ca2fad852 100644 --- a/libpod/adapter/runtime_remote.go +++ b/libpod/adapter/runtime_remote.go @@ -486,7 +486,7 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti if err != nil { return err } - buildinfo.ContextDir = strings.Replace(tempFile, ":", "", -1) + buildinfo.ContextDir = tempFile reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo) if err != nil { @@ -549,7 +549,7 @@ func (r *LocalRuntime) SendFileOverVarlink(source string) (string, error) { } - return tempFile, nil + return strings.Replace(tempFile, ":", "", -1), nil } // GetAllVolumes retrieves all the volumes @@ -763,3 +763,36 @@ func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) e } return nil } + +// LoadImage loads a container image from a remote client's filesystem +func (r *LocalRuntime) LoadImage(ctx context.Context, name string, cli *cliconfig.LoadValues) (string, error) { + var names string + remoteTempFile, err := r.SendFileOverVarlink(cli.Input) + if err != nil { + return "", nil + } + more := varlink.More + if cli.Quiet { + more = 0 + } + reply, err := iopodman.LoadImage().Send(r.Conn, uint64(more), name, remoteTempFile, cli.Quiet, true) + if err != nil { + return "", err + } + + for { + responses, flags, err := reply() + if err != nil { + logrus.Error(err) + return "", err + } + for _, line := range responses.Logs { + fmt.Print(line) + } + names = responses.Id + if flags&varlink.Continues == 0 { + break + } + } + return names, nil +} diff --git a/libpod/image/image.go b/libpod/image/image.go index 547fb8994..b20419d7b 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -32,7 +32,7 @@ import ( digest "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 1e9689362..451c2ebe7 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -3,7 +3,6 @@ package libpod import ( "context" "fmt" - "github.com/opencontainers/image-spec/specs-go/v1" "io" "io/ioutil" "net/http" @@ -15,6 +14,11 @@ import ( "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/pkg/errors" + + "github.com/containers/image/directory" + dockerarchive "github.com/containers/image/docker/archive" + ociarchive "github.com/containers/image/oci/archive" + "github.com/opencontainers/image-spec/specs-go/v1" ) // Runtime API @@ -211,3 +215,41 @@ func downloadFromURL(source string) (string, error) { return outFile.Name(), nil } + +// LoadImage loads a container image into local storage +func (r *Runtime) LoadImage(ctx context.Context, name, inputFile string, writer io.Writer, signaturePolicy string) (string, error) { + var newImages []*image.Image + src, err := dockerarchive.ParseReference(inputFile) // FIXME? We should add dockerarchive.NewReference() + if err == nil { + newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer) + } + if err != nil { + // generate full src name with specified image:tag + src, err := ociarchive.NewReference(inputFile, name) // imageName may be "" + if err == nil { + newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer) + } + if err != nil { + src, err := directory.NewReference(inputFile) + if err == nil { + newImages, err = r.ImageRuntime().LoadFromArchiveReference(ctx, src, signaturePolicy, writer) + } + if err != nil { + return "", errors.Wrapf(err, "error pulling %q", name) + } + } + } + return getImageNames(newImages), nil +} + +func getImageNames(images []*image.Image) string { + var names string + for i := range images { + if i == 0 { + names = images[i].InputName + } else { + names += ", " + images[i].InputName + } + } + return names +} diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 77df77a29..a27bdb9e5 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -776,9 +776,6 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS c <- err close(c) }() - - // TODO When pull output gets fixed for the remote client, we need to look into how we can turn below - // into something re-usable. it is in build too var log []string done := false for { @@ -835,3 +832,73 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS } return call.ReplyPushImage(br) } + +// LoadImage ... +func (i *LibpodAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string, deleteInputFile, quiet bool) error { + var ( + names string + writer io.Writer + err error + ) + if !quiet { + writer = os.Stderr + } + + if call.WantsMore() { + call.Continues = true + } + output := bytes.NewBuffer([]byte{}) + + c := make(chan error) + go func() { + names, err = i.Runtime.LoadImage(getContext(), name, inputFile, writer, "") + c <- err + 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.Error(err) + return call.ReplyErrorOccurred(err.Error()) + } + done = true + default: + if !call.WantsMore() { + time.Sleep(1 * time.Second) + break + } + br := iopodman.MoreResponse{ + Logs: log, + } + call.ReplyLoadImage(br) + log = []string{} + } + } else { + return call.ReplyErrorOccurred(err.Error()) + } + if done { + break + } + } + call.Continues = false + + br := iopodman.MoreResponse{ + Logs: log, + Id: names, + } + if deleteInputFile { + if err := os.Remove(inputFile); err != nil { + logrus.Errorf("unable to delete input file %s", inputFile) + } + } + return call.ReplyLoadImage(br) +} diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go index 423f99ac8..c85810454 100644 --- a/test/e2e/load_test.go +++ b/test/e2e/load_test.go @@ -199,11 +199,9 @@ var _ = Describe("Podman load", func() { It("podman load localhost registry from scratch and :latest", func() { outfile := filepath.Join(podmanTest.TempDir, "load_test.tar.gz") - setup := podmanTest.Podman([]string{"pull", fedoraMinimal}) - setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + podmanTest.RestoreArtifact("fedora-minimal:latest") - setup = podmanTest.Podman([]string{"tag", "fedora-minimal", "hello"}) + setup := podmanTest.Podman([]string{"tag", "fedora-minimal", "hello"}) setup.WaitWithDefaultTimeout() Expect(setup.ExitCode()).To(Equal(0)) |