mirror of
https://github.com/containers/podman.git
synced 2025-05-29 14:06:29 +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:
@ -17,7 +17,6 @@ func getMainCommands() []*cobra.Command {
|
||||
generateCommand.Command,
|
||||
_containerKubeCommand,
|
||||
_psCommand,
|
||||
_loadCommand,
|
||||
_loginCommand,
|
||||
_logoutCommand,
|
||||
_logsCommand,
|
||||
|
@ -24,6 +24,7 @@ var imageSubCommands = []*cobra.Command{
|
||||
_imagesCommand,
|
||||
_importCommand,
|
||||
_inspectCommand,
|
||||
_loadCommand,
|
||||
_pruneImagesCommand,
|
||||
_pullCommand,
|
||||
_pushCommand,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ var mainCommands = []*cobra.Command{
|
||||
_infoCommand,
|
||||
_inspectCommand,
|
||||
_killCommand,
|
||||
_loadCommand,
|
||||
podCommand.Command,
|
||||
_pullCommand,
|
||||
_pushCommand,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
|
||||
|
Reference in New Issue
Block a user