Skip to content

Commit

Permalink
WIP - multi-image archives
Browse files Browse the repository at this point in the history
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
  • Loading branch information
vrothberg committed Jul 31, 2020
1 parent 1170430 commit aef021b
Show file tree
Hide file tree
Showing 102 changed files with 4,326 additions and 2,954 deletions.
2 changes: 1 addition & 1 deletion cmd/podman/images/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func saveFlags(flags *pflag.FlagSet) {
flags.StringVar(&saveOpts.Format, "format", define.V2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)")
flags.StringVarP(&saveOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
flags.BoolVarP(&saveOpts.Quiet, "quiet", "q", false, "Suppress the output")

flags.BoolVarP(&saveOpts.MultiImageArchive, "multi-image-archive", "m", false, "Interpret additional arguments as images not tags and create a multi-image-archive (only for docker-archive)")
}

func save(cmd *cobra.Command, args []string) error {
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ require (
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
golang.org/x/text v0.3.3 // indirect
gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.18.6
k8s.io/apimachinery v0.18.6
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab
)

replace github.com/containers/image/v5 => github.com/mtrmac/image/v5 v5.0.0-20200729202706-569764057fa8
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,12 @@ github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDpl
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.2 h1:Q0/IPs8ohfbXNxEfyJ2pFVmvJu5BhqJUAmc6ES9NKbo=
github.com/containers/ocicrypt v1.0.2/go.mod h1:nsOhbP19flrX6rE7ieGFvBlr7modwmNjsqWarIUce4M=
github.com/containers/ocicrypt v1.0.3 h1:vYgl+RZ9Q3DPMuTfxmN+qp0X2Bj52uuY2vnt6GzVe1c=
github.com/containers/ocicrypt v1.0.3/go.mod h1:CUBa+8MRNL/VkpxYIpaMtgn1WgXGyvPQj8jcy0EVG6g=
github.com/containers/psgo v1.5.1 h1:MQNb7FLbXqBdqz6u4lI2QWizVz4RSTzs1+Nk9XT1iVA=
github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU=
github.com/containers/storage v1.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc=
github.com/containers/storage v1.21.1/go.mod h1:I1EIAA7B4OwWRSA0b4yq2AW1wjvvfcY0zLWQuwTa4zw=
github.com/containers/storage v1.21.2 h1:bf9IqA+g6ClBviqVG5lVCp5tTH9lvWwjYws7mVYSti0=
github.com/containers/storage v1.21.2/go.mod h1:I1EIAA7B4OwWRSA0b4yq2AW1wjvvfcY0zLWQuwTa4zw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
Expand Down Expand Up @@ -235,6 +238,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/insomniacslk/dhcp v0.0.0-20200420235442-ed3125c2efe7/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw=
Expand Down Expand Up @@ -296,6 +301,8 @@ github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6Sl
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
github.com/mtrmac/gpgme v0.1.2 h1:dNOmvYmsrakgW7LcgiprD0yfRuQQe8/C8F6Z+zogO3s=
github.com/mtrmac/gpgme v0.1.2/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI=
github.com/mtrmac/image/v5 v5.0.0-20200729202706-569764057fa8 h1:1Sl2U2EXN8gm4S/23mjdQsv/XR/ayBGY/d3sjDNL0h4=
github.com/mtrmac/image/v5 v5.0.0-20200729202706-569764057fa8/go.mod h1:H4Qv6eH5dA9fvSU59wzb5pfDjuD+OHaJjm/IcEkKmf4=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
Expand Down Expand Up @@ -457,6 +464,8 @@ github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02
github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
github.com/vbauerster/mpb/v5 v5.2.2 h1:zIICVOm+XD+uV6crpSORaL6I0Q1WqOdvxZTp+r3L9cw=
github.com/vbauerster/mpb/v5 v5.2.2/go.mod h1:W5Fvgw4dm3/0NhqzV8j6EacfuTe5SvnzBRwiXxDR9ww=
github.com/vbauerster/mpb/v5 v5.2.4 h1:PLP8vv75RcEgxGoJVtKaRD2FHSxEmIV/u4ZuOrfO8Qg=
github.com/vbauerster/mpb/v5 v5.2.4/go.mod h1:K4iCHQp5sWnmAgEn+uW1sAxSilctb4JPAGXx49jV+Aw=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
Expand All @@ -479,6 +488,8 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
Expand Down Expand Up @@ -559,6 +570,8 @@ golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
Expand Down
186 changes: 183 additions & 3 deletions libpod/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/docker/archive"
dockerarchive "github.com/containers/image/v5/docker/archive"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/image"
Expand Down Expand Up @@ -170,15 +171,194 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile
return newImage, nil
}

// SaveImages stores one more images in a multi-image archive.
// Note that only `docker-archive` supports storing multiple
// image.
func (ir *Runtime) SaveImages(ctx context.Context, namesOrIDs []string, format string, outputFile string, quiet bool) (finalErr error) {
if format != DockerArchive {
return errors.Errorf("multi-image archives are only supported in in the %q format", DockerArchive)
}

sys := GetSystemContext("", "", false)

archWriter, err := archive.NewWriter(sys, outputFile)
if err != nil {
return err
}
defer func() {
err := archWriter.Close()
if err == nil {
return
}
if finalErr == nil {
finalErr = err
return
}
finalErr = errors.Wrap(finalErr, err.Error())
}()

// Check whether c/image's progress bars use stderr or stdout. Use
// stderr in case we need to be quiet or if the output is set to
// stdout. If the output is set of stout, any log message there would
// corrupt the tarfile.
writer := os.Stdout
if quiet || outputFile == os.Stdout.Name() {
writer = os.Stderr
}

// Look up the images (and their tags) in the local storage.
imageTags := make(map[*Image][]reference.NamedTagged)
imageOrder := []*Image{}
for _, nameOrID := range namesOrIDs {
// Look up the name or ID in the local image storage.
localImage, err := ir.NewFromLocal(nameOrID)
if err != nil {
return err
}

tags, exists := imageTags[localImage]
if !exists {
// Preserve the relative order AND make the archives
// deterministic - iterating over go maps yields a
// random order.
imageOrder = append(imageOrder, localImage)
}

// Unless we referred to an ID, add the input as a tag.
id := localImage.ID()
if strings.HasPrefix(id, nameOrID) {
continue
}
tag, err := NormalizedTag(nameOrID)
if err != nil {
return err
}
refTagged, _ := tag.(reference.NamedTagged)
tags = append(tags, refTagged)
imageTags[localImage] = tags
}

policyContext, err := getPolicyContext(sys)
if err != nil {
return err
}
defer func() {
if err := policyContext.Destroy(); err != nil {
logrus.Errorf("failed to destroy policy context: %q", err)
}
}()

// Now copy the images one-by-one.
for _, img := range imageOrder {
additionalTags := imageTags[img]
copyOptions := getCopyOptions(sys, writer, nil, nil, SigningOptions{}, "", additionalTags)
copyOptions.DestinationCtx.SystemRegistriesConfPath = registries.SystemRegistriesConfPath()

// For copying, we need a source reference that we can create
// from the image.
src, err := is.Transport.ParseStoreReference(img.imageruntime.store, img.ID())
if err != nil {
return errors.Wrapf(err, "error getting source imageReference for %q", img.InputName)
}

dest, err := archWriter.NewReference(nil)
if err != nil {
return err
}
_, err = cp.Image(ctx, policyContext, dest, src, copyOptions)
if err != nil {
return err
}
}

return nil
}

func (ir *Runtime) loadMultiImageArchive(ctx context.Context, sys *types.SystemContext, ref types.ImageReference, writer io.Writer) (loadedImages []string, finalErr error) {
if ref.Transport().Name() != DockerArchive {
return nil, errors.Errorf("wrong transport %q: only %q supports multi-image archives", ref, DockerArchive)
}

archReader, err := archive.NewReader(sys, ref.StringWithinTransport())
if err != nil {
return nil, err
}
defer func() {
err := archReader.Close()
if err == nil {
return
}
if finalErr == nil {
finalErr = err
return
}
finalErr = errors.Wrap(finalErr, err.Error())
}()

refLists, err := archReader.List()
if err != nil {
return nil, err
}

for _, refList := range refLists {
refPairs := []pullRefPair{}
for _, ref := range refList {
destName := ""
if dkrRef := ref.DockerReference(); dkrRef != nil {
destName = ref.DockerReference().String()
} else {
destName, err = getImageDigest(ctx, ref, sys)
if err != nil {
return nil, err
}
}
pair, err := ir.getPullRefPair(ref, destName)
if err != nil {
return nil, err
}
refPairs = append(refPairs, pair)
}

goal := pullGoal{
refPairs: refPairs,
pullAllPairs: true,
usedSearchRegistries: false,
searchedRegistries: nil,
}

imageNames, err := ir.doPullImage(ctx, sys, goal, writer, SigningOptions{}, &DockerRegistryOptions{}, nil)
if err != nil {
return nil, err
}

loadedImages = append(loadedImages, imageNames...)
}

return loadedImages, nil
}

// LoadFromArchiveReference creates a new image object for images pulled from a tar archive and the like (podman load)
// This function is needed because it is possible for a tar archive to have multiple tags for one image
func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*Image, error) {
if signaturePolicyPath == "" {
signaturePolicyPath = ir.SignaturePolicyPath
}
imageNames, err := ir.pullImageFromReference(ctx, srcRef, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{})
if err != nil {
return nil, errors.Wrapf(err, "unable to pull %s", transports.ImageName(srcRef))

var imageNames []string
var err error

switch srcRef.Transport().Name() {
case DockerArchive:
sys := GetSystemContext(signaturePolicyPath, "", false)
imageNames, err = ir.loadMultiImageArchive(ctx, sys, srcRef, writer)
if err != nil {
return nil, errors.Wrapf(err, "unable to load %s", transports.ImageName(srcRef))
}
default:
imageNames, err = ir.pullImageFromReference(ctx, srcRef, writer, "", signaturePolicyPath, SigningOptions{}, &DockerRegistryOptions{})
if err != nil {
return nil, errors.Wrapf(err, "unable to pull %s", transports.ImageName(srcRef))
}
}

newImages := make([]*Image, 0, len(imageNames))
Expand Down
16 changes: 13 additions & 3 deletions pkg/domain/entities/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,21 @@ type ImageImportReport struct {
Id string //nolint
}

// ImageSaveOptions provide options for saving images.
type ImageSaveOptions struct {
// Compress layers when saving to a directory.
Compress bool
Format string
Output string
Quiet bool
// Format of saving the image: oci-archive, oci-dir (directory with oci
// manifest type), docker-archive, docker-dir (directory with v2s2
// manifest type).
Format string
// MultiImageArchive denotes if the created archive shall include more
// than one image.
MultiImageArchive bool
// Output - write image to the specified path.
Output string
// Quiet - suppress output when copying images
Quiet bool
}

// ImageTreeOptions provides options for ImageEngine.Tree()
Expand Down
4 changes: 4 additions & 0 deletions pkg/domain/infra/abi/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,10 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti
}

func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error {
if options.MultiImageArchive {
nameOrIDs := append([]string{nameOrID}, tags...)
return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet)
}
newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions vendor/github.com/containers/image/v5/copy/copy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit aef021b

Please sign in to comment.