diff --git a/cmd/skopeo/sync.go b/cmd/skopeo/sync.go index 1c9a90d0a9..7723032020 100644 --- a/cmd/skopeo/sync.go +++ b/cmd/skopeo/sync.go @@ -35,6 +35,7 @@ type syncOptions struct { source string // Source repository name destination string // Destination registry name scoped bool // When true, namespace copied images at destination using the source repository name + all bool // Copy all of the images if an image in the source is a list } // repoDescriptor contains information of a single repository used as a sync source. @@ -96,6 +97,7 @@ See skopeo-sync(1) for details. flags.StringVarP(&opts.source, "src", "s", "", "SOURCE transport type") flags.StringVarP(&opts.destination, "dest", "d", "", "DESTINATION transport type") flags.BoolVar(&opts.scoped, "scoped", false, "Images at DESTINATION are prefix using the full source image path as scope") + flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list") flags.AddFlagSet(&sharedFlags) flags.AddFlagSet(&srcFlags) flags.AddFlagSet(&destFlags) @@ -522,6 +524,11 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error { return errors.New("sync from 'dir' to 'dir' not implemented, consider using rsync instead") } + imageListSelection := copy.CopySystemImage + if opts.all { + imageListSelection = copy.CopyAllImages + } + sourceCtx, err := opts.srcImage.newSystemContext() if err != nil { return err @@ -547,10 +554,11 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error { imagesNumber := 0 options := copy.Options{ - RemoveSignatures: opts.removeSignatures, - SignBy: opts.signByFingerprint, - ReportWriter: os.Stdout, - DestinationCtx: destinationCtx, + RemoveSignatures: opts.removeSignatures, + SignBy: opts.signByFingerprint, + ReportWriter: os.Stdout, + DestinationCtx: destinationCtx, + ImageListSelection: imageListSelection, } for _, srcRepo := range srcRepoList { diff --git a/docs/skopeo-sync.1.md b/docs/skopeo-sync.1.md index be5195e0e8..3bb1cf3435 100644 --- a/docs/skopeo-sync.1.md +++ b/docs/skopeo-sync.1.md @@ -32,6 +32,11 @@ When the `--scoped` option is specified, images are prefixed with the source ima name can be stored at _destination_. ## OPTIONS +**--all** +If one of the images in __src__ refers to a list of images, instead of copying just the image which matches the current OS and +architecture (subject to the use of the global --override-os, --override-arch and --override-variant options), attempt to copy all of +the images in the list, and the list itself. + **--authfile** _path_ Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`. diff --git a/integration/sync_test.go b/integration/sync_test.go index 36922da615..3735c0041c 100644 --- a/integration/sync_test.go +++ b/integration/sync_test.go @@ -117,6 +117,34 @@ func (s *SyncSuite) TestDocker2DirTagged(c *check.C) { c.Assert(out, check.Equals, "") } +func (s *SyncSuite) TestDocker2DirTaggedAll(c *check.C) { + tmpDir, err := ioutil.TempDir("", "skopeo-sync-test") + c.Assert(err, check.IsNil) + defer os.RemoveAll(tmpDir) + + // FIXME: It would be nice to use one of the local Docker registries instead of neeeding an Internet connection. + image := "busybox:latest" + imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image)) + c.Assert(err, check.IsNil) + imagePath := imageRef.DockerReference().String() + + dir1 := path.Join(tmpDir, "dir1") + dir2 := path.Join(tmpDir, "dir2") + + // sync docker => dir + assertSkopeoSucceeds(c, "", "sync", "--all", "--scoped", "--src", "docker", "--dest", "dir", image, dir1) + _, err = os.Stat(path.Join(dir1, imagePath, "manifest.json")) + c.Assert(err, check.IsNil) + + // copy docker => dir + assertSkopeoSucceeds(c, "", "copy", "--all", "docker://"+image, "dir:"+dir2) + _, err = os.Stat(path.Join(dir2, "manifest.json")) + c.Assert(err, check.IsNil) + + out := combinedOutputOfCommand(c, "diff", "-urN", path.Join(dir1, imagePath), dir2) + c.Assert(out, check.Equals, "") +} + func (s *SyncSuite) TestScoped(c *check.C) { // FIXME: It would be nice to use one of the local Docker registries instead of neeeding an Internet connection. image := "busybox:latest"