Skip to content

Commit bbf5356

Browse files
Support s3 mirroring during oc adm release mirror
Extend the target support for s3 to release mirroring.
1 parent 63176d5 commit bbf5356

2 files changed

Lines changed: 78 additions & 12 deletions

File tree

pkg/oc/cli/admin/release/mirror.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ import (
3434
// NewMirrorOptions creates the options for mirroring a release.
3535
func NewMirrorOptions(streams genericclioptions.IOStreams) *MirrorOptions {
3636
return &MirrorOptions{
37-
IOStreams: streams,
37+
IOStreams: streams,
38+
ParallelOptions: imagemanifest.ParallelOptions{MaxPerRegistry: 6},
3839
}
3940
}
4041

@@ -153,7 +154,7 @@ func (o *MirrorOptions) Run() error {
153154

154155
var recreateRequired bool
155156
var hasPrefix bool
156-
var targetFn func(name string) imagereference.DockerImageReference
157+
var targetFn func(name string) mirror.MirrorReference
157158
var dst string
158159
if len(o.ToImageStream) > 0 {
159160
dst = imagereference.DockerImageReference{
@@ -167,13 +168,13 @@ func (o *MirrorOptions) Run() error {
167168

168169
if strings.Contains(dst, "${component}") {
169170
format := strings.Replace(dst, "${component}", replaceComponentMarker, -1)
170-
dstRef, err := imagereference.Parse(format)
171+
dstRef, err := mirror.ParseMirrorReference(format)
171172
if err != nil {
172173
return fmt.Errorf("--to must be a valid image reference: %v", err)
173174
}
174-
targetFn = func(name string) imagereference.DockerImageReference {
175+
targetFn = func(name string) mirror.MirrorReference {
175176
value := strings.Replace(dst, "${component}", name, -1)
176-
ref, err := imagereference.Parse(value)
177+
ref, err := mirror.ParseMirrorReference(value)
177178
if err != nil {
178179
klog.Fatalf("requested component %q could not be injected into %s: %v", name, dst, err)
179180
}
@@ -183,22 +184,25 @@ func (o *MirrorOptions) Run() error {
183184
recreateRequired = replaceCount > 1 || (replaceCount == 1 && !strings.Contains(dstRef.Tag, replaceComponentMarker))
184185

185186
} else {
186-
ref, err := imagereference.Parse(dst)
187+
ref, err := mirror.ParseMirrorReference(dst)
187188
if err != nil {
188189
return fmt.Errorf("--to must be a valid image repository: %v", err)
189190
}
190191
if len(ref.ID) > 0 || len(ref.Tag) > 0 {
191192
return fmt.Errorf("--to must be to an image repository and may not contain a tag or digest")
192193
}
193-
targetFn = func(name string) imagereference.DockerImageReference {
194+
targetFn = func(name string) mirror.MirrorReference {
194195
copied := ref
195196
copied.Tag = name
196197
return copied
197198
}
198199
hasPrefix = true
199200
}
200201

201-
o.TargetFn = targetFn
202+
o.TargetFn = func(name string) imagereference.DockerImageReference {
203+
ref := targetFn(name)
204+
return ref.DockerImageReference
205+
}
202206

203207
if recreateRequired {
204208
return fmt.Errorf("when mirroring to multiple repositories, use the new release command with --from-release and --mirror")
@@ -255,10 +259,11 @@ func (o *MirrorOptions) Run() error {
255259
Name: o.ToRelease,
256260
})
257261
} else if !o.SkipRelease {
262+
dstRef := targetFn("release")
258263
mappings = append(mappings, mirror.Mapping{
259-
Type: mirror.DestinationRegistry,
260264
Source: srcRef,
261-
Destination: targetFn("release"),
265+
Type: dstRef.Type(),
266+
Destination: dstRef.Combined(),
262267
Name: "release",
263268
})
264269
}
@@ -278,10 +283,11 @@ func (o *MirrorOptions) Run() error {
278283
return fmt.Errorf("image-references should only contain pointers to images by digest: %s", tag.From.Name)
279284
}
280285

286+
dstMirrorRef := targetFn(tag.Name)
281287
mappings = append(mappings, mirror.Mapping{
282-
Type: mirror.DestinationRegistry,
283288
Source: from,
284-
Destination: targetFn(tag.Name),
289+
Type: dstMirrorRef.Type(),
290+
Destination: dstMirrorRef.Combined(),
285291
Name: tag.Name,
286292
})
287293
klog.V(2).Infof("Mapping %#v", mappings[len(mappings)-1])

pkg/oc/cli/image/mirror/mappings.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"os"
8+
"path"
89
"strings"
910
"sync"
1011

@@ -36,6 +37,65 @@ func parseSource(ref string) (reference.DockerImageReference, error) {
3637
return src, nil
3738
}
3839

40+
type AWSReference struct {
41+
Bucket string
42+
Region string
43+
}
44+
45+
type MirrorReference struct {
46+
reference.DockerImageReference
47+
AWS *AWSReference
48+
}
49+
50+
func (r MirrorReference) Type() DestinationType {
51+
if r.AWS != nil {
52+
return DestinationS3
53+
}
54+
return DestinationRegistry
55+
}
56+
57+
func (r MirrorReference) Combined() reference.DockerImageReference {
58+
if r.AWS == nil {
59+
return r.DockerImageReference
60+
}
61+
copied := r.DockerImageReference
62+
copied.Registry = "s3.amazonaws.com"
63+
copied.Namespace = path.Join(r.AWS.Region, r.AWS.Bucket, r.Namespace)
64+
return copied
65+
}
66+
67+
func ParseMirrorReference(ref string) (MirrorReference, error) {
68+
var dst MirrorReference
69+
switch {
70+
case strings.HasPrefix(ref, "s3://"):
71+
ref = strings.TrimPrefix(ref, "s3://")
72+
dst.AWS = &AWSReference{}
73+
}
74+
image, err := reference.Parse(ref)
75+
if err != nil {
76+
return dst, fmt.Errorf("%q is not a valid image reference: %v", ref, err)
77+
}
78+
if len(dst.ID) != 0 {
79+
return dst, fmt.Errorf("you must specify a tag for DST or leave it blank to only push by digest")
80+
}
81+
dst.DockerImageReference = image
82+
if dst.AWS != nil {
83+
parts := strings.SplitN(image.RepositoryName(), "/", 3)
84+
if len(parts) < 3 {
85+
return dst, fmt.Errorf("s3 target URLs must have at least 3 path segments: REGION/BUCKET/REPO[/...]")
86+
}
87+
dst.AWS.Region = parts[0]
88+
dst.AWS.Bucket = parts[1]
89+
dst.Registry = fmt.Sprintf("%s.s3.amazonaws.com", parts[0])
90+
dst.Name = path.Base(parts[2])
91+
dst.Namespace = path.Dir(parts[2])
92+
if dst.Namespace == "." {
93+
dst.Namespace = ""
94+
}
95+
}
96+
return dst, nil
97+
}
98+
3999
func parseDestination(ref string) (reference.DockerImageReference, DestinationType, error) {
40100
dstType := DestinationRegistry
41101
switch {

0 commit comments

Comments
 (0)