diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/adapter/client.go | 11 | ||||
-rw-r--r-- | libpod/adapter/runtime.go | 60 | ||||
-rw-r--r-- | libpod/adapter/runtime_remote.go | 114 | ||||
-rw-r--r-- | libpod/container_internal.go | 4 | ||||
-rw-r--r-- | libpod/image/image.go | 10 | ||||
-rw-r--r-- | libpod/image/image_test.go | 8 | ||||
-rw-r--r-- | libpod/image/prune.go | 39 | ||||
-rw-r--r-- | libpod/image/pull.go | 44 | ||||
-rw-r--r-- | libpod/runtime.go | 22 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 43 | ||||
-rw-r--r-- | libpod/runtime_img.go | 79 | ||||
-rw-r--r-- | libpod/runtime_pod_infra_linux.go | 2 |
12 files changed, 407 insertions, 29 deletions
diff --git a/libpod/adapter/client.go b/libpod/adapter/client.go index b3bb9acae..6512a5952 100644 --- a/libpod/adapter/client.go +++ b/libpod/adapter/client.go @@ -34,3 +34,14 @@ func (r RemoteRuntime) Connect() (*varlink.Connection, error) { } return connection, nil } + +// RefreshConnection is used to replace the current r.Conn after things like +// using an upgraded varlink connection +func (r RemoteRuntime) RefreshConnection() error { + newConn, err := r.Connect() + if err != nil { + return err + } + r.Conn = newConn + return nil +} diff --git a/libpod/adapter/runtime.go b/libpod/adapter/runtime.go index 1f3599082..2c408dd2f 100644 --- a/libpod/adapter/runtime.go +++ b/libpod/adapter/runtime.go @@ -4,12 +4,17 @@ package adapter import ( "context" + "github.com/pkg/errors" "io" + "io/ioutil" + "os" + "strconv" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/rootless" "github.com/urfave/cli" ) @@ -78,8 +83,8 @@ func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef type } // New calls into local storage to look for an image in local storage or to pull it -func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, forcePull bool) (*ContainerImage, error) { - img, err := r.Runtime.ImageRuntime().New(ctx, name, signaturePolicyPath, authfile, writer, dockeroptions, signingoptions, forcePull) +func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, forcePull bool, label *string) (*ContainerImage, error) { + img, err := r.Runtime.ImageRuntime().New(ctx, name, signaturePolicyPath, authfile, writer, dockeroptions, signingoptions, forcePull, label) if err != nil { return nil, err } @@ -99,3 +104,54 @@ func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { } return &Container{ctr}, nil } + +// PruneImages is wrapper into PruneImages within the image pkg +func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { + return r.ImageRuntime().PruneImages(all) +} + +// Export is a wrapper to container export to a tarfile +func (r *LocalRuntime) Export(name string, path string) error { + ctr, err := r.Runtime.LookupContainer(name) + if err != nil { + return errors.Wrapf(err, "error looking up container %q", name) + } + if os.Geteuid() != 0 { + state, err := ctr.State() + if err != nil { + return errors.Wrapf(err, "cannot read container state %q", ctr.ID()) + } + if state == libpod.ContainerStateRunning || state == libpod.ContainerStatePaused { + data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) + if err != nil { + return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return errors.Wrapf(err, "cannot parse PID %q", data) + } + became, ret, err := rootless.JoinDirectUserAndMountNS(uint(conmonPid)) + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } else { + became, ret, err := rootless.BecomeRootInUserNS() + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } + } + + return ctr.Export(path) +} + +// Import is a wrapper to import a container image +func (r *LocalRuntime) Import(ctx context.Context, source, reference string, changes []string, history string, quiet bool) (string, error) { + return r.Runtime.Import(ctx, source, reference, changes, history, quiet) +} diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go index 7189348bc..c73e98c95 100644 --- a/libpod/adapter/runtime_remote.go +++ b/libpod/adapter/runtime_remote.go @@ -3,10 +3,13 @@ package adapter import ( + "bufio" "context" "encoding/json" "fmt" + "github.com/pkg/errors" "io" + "os" "strings" "time" @@ -156,7 +159,10 @@ func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef type } // New calls into local storage to look for an image in local storage or to pull it -func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, forcePull bool) (*ContainerImage, error) { +func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, forcePull bool, label *string) (*ContainerImage, error) { + if label != nil { + return nil, errors.New("the remote client function does not support checking a remote image for a label") + } // TODO Creds needs to be figured out here too, like above tlsBool := dockeroptions.DockerInsecureSkipTLSVerify // Remember SkipTlsVerify is the opposite of tlsverify @@ -320,3 +326,109 @@ func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig { return &data } + +// PruneImages is the wrapper call for a remote-client to prune images +func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { + return iopodman.ImagesPrune().Call(r.Conn, all) +} + +// Export is a wrapper to container export to a tarfile +func (r *LocalRuntime) Export(name string, path string) error { + tempPath, err := iopodman.ExportContainer().Call(r.Conn, name, "") + if err != nil { + return err + } + + outputFile, err := os.Create(path) + if err != nil { + return err + } + defer outputFile.Close() + + writer := bufio.NewWriter(outputFile) + defer writer.Flush() + + reply, err := iopodman.ReceiveFile().Send(r.Conn, varlink.Upgrade, tempPath, true) + if err != nil { + return err + } + + length, _, err := reply() + if err != nil { + return errors.Wrap(err, "unable to get file length for transfer") + } + + reader := r.Conn.Reader + if _, err := io.CopyN(writer, reader, length); err != nil { + return errors.Wrap(err, "file transer failed") + } + + return nil +} + +// Import implements the remote calls required to import a container image to the store +func (r *LocalRuntime) Import(ctx context.Context, source, reference string, changes []string, history string, quiet bool) (string, error) { + // First we send the file to the host + fs, err := os.Open(source) + if err != nil { + return "", err + } + + fileInfo, err := fs.Stat() + if err != nil { + return "", err + } + reply, err := iopodman.SendFile().Send(r.Conn, varlink.Upgrade, "", int64(fileInfo.Size())) + if err != nil { + return "", err + } + _, _, err = reply() + if err != nil { + return "", err + } + + reader := bufio.NewReader(fs) + _, err = reader.WriteTo(r.Conn.Writer) + if err != nil { + return "", err + } + r.Conn.Writer.Flush() + + // All was sent, wait for the ACK from the server + tempFile, err := r.Conn.Reader.ReadString(':') + if err != nil { + return "", err + } + + // r.Conn is kaput at this point due to the upgrade + if err := r.RemoteRuntime.RefreshConnection(); err != nil { + return "", err + + } + return iopodman.ImportImage().Call(r.Conn, strings.TrimRight(tempFile, ":"), reference, history, changes, true) +} + +// GetAllVolumes retrieves all the volumes +func (r *LocalRuntime) GetAllVolumes() ([]*libpod.Volume, error) { + return nil, libpod.ErrNotImplemented +} + +// RemoveVolume removes a volumes +func (r *LocalRuntime) RemoveVolume(ctx context.Context, v *libpod.Volume, force, prune bool) error { + return libpod.ErrNotImplemented +} + +// GetContainers retrieves all containers from the state +// Filters can be provided which will determine what containers are included in +// the output. Multiple filters are handled by ANDing their output, so only +// containers matching all filters are returned +func (r *LocalRuntime) GetContainers(filters ...libpod.ContainerFilter) ([]*libpod.Container, error) { + return nil, libpod.ErrNotImplemented +} + +// RemoveContainer removes the given container +// If force is specified, the container will be stopped first +// Otherwise, RemoveContainer will return an error if the container is running +func (r *LocalRuntime) RemoveContainer(ctx context.Context, c *libpod.Container, force bool) error { + return libpod.ErrNotImplemented +} diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 39c1501da..b0dcc853e 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -392,7 +392,9 @@ func resetState(state *ContainerState) error { state.PID = 0 state.Mountpoint = "" state.Mounted = false - state.State = ContainerStateConfigured + if state.State != ContainerStateExited { + state.State = ContainerStateConfigured + } state.ExecSessions = make(map[string]*ExecSession) state.NetworkStatus = nil state.BindMounts = make(map[string]string) diff --git a/libpod/image/image.go b/libpod/image/image.go index ea326d820..739372e77 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -125,7 +125,7 @@ func (ir *Runtime) NewFromLocal(name string) (*Image, error) { // New creates a new image object where the image could be local // or remote -func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, forcePull bool) (*Image, error) { +func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions, forcePull bool, label *string) (*Image, error) { // We don't know if the image is local or not ... check local first newImage := Image{ InputName: name, @@ -145,7 +145,7 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile if signaturePolicyPath == "" { signaturePolicyPath = ir.SignaturePolicyPath } - imageName, err := ir.pullImageFromHeuristicSource(ctx, name, writer, authfile, signaturePolicyPath, signingoptions, dockeroptions) + imageName, err := ir.pullImageFromHeuristicSource(ctx, name, writer, authfile, signaturePolicyPath, signingoptions, dockeroptions, label) if err != nil { return nil, errors.Wrapf(err, "unable to pull %s", name) } @@ -823,9 +823,9 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { return nil, err } - var repoDigests []string - for _, name := range i.Names() { - repoDigests = append(repoDigests, strings.SplitN(name, ":", 2)[0]+"@"+i.Digest().String()) + repoDigests, err := i.RepoDigests() + if err != nil { + return nil, err } driver, err := i.DriverData() diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index 077ae460e..075ba119d 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -87,9 +87,9 @@ func TestImage_NewFromLocal(t *testing.T) { // Need images to be present for this test ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) - bb, err := ir.New(context.Background(), "docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}, false) + bb, err := ir.New(context.Background(), "docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}, false, nil) assert.NoError(t, err) - bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, false) + bbglibc, err := ir.New(context.Background(), "docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{}, false, nil) assert.NoError(t, err) tm, err := makeLocalMatrix(bb, bbglibc) @@ -136,7 +136,7 @@ func TestImage_New(t *testing.T) { // Iterate over the names and delete the image // after the pull for _, img := range names { - newImage, err := ir.New(context.Background(), img, "", "", writer, nil, SigningOptions{}, false) + newImage, err := ir.New(context.Background(), img, "", "", writer, nil, SigningOptions{}, false, nil) assert.NoError(t, err) assert.NotEqual(t, newImage.ID(), "") err = newImage.Remove(false) @@ -164,7 +164,7 @@ func TestImage_MatchRepoTag(t *testing.T) { } ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) - newImage, err := ir.New(context.Background(), "busybox", "", "", os.Stdout, nil, SigningOptions{}, false) + newImage, err := ir.New(context.Background(), "busybox", "", "", os.Stdout, nil, SigningOptions{}, false, nil) assert.NoError(t, err) err = newImage.TagImage("foo:latest") assert.NoError(t, err) diff --git a/libpod/image/prune.go b/libpod/image/prune.go index 6a1f160d5..8602c222c 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -1,9 +1,11 @@ package image +import "github.com/pkg/errors" + // GetPruneImages returns a slice of images that have no names/unused -func (ir *Runtime) GetPruneImages() ([]*Image, error) { +func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) { var ( - unamedImages []*Image + pruneImages []*Image ) allImages, err := ir.GetImages() if err != nil { @@ -11,16 +13,35 @@ func (ir *Runtime) GetPruneImages() ([]*Image, error) { } for _, i := range allImages { if len(i.Names()) == 0 { - unamedImages = append(unamedImages, i) + pruneImages = append(pruneImages, i) continue } - containers, err := i.Containers() - if err != nil { - return nil, err + if all { + containers, err := i.Containers() + if err != nil { + return nil, err + } + if len(containers) < 1 { + pruneImages = append(pruneImages, i) + } } - if len(containers) < 1 { - unamedImages = append(unamedImages, i) + } + return pruneImages, nil +} + +// PruneImages prunes dangling and optionally all unused images from the local +// image store +func (ir *Runtime) PruneImages(all bool) ([]string, error) { + var prunedCids []string + pruneImages, err := ir.GetPruneImages(all) + if err != nil { + return nil, errors.Wrap(err, "unable to get images to prune") + } + for _, p := range pruneImages { + if err := p.Remove(true); err != nil { + return nil, errors.Wrap(err, "failed to prune image") } + prunedCids = append(prunedCids, p.ID()) } - return unamedImages, nil + return prunedCids, nil } diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 434b83520..6fef96e37 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "strings" cp "github.com/containers/image/copy" "github.com/containers/image/directory" @@ -192,7 +193,7 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. // pullImageFromHeuristicSource pulls an image based on inputName, which is heuristically parsed and may involve configured registries. // Use pullImageFromReference if the source is known precisely. -func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions) ([]string, error) { +func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, label *string) ([]string, error) { var goal *pullGoal sc := GetSystemContext(signaturePolicyPath, authfile, false) srcRef, err := alltransports.ParseImageName(inputName) @@ -208,7 +209,7 @@ func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName s return nil, errors.Wrapf(err, "error determining pull goal for image %q", inputName) } } - return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions) + return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, label) } // pullImageFromReference pulls an image from a types.imageReference. @@ -218,11 +219,11 @@ func (ir *Runtime) pullImageFromReference(ctx context.Context, srcRef types.Imag if err != nil { return nil, errors.Wrapf(err, "error determining pull goal for image %q", transports.ImageName(srcRef)) } - return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions) + return ir.doPullImage(ctx, sc, *goal, writer, signingOptions, dockerOptions, nil) } // doPullImage is an internal helper interpreting pullGoal. Almost everyone should call one of the callers of doPullImage instead. -func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goal pullGoal, writer io.Writer, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions) ([]string, error) { +func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goal pullGoal, writer io.Writer, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, label *string) ([]string, error) { policyContext, err := getPolicyContext(sc) if err != nil { return nil, err @@ -230,8 +231,12 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa defer policyContext.Destroy() systemRegistriesConfPath := registries.SystemRegistriesConfPath() - var images []string - var pullErrors *multierror.Error + + var ( + images []string + pullErrors *multierror.Error + ) + for _, imageInfo := range goal.refPairs { copyOptions := getCopyOptions(sc, writer, dockerOptions, nil, signingOptions, "", nil) copyOptions.SourceCtx.SystemRegistriesConfPath = systemRegistriesConfPath // FIXME: Set this more globally. Probably no reason not to have it in every types.SystemContext, and to compute the value just once in one place. @@ -239,6 +244,13 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa if writer != nil && (imageInfo.srcRef.Transport().Name() == DockerTransport || imageInfo.srcRef.Transport().Name() == AtomicTransport) { io.WriteString(writer, fmt.Sprintf("Trying to pull %s...", imageInfo.image)) } + // If the label is not nil, check if the label exists and if not, return err + if label != nil { + if err := checkRemoteImageForLabel(ctx, *label, imageInfo, sc); err != nil { + return nil, err + } + } + _, err = cp.Image(ctx, policyContext, imageInfo.dstRef, imageInfo.srcRef, copyOptions) if err != nil { pullErrors = multierror.Append(pullErrors, err) @@ -314,3 +326,23 @@ func (ir *Runtime) pullGoalFromPossiblyUnqualifiedName(inputName string) (*pullG searchedRegistries: searchRegistries, }, nil } + +// checkRemoteImageForLabel checks if the remote image has a specific label. if the label exists, we +// return nil, else we return an error +func checkRemoteImageForLabel(ctx context.Context, label string, imageInfo pullRefPair, sc *types.SystemContext) error { + labelImage, err := imageInfo.srcRef.NewImage(ctx, sc) + if err != nil { + return err + } + remoteInspect, err := labelImage.Inspect(ctx) + if err != nil { + return err + } + // Labels are case insensitive; so we iterate instead of simple lookup + for k := range remoteInspect.Labels { + if strings.ToLower(label) == strings.ToLower(k) { + return nil + } + } + return errors.Errorf("%s has no label %s", imageInfo.image, label) +} diff --git a/libpod/runtime.go b/libpod/runtime.go index c7000d84a..c975f628b 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -385,6 +385,28 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { if _, err := toml.Decode(string(contents), runtime.config); err != nil { return nil, errors.Wrapf(err, "error decoding configuration file %s", configPath) } + } else if rootless.IsRootless() { + // If the configuration file was not found but we are running in rootless, a subset of the + // global config file is used. + for _, path := range []string{OverrideConfigPath, ConfigPath} { + contents, err := ioutil.ReadFile(OverrideConfigPath) + if err != nil { + // Ignore any error, the file might not be readable by us. + continue + } + tmpConfig := new(RuntimeConfig) + if _, err := toml.Decode(string(contents), tmpConfig); err != nil { + return nil, errors.Wrapf(err, "error decoding configuration file %s", path) + } + + // Cherry pick the settings we want from the global configuration + runtime.config.ConmonPath = tmpConfig.ConmonPath + runtime.config.ConmonEnvVars = tmpConfig.ConmonEnvVars + runtime.config.OCIRuntimes = tmpConfig.OCIRuntimes + runtime.config.CNIPluginDir = tmpConfig.CNIPluginDir + runtime.config.NoPivotRoot = tmpConfig.NoPivotRoot + break + } } // Overwrite config with user-given configuration options diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 6d5ce5a7e..9afdef7b6 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -2,9 +2,11 @@ package libpod import ( "context" + "io/ioutil" "os" "path" "path/filepath" + "strconv" "strings" "time" @@ -521,3 +523,44 @@ func isNamedVolume(volName string) bool { } return false } + +// Export is the libpod portion of exporting a container to a tar file +func (r *Runtime) Export(name string, path string) error { + ctr, err := r.LookupContainer(name) + if err != nil { + return err + } + if os.Geteuid() != 0 { + state, err := ctr.State() + if err != nil { + return errors.Wrapf(err, "cannot read container state %q", ctr.ID()) + } + if state == ContainerStateRunning || state == ContainerStatePaused { + data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) + if err != nil { + return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return errors.Wrapf(err, "cannot parse PID %q", data) + } + became, ret, err := rootless.JoinDirectUserAndMountNS(uint(conmonPid)) + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } else { + became, ret, err := rootless.BecomeRootInUserNS() + if err != nil { + return err + } + if became { + os.Exit(ret) + } + } + } + return ctr.Export(path) + +} diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 66844bb31..c20aa77a3 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -3,9 +3,16 @@ package libpod import ( "context" "fmt" + "github.com/opencontainers/image-spec/specs-go/v1" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" "github.com/containers/buildah/imagebuildah" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/pkg/errors" ) @@ -132,3 +139,75 @@ func (r *Runtime) Build(ctx context.Context, options imagebuildah.BuildOptions, _, _, err := imagebuildah.BuildDockerfiles(ctx, r.store, options, dockerfiles...) return err } + +// Import is called as an intermediary to the image library Import +func (r *Runtime) Import(ctx context.Context, source string, reference string, changes []string, history string, quiet bool) (string, error) { + var ( + writer io.Writer + err error + ) + + ic := v1.ImageConfig{} + if len(changes) > 0 { + ic, err = util.GetImageConfig(changes) + if err != nil { + return "", errors.Wrapf(err, "error adding config changes to image %q", source) + } + } + + hist := []v1.History{ + {Comment: history}, + } + + config := v1.Image{ + Config: ic, + History: hist, + } + + writer = nil + if !quiet { + writer = os.Stderr + } + + // if source is a url, download it and save to a temp file + u, err := url.ParseRequestURI(source) + if err == nil && u.Scheme != "" { + file, err := downloadFromURL(source) + if err != nil { + return "", err + } + defer os.Remove(file) + source = file + } + + newImage, err := r.imageRuntime.Import(ctx, source, reference, writer, image.SigningOptions{}, config) + if err != nil { + return "", err + } + return newImage.ID(), nil +} + +// donwloadFromURL downloads an image in the format "https:/example.com/myimage.tar" +// and temporarily saves in it /var/tmp/importxyz, which is deleted after the image is imported +func downloadFromURL(source string) (string, error) { + fmt.Printf("Downloading from %q\n", source) + + outFile, err := ioutil.TempFile("/var/tmp", "import") + if err != nil { + return "", errors.Wrap(err, "error creating file") + } + defer outFile.Close() + + response, err := http.Get(source) + if err != nil { + return "", errors.Wrapf(err, "error downloading %q", source) + } + defer response.Body.Close() + + _, err = io.Copy(outFile, response.Body) + if err != nil { + return "", errors.Wrapf(err, "error saving %s to %s", source, outFile.Name()) + } + + return outFile.Name(), nil +} diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 5e1051150..4f221764a 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -67,7 +67,7 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, return nil, ErrRuntimeStopped } - newImage, err := r.ImageRuntime().New(ctx, r.config.InfraImage, "", "", nil, nil, image.SigningOptions{}, false) + newImage, err := r.ImageRuntime().New(ctx, r.config.InfraImage, "", "", nil, nil, image.SigningOptions{}, false, nil) if err != nil { return nil, err } |