mirror of
https://github.com/containers/podman.git
synced 2025-05-31 15:42:48 +08:00
save and load should support multi-tag for docker-archive
The docker-archive tar files can have multiple tags for the same image stored in it. Load pulls all the tags found in the archive when loading a tar file. Save can oush multiple tags of the same image to a tar archive. Signed-off-by: umohnani8 <umohnani@redhat.com> Closes: #819 Approved by: rhatdan
This commit is contained in:
8
API.md
8
API.md
@ -19,7 +19,7 @@ in the [API.md](https://github.com/projectatomic/libpod/blob/master/API.md) file
|
||||
|
||||
[func ExportContainer(name: string, path: string) string](#ExportContainer)
|
||||
|
||||
[func ExportImage(name: string, destination: string, compress: bool) string](#ExportImage)
|
||||
[func ExportImage(name: string, destination: string, compress: bool, tags: []string) string](#ExportImage)
|
||||
|
||||
[func GetAttachSockets(name: string) Sockets](#GetAttachSockets)
|
||||
|
||||
@ -190,9 +190,11 @@ The return value is the written tarfile.
|
||||
### <a name="ExportImage"></a>func ExportImage
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ExportImage(name: [string](https://godoc.org/builtin#string), destination: [string](https://godoc.org/builtin#string), compress: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
|
||||
method ExportImage(name: [string](https://godoc.org/builtin#string), destination: [string](https://godoc.org/builtin#string), compress: [bool](https://godoc.org/builtin#bool), tags: [[]string](#[]string)) [string](https://godoc.org/builtin#string)</div>
|
||||
ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also
|
||||
a booleon option to force compression. Upon completion, the ID of the image is returned. If the image cannot
|
||||
a booleon option to force compression. It also takes in a string array of tags to be able to save multiple
|
||||
tags of the same image to a tarball (each tag should be of the form <image>:<tag>). Upon completion, the ID
|
||||
of the image is returned. If the image cannot
|
||||
be found in local storage, an [ImageNotFound](#ImageNotFound) error will be returned. See also [ImportImage](ImportImage).
|
||||
### <a name="GetAttachSockets"></a>func GetAttachSockets
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
libpodImage "github.com/projectatomic/libpod/libpod/image"
|
||||
"github.com/projectatomic/libpod/libpod/image"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@ -105,22 +105,34 @@ func loadCmd(c *cli.Context) error {
|
||||
ctx := getContext()
|
||||
|
||||
src := libpod.DockerArchive + ":" + input
|
||||
newImage, err := runtime.ImageRuntime().New(ctx, src, c.String("signature-policy"), "", writer, &libpodImage.DockerRegistryOptions{}, libpodImage.SigningOptions{}, false, false)
|
||||
newImages, err := runtime.ImageRuntime().LoadFromArchive(ctx, src, c.String("signature-policy"), writer)
|
||||
if err != nil {
|
||||
// generate full src name with specified image:tag
|
||||
fullSrc := libpod.OCIArchive + ":" + input
|
||||
if image != "" {
|
||||
fullSrc = fullSrc + ":" + image
|
||||
}
|
||||
newImage, err = runtime.ImageRuntime().New(ctx, fullSrc, c.String("signature-policy"), "", writer, &libpodImage.DockerRegistryOptions{}, libpodImage.SigningOptions{}, false, false)
|
||||
newImages, err = runtime.ImageRuntime().LoadFromArchive(ctx, fullSrc, c.String("signature-policy"), writer)
|
||||
if err != nil {
|
||||
src = libpod.DirTransport + ":" + input
|
||||
newImage, err = runtime.ImageRuntime().New(ctx, src, c.String("signature-policy"), "", writer, &libpodImage.DockerRegistryOptions{}, libpodImage.SigningOptions{}, false, false)
|
||||
newImages, err = runtime.ImageRuntime().LoadFromArchive(ctx, src, c.String("signature-policy"), writer)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error pulling %q", src)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("Loaded image: ", newImage.InputName)
|
||||
fmt.Println("Loaded image(s): " + getImageNames(newImages))
|
||||
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
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
image2 "github.com/projectatomic/libpod/libpod/image"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -90,7 +91,10 @@ func pullCmd(c *cli.Context) error {
|
||||
registryCreds = creds
|
||||
}
|
||||
|
||||
var writer io.Writer
|
||||
var (
|
||||
writer io.Writer
|
||||
imgID string
|
||||
)
|
||||
if !c.Bool("quiet") {
|
||||
writer = os.Stderr
|
||||
}
|
||||
@ -104,13 +108,23 @@ func pullCmd(c *cli.Context) error {
|
||||
forceSecure = c.Bool("tls-verify")
|
||||
}
|
||||
|
||||
newImage, err := runtime.ImageRuntime().New(getContext(), image, c.String("signature-policy"), c.String("authfile"), writer, &dockerRegistryOptions, image2.SigningOptions{}, true, forceSecure)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error pulling image %q", image)
|
||||
// Possible for docker-archive to have multiple tags, so use NewFromLoad instead
|
||||
if strings.Contains(image, libpod.DockerArchive) {
|
||||
newImage, err := runtime.ImageRuntime().LoadFromArchive(getContext(), image, c.String("signature-policy"), writer)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error pulling image from %q", image)
|
||||
}
|
||||
imgID = newImage[0].ID()
|
||||
} else {
|
||||
newImage, err := runtime.ImageRuntime().New(getContext(), image, c.String("signature-policy"), c.String("authfile"), writer, &dockerRegistryOptions, image2.SigningOptions{}, true, forceSecure)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error pulling image %q", image)
|
||||
}
|
||||
imgID = newImage.ID()
|
||||
}
|
||||
|
||||
// Intentionally choosing to ignore if there is an error because
|
||||
// outputting the image ID is a NTH and not integral to the pull
|
||||
fmt.Println(newImage.ID())
|
||||
fmt.Println(imgID)
|
||||
return nil
|
||||
}
|
||||
|
@ -165,5 +165,5 @@ func pushCmd(c *cli.Context) error {
|
||||
}
|
||||
|
||||
//return runtime.PushImage(srcName, destName, options)
|
||||
return newImage.PushImage(getContext(), destName, manifestType, c.String("authfile"), c.String("signature-policy"), writer, c.Bool("compress"), so, &dockerRegistryOptions, forceSecure)
|
||||
return newImage.PushImage(getContext(), destName, manifestType, c.String("authfile"), c.String("signature-policy"), writer, c.Bool("compress"), so, &dockerRegistryOptions, forceSecure, nil)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/manifest"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
@ -109,24 +110,29 @@ func saveCmd(c *cli.Context) error {
|
||||
return errors.Errorf("unknown format option %q", c.String("format"))
|
||||
}
|
||||
|
||||
// only one image is supported for now
|
||||
// future pull requests will fix this
|
||||
for _, image := range args {
|
||||
newImage, err := runtime.ImageRuntime().NewFromLocal(image)
|
||||
// supports saving multiple tags to the same tar archive
|
||||
var additionaltags []reference.NamedTagged
|
||||
if len(args) > 1 {
|
||||
additionaltags, err = libpodImage.GetAdditionalTags(args[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest := dst
|
||||
// need dest to be in the format transport:path:reference for the following transports
|
||||
if (strings.Contains(dst, libpod.OCIArchive) || strings.Contains(dst, libpod.DockerArchive)) && !strings.Contains(newImage.ID(), image) {
|
||||
dest = dst + ":" + image
|
||||
}
|
||||
if err := newImage.PushImage(getContext(), dest, manifestType, "", "", writer, c.Bool("compress"), libpodImage.SigningOptions{}, &libpodImage.DockerRegistryOptions{}, false); err != nil {
|
||||
if err2 := os.Remove(output); err2 != nil {
|
||||
logrus.Errorf("error deleting %q: %v", output, err)
|
||||
}
|
||||
return errors.Wrapf(err, "unable to save %q", image)
|
||||
}
|
||||
}
|
||||
newImage, err := runtime.ImageRuntime().NewFromLocal(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest := dst
|
||||
// need dest to be in the format transport:path:reference for the following transports
|
||||
if (strings.Contains(dst, libpod.OCIArchive) || strings.Contains(dst, libpod.DockerArchive)) && !strings.Contains(newImage.ID(), args[0]) {
|
||||
dest = dst + ":" + args[0]
|
||||
}
|
||||
if err := newImage.PushImage(getContext(), dest, manifestType, "", "", writer, c.Bool("compress"), libpodImage.SigningOptions{}, &libpodImage.DockerRegistryOptions{}, false, additionaltags); err != nil {
|
||||
if err2 := os.Remove(output); err2 != nil {
|
||||
logrus.Errorf("error deleting %q: %v", output, err)
|
||||
}
|
||||
return errors.Wrapf(err, "unable to save %q", args)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -542,9 +542,11 @@ method Commit(name: string, image_name: string, changes: []string, author: strin
|
||||
method ImportImage(source: string, reference: string, message: string, changes: []string) -> (image: string)
|
||||
|
||||
# ExportImage takes the name or ID of an image and exports it to a destination like a tarball. There is also
|
||||
# a boolean option to force compression. Upon completion, the ID of the image is returned. If the image cannot
|
||||
# be found in local storage, an [ImageNotFound](#ImageNotFound) error will be returned. See also [ImportImage](ImportImage).
|
||||
method ExportImage(name: string, destination: string, compress: bool) -> (image: string)
|
||||
# a booleon option to force compression. It also takes in a string array of tags to be able to save multiple
|
||||
# tags of the same image to a tarball (each tag should be of the form <image>:<tag>). Upon completion, the ID
|
||||
# of the image is returned. If the image cannot be found in local storage, an [ImageNotFound](#ImageNotFound)
|
||||
# error will be returned. See also [ImportImage](ImportImage).
|
||||
method ExportImage(name: string, destination: string, compress: bool, tags: []string) -> (image: string)
|
||||
|
||||
# PullImage pulls an image from a repository to local storage. After the pull is successful, the ID of the image
|
||||
# is returned.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,9 @@
|
||||
package image
|
||||
|
||||
import "github.com/containers/image/types"
|
||||
import (
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/types"
|
||||
)
|
||||
|
||||
// DockerRegistryOptions encapsulates settings that affect how we connect or
|
||||
// authenticate to a remote registry.
|
||||
@ -22,7 +25,7 @@ type DockerRegistryOptions struct {
|
||||
|
||||
// GetSystemContext constructs a new system context from the given signaturePolicy path and the
|
||||
// values in the DockerRegistryOptions
|
||||
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string, forceCompress bool) *types.SystemContext {
|
||||
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string, forceCompress bool, additionalDockerArchiveTags []reference.NamedTagged) *types.SystemContext {
|
||||
sc := &types.SystemContext{
|
||||
SignaturePolicyPath: signaturePolicyPath,
|
||||
DockerAuthConfig: o.DockerRegistryCreds,
|
||||
@ -30,6 +33,7 @@ func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile st
|
||||
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
|
||||
AuthFilePath: authFile,
|
||||
DirForceCompress: forceCompress,
|
||||
DockerArchiveAdditionalTags: additionalDockerArchiveTags,
|
||||
}
|
||||
return sc
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
|
||||
return nil, errors.Wrapf(err, "unable to pull %s", name)
|
||||
}
|
||||
|
||||
newImage.InputName = imageName
|
||||
newImage.InputName = imageName[0]
|
||||
img, err := newImage.getLocalImage()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error retrieving local image after pulling %s", name)
|
||||
@ -156,6 +156,41 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
|
||||
return &newImage, nil
|
||||
}
|
||||
|
||||
// LoadFromArchive creates a new image object for images pulled from a tar archive (podman load)
|
||||
// This function is needed because it is possible for a tar archive to have multiple tags for one image
|
||||
func (ir *Runtime) LoadFromArchive(ctx context.Context, name, signaturePolicyPath string, writer io.Writer) ([]*Image, error) {
|
||||
var newImages []*Image
|
||||
newImage := Image{
|
||||
InputName: name,
|
||||
Local: false,
|
||||
imageruntime: ir,
|
||||
}
|
||||
|
||||
if signaturePolicyPath == "" {
|
||||
signaturePolicyPath = ir.SignaturePolicyPath
|
||||
}
|
||||
imageNames, err := newImage.pullImage(ctx, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{}, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to pull %s", name)
|
||||
}
|
||||
|
||||
for _, name := range imageNames {
|
||||
newImage := Image{
|
||||
InputName: name,
|
||||
Local: true,
|
||||
imageruntime: ir,
|
||||
}
|
||||
img, err := newImage.getLocalImage()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error retrieving local image after pulling %s", name)
|
||||
}
|
||||
newImage.image = img
|
||||
newImages = append(newImages, &newImage)
|
||||
}
|
||||
|
||||
return newImages, nil
|
||||
}
|
||||
|
||||
// Shutdown closes down the storage and require a bool arg as to
|
||||
// whether it should do so forcibly.
|
||||
func (ir *Runtime) Shutdown(force bool) error {
|
||||
@ -428,7 +463,7 @@ func (i *Image) UntagImage(tag string) error {
|
||||
}
|
||||
|
||||
// PushImage pushes the given image to a location described by the given path
|
||||
func (i *Image) PushImage(ctx context.Context, destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, forceSecure bool) error {
|
||||
func (i *Image) PushImage(ctx context.Context, destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, forceSecure bool, additionalDockerArchiveTags []reference.NamedTagged) error {
|
||||
if destination == "" {
|
||||
return errors.Wrapf(syscall.EINVAL, "destination image name must be specified")
|
||||
}
|
||||
@ -464,7 +499,7 @@ func (i *Image) PushImage(ctx context.Context, destination, manifestMIMEType, au
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copyOptions := getCopyOptions(writer, signaturePolicyPath, nil, dockerRegistryOptions, signingOptions, authFile, manifestMIMEType, forceCompress)
|
||||
copyOptions := getCopyOptions(writer, signaturePolicyPath, nil, dockerRegistryOptions, signingOptions, authFile, manifestMIMEType, forceCompress, additionalDockerArchiveTags)
|
||||
if strings.HasPrefix(DockerTransport, dest.Transport().Name()) {
|
||||
imgRef, err := reference.Parse(dest.DockerReference().String())
|
||||
if err != nil {
|
||||
@ -749,7 +784,7 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
|
||||
return nil, err
|
||||
}
|
||||
defer policyContext.Destroy()
|
||||
copyOptions := getCopyOptions(writer, "", nil, nil, signingOptions, "", "", false)
|
||||
copyOptions := getCopyOptions(writer, "", nil, nil, signingOptions, "", "", false, nil)
|
||||
dest, err := is.Transport.ParseStoreReference(ir.store, reference)
|
||||
if err != nil {
|
||||
errors.Wrapf(err, "error getting image reference for %q", reference)
|
||||
|
@ -100,21 +100,25 @@ func (ir *Runtime) getPullListFromRef(ctx context.Context, srcRef types.ImageRef
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
} else {
|
||||
var dest string
|
||||
var dest []string
|
||||
if len(manifest[0].RepoTags) > 0 {
|
||||
dest = manifest[0].RepoTags[0]
|
||||
dest = append(dest, manifest[0].RepoTags...)
|
||||
} else {
|
||||
// If the input image has no repotags, we need to feed it a dest anyways
|
||||
dest, err = getImageDigest(ctx, srcRef, sc)
|
||||
digest, err := getImageDigest(ctx, srcRef, sc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dest = append(dest, digest)
|
||||
}
|
||||
pullInfo, err := ir.getPullStruct(srcRef, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Need to load in all the repo tags from the manifest
|
||||
for _, dst := range dest {
|
||||
pullInfo, err := ir.getPullStruct(srcRef, dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
}
|
||||
} else if srcRef.Transport().Name() == OCIArchive {
|
||||
// retrieve the manifest from index.json to access the image name
|
||||
@ -164,7 +168,7 @@ func (ir *Runtime) getPullListFromRef(ctx context.Context, srcRef types.ImageRef
|
||||
// pullImage pulls an image from configured registries
|
||||
// By default, only the latest tag (or a specific tag if requested) will be
|
||||
// pulled.
|
||||
func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, forceSecure bool) (string, error) {
|
||||
func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, forceSecure bool) ([]string, error) {
|
||||
// pullImage copies the image from the source to the destination
|
||||
var pullStructs []*pullStruct
|
||||
sc := GetSystemContext(signaturePolicyPath, authfile, false)
|
||||
@ -173,31 +177,31 @@ func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signa
|
||||
// could be trying to pull from registry with short name
|
||||
pullStructs, err = i.createNamesToPull()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error getting default registries to try")
|
||||
return nil, errors.Wrap(err, "error getting default registries to try")
|
||||
}
|
||||
} else {
|
||||
pullStructs, err = i.imageruntime.getPullListFromRef(ctx, srcRef, i.InputName, sc)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error getting pullStruct info to pull image %q", i.InputName)
|
||||
return nil, errors.Wrapf(err, "error getting pullStruct info to pull image %q", i.InputName)
|
||||
}
|
||||
}
|
||||
policyContext, err := getPolicyContext(sc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
defer policyContext.Destroy()
|
||||
|
||||
insecureRegistries, err := registries.GetInsecureRegistries()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var images []string
|
||||
for _, imageInfo := range pullStructs {
|
||||
copyOptions := getCopyOptions(writer, signaturePolicyPath, dockerOptions, nil, signingOptions, authfile, "", false)
|
||||
copyOptions := getCopyOptions(writer, signaturePolicyPath, dockerOptions, nil, signingOptions, authfile, "", false, nil)
|
||||
if strings.HasPrefix(DockerTransport, imageInfo.srcRef.Transport().Name()) {
|
||||
imgRef, err := reference.Parse(imageInfo.srcRef.DockerReference().String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
registry := reference.Domain(imgRef.(reference.Named))
|
||||
|
||||
@ -215,10 +219,13 @@ func (i *Image) pullImage(ctx context.Context, writer io.Writer, authfile, signa
|
||||
io.WriteString(writer, "Failed\n")
|
||||
}
|
||||
} else {
|
||||
return imageInfo.image, nil
|
||||
if imageInfo.srcRef.Transport().Name() != DockerArchive {
|
||||
return []string{imageInfo.image}, nil
|
||||
}
|
||||
images = append(images, imageInfo.image)
|
||||
}
|
||||
}
|
||||
return "", errors.Wrapf(err, "error pulling image from")
|
||||
return images, errors.Wrapf(err, "error pulling image from")
|
||||
}
|
||||
|
||||
// createNamesToPull looks at a decomposed image and determines the possible
|
||||
|
@ -53,15 +53,15 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er
|
||||
}
|
||||
|
||||
// getCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
|
||||
func getCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile, manifestType string, forceCompress bool) *cp.Options {
|
||||
func getCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile, manifestType string, forceCompress bool, additionalDockerArchiveTags []reference.NamedTagged) *cp.Options {
|
||||
if srcDockerRegistry == nil {
|
||||
srcDockerRegistry = &DockerRegistryOptions{}
|
||||
}
|
||||
if destDockerRegistry == nil {
|
||||
destDockerRegistry = &DockerRegistryOptions{}
|
||||
}
|
||||
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress, additionalDockerArchiveTags)
|
||||
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress, additionalDockerArchiveTags)
|
||||
return &cp.Options{
|
||||
RemoveSignatures: signing.RemoveSignatures,
|
||||
SignBy: signing.SignBy,
|
||||
@ -110,3 +110,20 @@ func ReposToMap(repotags []string) map[string][]string {
|
||||
}
|
||||
return repos
|
||||
}
|
||||
|
||||
// GetAdditionalTags returns a list of reference.NamedTagged for the
|
||||
// additional tags given in images
|
||||
func GetAdditionalTags(images []string) ([]reference.NamedTagged, error) {
|
||||
var allTags []reference.NamedTagged
|
||||
for _, img := range images {
|
||||
ref, err := reference.ParseNormalizedNamed(img)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error parsing additional tags")
|
||||
}
|
||||
refTagged, isTagged := ref.(reference.NamedTagged)
|
||||
if isTagged {
|
||||
allTags = append(allTags, refTagged)
|
||||
}
|
||||
}
|
||||
return allTags, nil
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ func (i *LibpodAPI) HistoryImage(call ioprojectatomicpodman.VarlinkCall, name st
|
||||
}
|
||||
|
||||
// PushImage pushes an local image to registry
|
||||
// TODO We need to add options for signing, credentials, and tls
|
||||
// TODO We need to add options for signing, credentials, tls, and multi-tag
|
||||
func (i *LibpodAPI) PushImage(call ioprojectatomicpodman.VarlinkCall, name, tag string, tlsVerify bool) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
@ -139,7 +139,7 @@ func (i *LibpodAPI) PushImage(call ioprojectatomicpodman.VarlinkCall, name, tag
|
||||
|
||||
so := image.SigningOptions{}
|
||||
|
||||
if err := newImage.PushImage(getContext(), destname, "", "", "", nil, false, so, &dockerRegistryOptions, false); err != nil {
|
||||
if err := newImage.PushImage(getContext(), destname, "", "", "", nil, false, so, &dockerRegistryOptions, false, nil); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyPushImage(newImage.ID())
|
||||
@ -292,7 +292,7 @@ func (i *LibpodAPI) ImportImage(call ioprojectatomicpodman.VarlinkCall, source,
|
||||
|
||||
// ExportImage exports an image to the provided destination
|
||||
// destination must have the transport type!!
|
||||
func (i *LibpodAPI) ExportImage(call ioprojectatomicpodman.VarlinkCall, name, destination string, compress bool) error {
|
||||
func (i *LibpodAPI) ExportImage(call ioprojectatomicpodman.VarlinkCall, name, destination string, compress bool, tags []string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
@ -301,7 +301,13 @@ func (i *LibpodAPI) ExportImage(call ioprojectatomicpodman.VarlinkCall, name, de
|
||||
if err != nil {
|
||||
return call.ReplyImageNotFound(name)
|
||||
}
|
||||
if err := newImage.PushImage(getContext(), destination, "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, false); err != nil {
|
||||
|
||||
additionalTags, err := image.GetAdditionalTags(tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := newImage.PushImage(getContext(), destination, "", "", "", nil, compress, image.SigningOptions{}, &image.DockerRegistryOptions{}, false, additionalTags); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyExportImage(newImage.ID())
|
||||
|
@ -567,8 +567,8 @@ func (p *PodmanTest) GetHostDistribution() string {
|
||||
return ""
|
||||
}
|
||||
for _, line := range content {
|
||||
if strings.HasPrefix(fmt.Sprintf("%s", line), "ID") {
|
||||
fields := strings.Split(fmt.Sprintf("%s", line), "=")
|
||||
if strings.HasPrefix(fmt.Sprintf("%x", line), "ID") {
|
||||
fields := strings.Split(fmt.Sprintf("%x", line), "=")
|
||||
if len(fields) < 2 {
|
||||
return ""
|
||||
}
|
||||
|
@ -133,4 +133,32 @@ var _ = Describe("Podman load", func() {
|
||||
save.WaitWithDefaultTimeout()
|
||||
Expect(save.ExitCode()).ToNot(Equal(0))
|
||||
})
|
||||
|
||||
It("podman load multiple tags", func() {
|
||||
outfile := filepath.Join(podmanTest.TempDir, "alpine.tar")
|
||||
alpVersion := "docker.io/library/alpine:3.2"
|
||||
|
||||
pull := podmanTest.Podman([]string{"pull", alpVersion})
|
||||
pull.WaitWithDefaultTimeout()
|
||||
Expect(pull.ExitCode()).To(Equal(0))
|
||||
|
||||
save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINE, alpVersion})
|
||||
save.WaitWithDefaultTimeout()
|
||||
Expect(save.ExitCode()).To(Equal(0))
|
||||
|
||||
rmi := podmanTest.Podman([]string{"rmi", ALPINE, alpVersion})
|
||||
rmi.WaitWithDefaultTimeout()
|
||||
Expect(rmi.ExitCode()).To(Equal(0))
|
||||
|
||||
result := podmanTest.Podman([]string{"load", "-i", outfile})
|
||||
result.WaitWithDefaultTimeout()
|
||||
Expect(result.ExitCode()).To(Equal(0))
|
||||
|
||||
inspect := podmanTest.Podman([]string{"inspect", ALPINE})
|
||||
inspect.WaitWithDefaultTimeout()
|
||||
Expect(result.ExitCode()).To(Equal(0))
|
||||
inspect = podmanTest.Podman([]string{"inspect", alpVersion})
|
||||
inspect.WaitWithDefaultTimeout()
|
||||
Expect(result.ExitCode()).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user