mirror of https://github.com/containers/podman.git
				
				
				
			Merge pull request #2385 from baude/remoteload
podman-remote load image
This commit is contained in:
		
						commit
						b4c10790d5
					
				|  | @ -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)) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue