From c582c4844f3e5b0e0bafe0e52ea9ec42dd65197e Mon Sep 17 00:00:00 2001 From: James Hewitt Date: Fri, 3 Dec 2021 16:02:40 +0000 Subject: [PATCH] Add option to preserve digests on copy When enabled, if digests can't be preserved an error will be raised. Signed-off-by: James Hewitt --- cmd/skopeo/sync.go | 3 +++ completions/bash/skopeo | 1 + docs/skopeo-sync.1.md | 2 ++ integration/sync_test.go | 16 ++++++++++++++++ 4 files changed, 22 insertions(+) diff --git a/cmd/skopeo/sync.go b/cmd/skopeo/sync.go index ef688fd2..a8418737 100644 --- a/cmd/skopeo/sync.go +++ b/cmd/skopeo/sync.go @@ -40,6 +40,7 @@ type syncOptions struct { 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 + preserveDigests bool // Preserve digests during sync keepGoing bool // Whether or not to abort the sync if there are any errors during syncing the images } @@ -106,6 +107,7 @@ See skopeo-sync(1) for details. 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.BoolVar(&opts.preserveDigests, "preserve-digests", false, "Preserve digests of images and lists") flags.BoolVarP(&opts.keepGoing, "keep-going", "", false, "Do not abort the sync if any image copy fails") flags.AddFlagSet(&sharedFlags) flags.AddFlagSet(&deprecatedTLSVerifyFlags) @@ -577,6 +579,7 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error { ReportWriter: os.Stdout, DestinationCtx: destinationCtx, ImageListSelection: imageListSelection, + PreserveDigests: opts.preserveDigests, OptimizeDestinationImageAlreadyExists: true, ForceManifestMIMEType: manifestType, } diff --git a/completions/bash/skopeo b/completions/bash/skopeo index 6858e126..02a28845 100644 --- a/completions/bash/skopeo +++ b/completions/bash/skopeo @@ -109,6 +109,7 @@ _skopeo_sync() { --src-no-creds --src-tls-verify --keep-going + --preserve-digests " local transports diff --git a/docs/skopeo-sync.1.md b/docs/skopeo-sync.1.md index 83e348ac..5f51b966 100644 --- a/docs/skopeo-sync.1.md +++ b/docs/skopeo-sync.1.md @@ -62,6 +62,8 @@ Print usage statement. **--scoped** Prefix images with the source image path, so that multiple images with the same name can be stored at _destination_. +**--preserve-digests** Preserve the digests during copying. Fail if the digest cannot be preserved. + **--remove-signatures** Do not copy signatures, if any, from _source-image_. This is necessary when copying a signed image to a destination which does not support signatures. **--sign-by**=_key-id_ Add a signature using that key ID for an image name corresponding to _destination-image_. diff --git a/integration/sync_test.go b/integration/sync_test.go index b1b734ea..2465a2ff 100644 --- a/integration/sync_test.go +++ b/integration/sync_test.go @@ -163,6 +163,22 @@ func (s *SyncSuite) TestDocker2DirTaggedAll(c *check.C) { c.Assert(out, check.Equals, "") } +func (s *SyncSuite) TestPreserveDigests(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 needing an Internet connection. + image := pullableTaggedManifestList + + // copy docker => dir + assertSkopeoSucceeds(c, "", "copy", "--all", "--preserve-digests", "docker://"+image, "dir:"+tmpDir) + _, err = os.Stat(path.Join(tmpDir, "manifest.json")) + c.Assert(err, check.IsNil) + + assertSkopeoFails(c, ".*Instructed to preserve digests.*", "copy", "--all", "--preserve-digests", "--format=oci", "docker://"+image, "dir:"+tmpDir) +} + func (s *SyncSuite) TestScoped(c *check.C) { // FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection. image := pullableTaggedImage