Skip to content

Commit ac380b9

Browse files
committed
Add an option to allow copying image indexes alone
This new --multi-arch option allows the user to select between copying the image associated with the system platform, all images in the index, or just the index itself without attempting to copy the images. Signed-off-by: James Hewitt <[email protected]>
1 parent 904c745 commit ac380b9

File tree

3 files changed

+74
-13
lines changed

3 files changed

+74
-13
lines changed

cmd/skopeo/copy.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type copyOptions struct {
3232
format commonFlag.OptionalString // Force conversion of the image to a specified format
3333
quiet bool // Suppress output information when copying images
3434
all bool // Copy all of the images if the source is a list
35+
multiArch commonFlag.OptionalString // How to handle multi architecture indexes
3536
encryptLayer []int // The list of layers to encrypt
3637
encryptionKeys []string // Keys needed to encrypt the image
3738
decryptionKeys []string // Keys needed to decrypt the image
@@ -72,6 +73,7 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
7273
flags.StringSliceVar(&opts.additionalTags, "additional-tag", []string{}, "additional tags (supports docker-archive)")
7374
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress output information when copying images")
7475
flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list")
76+
flags.Var(commonFlag.NewOptionalStringValue(&opts.multiArch), "multi-arch", `How to handle multi-architecture indexes (system, all, or index-only)`)
7577
flags.BoolVar(&opts.removeSignatures, "remove-signatures", false, "Do not copy signatures from SOURCE-IMAGE")
7678
flags.StringVar(&opts.signByFingerprint, "sign-by", "", "Sign the image using a GPG key with the specified `FINGERPRINT`")
7779
flags.StringVar(&opts.digestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
@@ -82,6 +84,24 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
8284
return cmd
8385
}
8486

87+
// parseMultiArch parses the list processing selection
88+
// It returns the copy.ImageListSelection to use with image.Copy option
89+
func parseMultiArch(multiArch string) (copy.ImageListSelection, error) {
90+
switch multiArch {
91+
case "system":
92+
return copy.CopySystemImage, nil
93+
case "all":
94+
return copy.CopyAllImages, nil
95+
// There is no CopyNoImages value in copy.ImageListSelection, but because we
96+
// don't provide an option to select a set of images to copy, we can use
97+
// CopySpecificImages.
98+
case "index-only":
99+
return copy.CopySpecificImages, nil
100+
default:
101+
return copy.CopySystemImage, fmt.Errorf("unknown multi-arch option %q. Choose one of the supported options: 'system', 'all', or 'index-only'", multiArch)
102+
}
103+
}
104+
85105
func (opts *copyOptions) run(args []string, stdout io.Writer) error {
86106
if len(args) != 2 {
87107
return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")}
@@ -143,7 +163,17 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
143163
if opts.quiet {
144164
stdout = nil
145165
}
166+
146167
imageListSelection := copy.CopySystemImage
168+
if opts.multiArch.Present() && opts.all {
169+
return fmt.Errorf("Cannot use all and multi-arch flags together")
170+
}
171+
if opts.multiArch.Present() {
172+
imageListSelection, err = parseMultiArch(opts.multiArch.Value())
173+
if err != nil {
174+
return err
175+
}
176+
}
147177
if opts.all {
148178
imageListSelection = copy.CopyAllImages
149179
}

docs/skopeo-copy.1.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ MANIFEST TYPE (oci, v2s1, or v2s2) to use in the destination (default is manifes
6666

6767
Print usage statement
6868

69+
**--multi-arch**
70+
71+
If _source-image_ refers to an index of images for multiple architectures, this option controls what is copied. Default is system.
72+
73+
Options:
74+
- system: Copy the image that matches the system architecture instead of the index
75+
- all: Copy all of the images in the index and the index itself
76+
- index-only: Copy only the index itself
77+
6978
**--quiet**, **-q**
7079

7180
Suppress output information when copying images.

integration/copy_test.go

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,20 @@ func (s *CopySuite) TestCopyWithManifestList(c *check.C) {
103103
assertSkopeoSucceeds(c, "", "copy", knownListImage, "dir:"+dir)
104104
}
105105

106-
func (s *CopySuite) TestCopyAllWithManifestList(c *check.C) {
106+
func (s *CopySuite) TestCopyAllFlagWithManifestList(c *check.C) {
107107
dir, err := ioutil.TempDir("", "copy-all-manifest-list")
108108
c.Assert(err, check.IsNil)
109109
defer os.RemoveAll(dir)
110110
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage, "dir:"+dir)
111111
}
112112

113+
func (s *CopySuite) TestCopyAllWithManifestList(c *check.C) {
114+
dir, err := ioutil.TempDir("", "copy-all-manifest-list")
115+
c.Assert(err, check.IsNil)
116+
defer os.RemoveAll(dir)
117+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", knownListImage, "dir:"+dir)
118+
}
119+
113120
func (s *CopySuite) TestCopyAllWithManifestListRoundTrip(c *check.C) {
114121
oci1, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
115122
c.Assert(err, check.IsNil)
@@ -123,10 +130,10 @@ func (s *CopySuite) TestCopyAllWithManifestListRoundTrip(c *check.C) {
123130
dir2, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
124131
c.Assert(err, check.IsNil)
125132
defer os.RemoveAll(dir2)
126-
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage, "oci:"+oci1)
127-
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci1, "dir:"+dir1)
128-
assertSkopeoSucceeds(c, "", "copy", "--all", "dir:"+dir1, "oci:"+oci2)
129-
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci2, "dir:"+dir2)
133+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", knownListImage, "oci:"+oci1)
134+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
135+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "dir:"+dir1, "oci:"+oci2)
136+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci2, "dir:"+dir2)
130137
assertDirImagesAreEqual(c, dir1, dir2)
131138
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)
132139
c.Assert(out, check.Equals, "")
@@ -145,15 +152,30 @@ func (s *CopySuite) TestCopyAllWithManifestListConverge(c *check.C) {
145152
dir2, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
146153
c.Assert(err, check.IsNil)
147154
defer os.RemoveAll(dir2)
148-
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage, "oci:"+oci1)
149-
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci1, "dir:"+dir1)
150-
assertSkopeoSucceeds(c, "", "copy", "--all", "--format", "oci", knownListImage, "dir:"+dir2)
151-
assertSkopeoSucceeds(c, "", "copy", "--all", "dir:"+dir2, "oci:"+oci2)
155+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", knownListImage, "oci:"+oci1)
156+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
157+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "--format", "oci", knownListImage, "dir:"+dir2)
158+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "dir:"+dir2, "oci:"+oci2)
152159
assertDirImagesAreEqual(c, dir1, dir2)
153160
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)
154161
c.Assert(out, check.Equals, "")
155162
}
156163

