diff --git a/cmd/skopeo/copy.go b/cmd/skopeo/copy.go index fc522d9f..9979685d 100644 --- a/cmd/skopeo/copy.go +++ b/cmd/skopeo/copy.go @@ -7,9 +7,11 @@ import ( "strings" "github.com/containers/image/copy" + "github.com/containers/image/manifest" "github.com/containers/image/transports" "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/urfave/cli" ) @@ -55,12 +57,27 @@ func copyHandler(context *cli.Context) error { return err } + var manifestType string + if context.IsSet("format") { + switch context.String("format") { + case "oci": + manifestType = imgspecv1.MediaTypeImageManifest + case "v2s1": + manifestType = manifest.DockerV2Schema1SignedMediaType + case "v2s2": + manifestType = manifest.DockerV2Schema2MediaType + default: + return fmt.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", context.String("format")) + } + } + return copy.Image(policyContext, destRef, srcRef, ©.Options{ - RemoveSignatures: removeSignatures, - SignBy: signBy, - ReportWriter: os.Stdout, - SourceCtx: sourceCtx, - DestinationCtx: destinationCtx, + RemoveSignatures: removeSignatures, + SignBy: signBy, + ReportWriter: os.Stdout, + SourceCtx: sourceCtx, + DestinationCtx: destinationCtx, + ForceManifestMIMEType: manifestType, }) } @@ -131,5 +148,13 @@ var copyCmd = cli.Command{ Value: "", Usage: "`DIRECTORY` to use to store retrieved blobs (OCI layout destinations only)", }, + cli.StringFlag{ + Name: "format, f", + Usage: "`MANIFEST TYPE` (oci, v2s1, or v2s2) to use when saving image to directory using the 'dir:' transport (default is manifest type of source)", + }, + cli.BoolFlag{ + Name: "dest-compress", + Usage: "Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source)", + }, }, } diff --git a/cmd/skopeo/utils.go b/cmd/skopeo/utils.go index e78fce6d..7bd4d953 100644 --- a/cmd/skopeo/utils.go +++ b/cmd/skopeo/utils.go @@ -18,6 +18,7 @@ func contextFromGlobalOptions(c *cli.Context, flagPrefix string) (*types.SystemC DockerInsecureSkipTLSVerify: !c.GlobalBoolT("tls-verify"), OSTreeTmpDirPath: c.String(flagPrefix + "ostree-tmp-dir"), OCISharedBlobDirPath: c.String(flagPrefix + "shared-blob-dir"), + DirForceCompress: c.Bool(flagPrefix + "compress"), } if c.IsSet(flagPrefix + "tls-verify") { ctx.DockerInsecureSkipTLSVerify = !c.BoolT(flagPrefix + "tls-verify") diff --git a/completions/bash/skopeo b/completions/bash/skopeo index b21644c0..10f621f0 100644 --- a/completions/bash/skopeo +++ b/completions/bash/skopeo @@ -20,20 +20,24 @@ _complete_() { } _skopeo_copy() { - local options_with_args=" - --sign-by - --src-creds --screds - --src-cert-dir - --src-tls-verify - --dest-creds --dcreds - --dest-cert-dir - --dest-ostree-tmp-dir - --dest-tls-verify - " - local boolean_options=" - --remove-signatures - " - _complete_ "$options_with_args" "$boolean_options" + local options_with_args=" + --format -f + --sign-by + --src-creds --screds + --src-cert-dir + --src-tls-verify + --dest-creds --dcreds + --dest-cert-dir + --dest-ostree-tmp-dir + --dest-tls-verify + " + + local boolean_options=" + --dest-compress + --remove-signatures + " + + _complete_ "$options_with_args" "$boolean_options" } _skopeo_inspect() { diff --git a/docs/skopeo.1.md b/docs/skopeo.1.md index a955b828..bf050a4e 100644 --- a/docs/skopeo.1.md +++ b/docs/skopeo.1.md @@ -60,12 +60,16 @@ Uses the system's trust policy to validate images, rejects images not trusted by _destination-image_ use the "image name" format described above + **--format, -f** _manifest-type_ Manifest type (oci, v2s1, or v2s2) to use when saving image to directory using the 'dir:' transport (default is manifest type of source) + **--remove-signatures** do not copy signatures, if any, from _source-image_. 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_ **--src-creds** _username[:password]_ for accessing the source registry + **--dest-compress** _bool-value_ Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source) + **--dest-creds** _username[:password]_ for accessing the destination registry **--src-cert-dir** _path_ Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the source registry diff --git a/integration/copy_test.go b/integration/copy_test.go index 95e939bb..506a1b3f 100644 --- a/integration/copy_test.go +++ b/integration/copy_test.go @@ -15,6 +15,7 @@ import ( "github.com/containers/image/signature" "github.com/go-check/check" "github.com/opencontainers/go-digest" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/image-tools/image" ) @@ -591,6 +592,32 @@ func (s *CopySuite) TestCopySchemaConversion(c *check.C) { s.testCopySchemaConversionRegistries(c, "docker://"+v2s1DockerRegistryURL+"/schema1", "docker://"+v2DockerRegistryURL+"/schema2") } +func (s *CopySuite) TestCopyManifestConversion(c *check.C) { + topDir, err := ioutil.TempDir("", "manifest-conversion") + c.Assert(err, check.IsNil) + defer os.RemoveAll(topDir) + srcDir := filepath.Join(topDir, "source") + destDir1 := filepath.Join(topDir, "dest1") + destDir2 := filepath.Join(topDir, "dest2") + + // oci to v2s1 and vice-versa not supported yet + // get v2s2 manifest type + assertSkopeoSucceeds(c, "", "copy", "docker://busybox", "dir:"+srcDir) + verifyManifestMIMEType(c, srcDir, manifest.DockerV2Schema2MediaType) + // convert from v2s2 to oci + assertSkopeoSucceeds(c, "", "copy", "--format=oci", "dir:"+srcDir, "dir:"+destDir1) + verifyManifestMIMEType(c, destDir1, imgspecv1.MediaTypeImageManifest) + // convert from oci to v2s2 + assertSkopeoSucceeds(c, "", "copy", "--format=v2s2", "dir:"+destDir1, "dir:"+destDir2) + verifyManifestMIMEType(c, destDir2, manifest.DockerV2Schema2MediaType) + // convert from v2s2 to v2s1 + assertSkopeoSucceeds(c, "", "copy", "--format=v2s1", "dir:"+srcDir, "dir:"+destDir1) + verifyManifestMIMEType(c, destDir1, manifest.DockerV2Schema1SignedMediaType) + // convert from v2s1 to v2s2 + assertSkopeoSucceeds(c, "", "copy", "--format=v2s2", "dir:"+destDir1, "dir:"+destDir2) + verifyManifestMIMEType(c, destDir2, manifest.DockerV2Schema2MediaType) +} + func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Registry, schema2Registry string) { topDir, err := ioutil.TempDir("", "schema-conversion") c.Assert(err, check.IsNil) diff --git a/vendor.conf b/vendor.conf index 6aa535b9..f8ff8289 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,5 +1,5 @@ github.com/urfave/cli v1.17.0 -github.com/containers/image master +github.com/containers/image f950aa3529148eb0dea90888c24b6682da641b13 github.com/opencontainers/go-digest master gopkg.in/cheggaaa/pb.v1 ad4efe000aa550bb54918c06ebbadc0ff17687b9 https://github.com/cheggaaa/pb github.com/containers/storage master