mirror of
https://github.com/containers/podman.git
synced 2025-06-08 00:00:51 +08:00
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>
This commit is contained in:
cmd/podman
libpod
pkg/varlinkapi
test/e2e
@ -17,7 +17,6 @@ func getMainCommands() []*cobra.Command {
|
|||||||
generateCommand.Command,
|
generateCommand.Command,
|
||||||
_containerKubeCommand,
|
_containerKubeCommand,
|
||||||
_psCommand,
|
_psCommand,
|
||||||
_loadCommand,
|
|
||||||
_loginCommand,
|
_loginCommand,
|
||||||
_logoutCommand,
|
_logoutCommand,
|
||||||
_logsCommand,
|
_logsCommand,
|
||||||
|
@ -24,6 +24,7 @@ var imageSubCommands = []*cobra.Command{
|
|||||||
_imagesCommand,
|
_imagesCommand,
|
||||||
_importCommand,
|
_importCommand,
|
||||||
_inspectCommand,
|
_inspectCommand,
|
||||||
|
_loadCommand,
|
||||||
_pruneImagesCommand,
|
_pruneImagesCommand,
|
||||||
_pullCommand,
|
_pullCommand,
|
||||||
_pushCommand,
|
_pushCommand,
|
||||||
|
@ -6,12 +6,8 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"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/cliconfig"
|
||||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
"github.com/containers/libpod/libpod/adapter"
|
||||||
"github.com/containers/libpod/libpod/image"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -56,14 +52,16 @@ func loadCmd(c *cliconfig.LoadValues) error {
|
|||||||
return errors.New("too many arguments. Requires exactly 1")
|
return errors.New("too many arguments. Requires exactly 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
|
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "could not get runtime")
|
return errors.Wrapf(err, "could not get runtime")
|
||||||
}
|
}
|
||||||
defer runtime.Shutdown(false)
|
defer runtime.Shutdown(false)
|
||||||
|
|
||||||
input := c.Input
|
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" {
|
if input == "/dev/stdin" {
|
||||||
fi, err := os.Stdin.Stat()
|
fi, err := os.Stdin.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -96,46 +94,10 @@ func loadCmd(c *cliconfig.LoadValues) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var writer io.Writer
|
names, err := runtime.LoadImage(getContext(), imageName, c)
|
||||||
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)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// generate full src name with specified image:tag
|
return err
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fmt.Println("Loaded image(s): " + getImageNames(newImages))
|
fmt.Println("Loaded image(s): " + names)
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
@ -45,6 +45,7 @@ var mainCommands = []*cobra.Command{
|
|||||||
_infoCommand,
|
_infoCommand,
|
||||||
_inspectCommand,
|
_inspectCommand,
|
||||||
_killCommand,
|
_killCommand,
|
||||||
|
_loadCommand,
|
||||||
podCommand.Command,
|
podCommand.Command,
|
||||||
_pullCommand,
|
_pullCommand,
|
||||||
_pushCommand,
|
_pushCommand,
|
||||||
|
@ -1102,8 +1102,11 @@ method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string)
|
|||||||
|
|
||||||
method ImageSave(options: ImageSaveOptions) -> (reply: MoreResponse)
|
method ImageSave(options: ImageSaveOptions) -> (reply: MoreResponse)
|
||||||
|
|
||||||
|
|
||||||
method GetPodsByContext(all: bool, latest: bool, args: []string) -> (pods: []string)
|
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.
|
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
|
||||||
error ImageNotFound (id: string)
|
error ImageNotFound (id: string)
|
||||||
|
|
||||||
|
@ -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)
|
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)
|
||||||
|
}
|
||||||
|
@ -486,7 +486,7 @@ func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, opti
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buildinfo.ContextDir = strings.Replace(tempFile, ":", "", -1)
|
buildinfo.ContextDir = tempFile
|
||||||
|
|
||||||
reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo)
|
reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo)
|
||||||
if err != nil {
|
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
|
// GetAllVolumes retrieves all the volumes
|
||||||
@ -763,3 +763,36 @@ func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) e
|
|||||||
}
|
}
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
ociv1 "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/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,6 @@ package libpod
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -15,6 +14,11 @@ import (
|
|||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/pkg/errors"
|
"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
|
// Runtime API
|
||||||
@ -211,3 +215,41 @@ func downloadFromURL(source string) (string, error) {
|
|||||||
|
|
||||||
return outFile.Name(), nil
|
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
|
||||||
|
}
|
||||||
|
@ -776,9 +776,6 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS
|
|||||||
c <- err
|
c <- err
|
||||||
close(c)
|
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
|
var log []string
|
||||||
done := false
|
done := false
|
||||||
for {
|
for {
|
||||||
@ -835,3 +832,73 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS
|
|||||||
}
|
}
|
||||||
return call.ReplyPushImage(br)
|
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)
|
||||||
|
}
|
||||||
|
@ -199,11 +199,9 @@ var _ = Describe("Podman load", func() {
|
|||||||
|
|
||||||
It("podman load localhost registry from scratch and :latest", func() {
|
It("podman load localhost registry from scratch and :latest", func() {
|
||||||
outfile := filepath.Join(podmanTest.TempDir, "load_test.tar.gz")
|
outfile := filepath.Join(podmanTest.TempDir, "load_test.tar.gz")
|
||||||
setup := podmanTest.Podman([]string{"pull", fedoraMinimal})
|
podmanTest.RestoreArtifact("fedora-minimal:latest")
|
||||||
setup.WaitWithDefaultTimeout()
|
|
||||||
Expect(setup.ExitCode()).To(Equal(0))
|
|
||||||
|
|
||||||
setup = podmanTest.Podman([]string{"tag", "fedora-minimal", "hello"})
|
setup := podmanTest.Podman([]string{"tag", "fedora-minimal", "hello"})
|
||||||
setup.WaitWithDefaultTimeout()
|
setup.WaitWithDefaultTimeout()
|
||||||
Expect(setup.ExitCode()).To(Equal(0))
|
Expect(setup.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user