164+
func (s *CopySuite) TestCopyNoneWithManifestList(c *check.C) {
165+
dir1, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
166+
c.Assert(err, check.IsNil)
167+
defer os.RemoveAll(dir1)
168+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=index-only", knownListImage, "dir:"+dir1)
169+
170+
manifestPath := filepath.Join(dir1, "manifest.json")
171+
readManifest, err := ioutil.ReadFile(manifestPath)
172+
c.Assert(err, check.IsNil)
173+
mimeType := manifest.GuessMIMEType(readManifest)
174+
c.Assert(mimeType, check.Equals, "application/vnd.docker.distribution.manifest.list.v2+json")
175+
out := combinedOutputOfCommand(c, "ls", "-1", dir1)
176+
c.Assert(out, check.Equals, "manifest.json\nversion\n")
177+
}
178+
157179
func (s *CopySuite) TestCopyWithManifestListConverge(c *check.C) {
158180
oci1, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
159181
c.Assert(err, check.IsNil)
@@ -168,9 +190,9 @@ func (s *CopySuite) TestCopyWithManifestListConverge(c *check.C) {
168190
c.Assert(err, check.IsNil)
169191
defer os.RemoveAll(dir2)
170192
assertSkopeoSucceeds(c, "", "copy", knownListImage, "oci:"+oci1)
171-
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci1, "dir:"+dir1)
193+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
172194
assertSkopeoSucceeds(c, "", "copy", "--format", "oci", knownListImage, "dir:"+dir2)
173-
assertSkopeoSucceeds(c, "", "copy", "--all", "dir:"+dir2, "oci:"+oci2)
195+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "dir:"+dir2, "oci:"+oci2)
174196
assertDirImagesAreEqual(c, dir1, dir2)
175197
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)
176198
c.Assert(out, check.Equals, "")
@@ -181,7 +203,7 @@ func (s *CopySuite) TestCopyAllWithManifestListStorageFails(c *check.C) {
181203
c.Assert(err, check.IsNil)
182204
defer os.RemoveAll(storage)
183205
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
184-
assertSkopeoFails(c, `.*destination transport .* does not support copying multiple images as a group.*`, "copy", "--all", knownListImage, "containers-storage:"+storage+"test")
206+
assertSkopeoFails(c, `.*destination transport .* does not support copying multiple images as a group.*`, "copy", "--multi-arch=all", knownListImage, "containers-storage:"+storage+"test")
185207
}
186208

187209
func (s *CopySuite) TestCopyWithManifestListStorage(c *check.C) {
@@ -239,7 +261,7 @@ func (s *CopySuite) TestCopyWithManifestListDigest(c *check.C) {
239261
c.Assert(err, check.IsNil)
240262
digest := manifestDigest.String()
241263
assertSkopeoSucceeds(c, "", "copy", knownListImage+"@"+digest, "dir:"+dir1)
242-
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage+"@"+digest, "dir:"+dir2)
264+
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", knownListImage+"@"+digest, "dir:"+dir2)
243265
assertSkopeoSucceeds(c, "", "copy", "dir:"+dir1, "oci:"+oci1)
244266
assertSkopeoSucceeds(c, "", "copy", "dir:"+dir2, "oci:"+oci2)
245267
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)

0 commit comments

Comments
 (0)