-
Notifications
You must be signed in to change notification settings - Fork 743
/
copy.go
158 lines (140 loc) · 4.96 KB
/
copy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package main
import (
"errors"
"fmt"
"io"
"strings"
"github.com/containers/image/copy"
"github.com/containers/image/docker/reference"
"github.com/containers/image/manifest"
"github.com/containers/image/transports"
"github.com/containers/image/transports/alltransports"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/urfave/cli"
)
type copyOptions struct {
global *globalOptions
srcImage *imageOptions
destImage *imageDestOptions
additionalTags cli.StringSlice // For docker-archive: destinations, in addition to the name:tag specified as destination, also add these
removeSignatures bool // Do not copy signatures from the source image
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
format optionalString // Force conversion of the image to a specified format
quiet bool // Suppress output information when copying images
}
func copyCmd(global *globalOptions) cli.Command {
sharedFlags, sharedOpts := sharedImageFlags()
srcFlags, srcOpts := imageFlags(global, sharedOpts, "src-", "screds")
destFlags, destOpts := imageDestFlags(global, sharedOpts, "dest-", "dcreds")
opts := copyOptions{global: global,
srcImage: srcOpts,
destImage: destOpts,
}
return cli.Command{
Name: "copy",
Usage: "Copy an IMAGE-NAME from one location to another",
Description: fmt.Sprintf(`
Container "IMAGE-NAME" uses a "transport":"details" format.
Supported transports:
%s
See skopeo(1) section "IMAGE NAMES" for the expected format
`, strings.Join(transports.ListNames(), ", ")),
ArgsUsage: "SOURCE-IMAGE DESTINATION-IMAGE",
Action: commandAction(opts.run),
// FIXME: Do we need to namespace the GPG aspect?
Flags: append(append(append([]cli.Flag{
cli.StringSliceFlag{
Name: "additional-tag",
Usage: "additional tags (supports docker-archive)",
Value: &opts.additionalTags, // Surprisingly StringSliceFlag does not support Destination:, but modifies Value: in place.
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Suppress output information when copying images",
Destination: &opts.quiet,
},
cli.BoolFlag{
Name: "remove-signatures",
Usage: "Do not copy signatures from SOURCE-IMAGE",
Destination: &opts.removeSignatures,
},
cli.StringFlag{
Name: "sign-by",
Usage: "Sign the image using a GPG key with the specified `FINGERPRINT`",
Destination: &opts.signByFingerprint,
},
cli.GenericFlag{
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)",
Value: newOptionalStringValue(&opts.format),
},
}, sharedFlags...), srcFlags...), destFlags...),
}
}
func (opts *copyOptions) run(args []string, stdout io.Writer) error {
if len(args) != 2 {
return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")}
}
if err := needReexec(args); err != nil {
return err
}
policyContext, err := opts.global.getPolicyContext()
if err != nil {
return fmt.Errorf("Error loading trust policy: %v", err)
}
defer policyContext.Destroy()
srcRef, err := alltransports.ParseImageName(args[0])
if err != nil {
return fmt.Errorf("Invalid source name %s: %v", args[0], err)
}
destRef, err := alltransports.ParseImageName(args[1])
if err != nil {
return fmt.Errorf("Invalid destination name %s: %v", args[1], err)
}
sourceCtx, err := opts.srcImage.newSystemContext()
if err != nil {
return err
}
destinationCtx, err := opts.destImage.newSystemContext()
if err != nil {
return err
}
var manifestType string
if opts.format.present {
switch opts.format.value {
case "oci":
manifestType = imgspecv1.MediaTypeImageManifest
case "v2s1":
manifestType = manifest.DockerV2Schema1SignedMediaType
case "v2s2":
manifestType = manifest.DockerV2Schema2MediaType
default:
return fmt.Errorf("unknown format %q. Choose one of the supported formats: 'oci', 'v2s1', or 'v2s2'", opts.format.value)
}
}
for _, image := range opts.additionalTags {
ref, err := reference.ParseNormalizedNamed(image)
if err != nil {
return fmt.Errorf("error parsing additional-tag '%s': %v", image, err)
}
namedTagged, isNamedTagged := ref.(reference.NamedTagged)
if !isNamedTagged {
return fmt.Errorf("additional-tag '%s' must be a tagged reference", image)
}
destinationCtx.DockerArchiveAdditionalTags = append(destinationCtx.DockerArchiveAdditionalTags, namedTagged)
}
ctx, cancel := opts.global.commandTimeoutContext()
defer cancel()
if opts.quiet {
stdout = nil
}
_, err = copy.Image(ctx, policyContext, destRef, srcRef, ©.Options{
RemoveSignatures: opts.removeSignatures,
SignBy: opts.signByFingerprint,
ReportWriter: stdout,
SourceCtx: sourceCtx,
DestinationCtx: destinationCtx,
ForceManifestMIMEType: manifestType,
})
return err
}