@@ -26,7 +26,6 @@ import (
26
26
"github.com/spf13/cobra"
27
27
"k8s.io/client-go/rest"
28
28
29
- kerrors "k8s.io/apimachinery/pkg/util/errors"
30
29
apirequest "k8s.io/apiserver/pkg/endpoints/request"
31
30
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
32
31
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@@ -297,86 +296,6 @@ func (o *pushOptions) Complete(args []string) error {
297
296
return nil
298
297
}
299
298
300
- type key struct {
301
- registry string
302
- repository string
303
- }
304
-
305
- type destination struct {
306
- t DestinationType
307
- ref imageapi.DockerImageReference
308
- tags []string
309
- }
310
-
311
- type pushTargets map [key ]destination
312
-
313
- type destinations struct {
314
- ref imageapi.DockerImageReference
315
- tags map [string ]pushTargets
316
- digests map [string ]pushTargets
317
- }
318
-
319
- func (d destinations ) mergeIntoDigests (srcDigest godigest.Digest , target pushTargets ) {
320
- srcKey := srcDigest .String ()
321
- current , ok := d .digests [srcKey ]
322
- if ! ok {
323
- d .digests [srcKey ] = target
324
- return
325
- }
326
- for repo , dst := range target {
327
- existing , ok := current [repo ]
328
- if ! ok {
329
- current [repo ] = dst
330
- continue
331
- }
332
- existing .tags = append (existing .tags , dst .tags ... )
333
- }
334
- }
335
-
336
- type targetTree map [key ]destinations
337
-
338
- func buildTargetTree (mappings []Mapping ) targetTree {
339
- tree := make (targetTree )
340
- for _ , m := range mappings {
341
- srcKey := key {registry : m .Source .Registry , repository : m .Source .RepositoryName ()}
342
- dstKey := key {registry : m .Destination .Registry , repository : m .Destination .RepositoryName ()}
343
-
344
- src , ok := tree [srcKey ]
345
- if ! ok {
346
- src .ref = m .Source .AsRepository ()
347
- src .digests = make (map [string ]pushTargets )
348
- src .tags = make (map [string ]pushTargets )
349
- tree [srcKey ] = src
350
- }
351
-
352
- var current pushTargets
353
- if tag := m .Source .Tag ; len (tag ) != 0 {
354
- current = src .tags [tag ]
355
- if current == nil {
356
- current = make (pushTargets )
357
- src .tags [tag ] = current
358
- }
359
- } else {
360
- current = src .digests [m .Source .ID ]
361
- if current == nil {
362
- current = make (pushTargets )
363
- src .digests [m .Source .ID ] = current
364
- }
365
- }
366
-
367
- dst , ok := current [dstKey ]
368
- if ! ok {
369
- dst .ref = m .Destination .AsRepository ()
370
- dst .t = m .Type
371
- }
372
- if len (m .Destination .Tag ) > 0 {
373
- dst .tags = append (dst .tags , m .Destination .Tag )
374
- }
375
- current [dstKey ] = dst
376
- }
377
- return tree
378
- }
379
-
380
299
type retrieverError struct {
381
300
src , dst imageapi.DockerImageReference
382
301
err error
@@ -418,44 +337,52 @@ func (o *pushOptions) includeDescriptor(d *manifestlist.ManifestDescriptor) bool
418
337
var ErrAlreadyExists = fmt .Errorf ("blob already exists in the target location" )
419
338
420
339
func (o * pushOptions ) Run () error {
340
+ p , err := o .plan ()
341
+ if err != nil {
342
+ return err
343
+ }
344
+ p .Print (o .Out )
345
+
346
+ return nil
347
+ }
348
+
349
+ func (o * pushOptions ) plan () (* plan , error ) {
421
350
tree := buildTargetTree (o .Mappings )
422
351
423
352
creds := dockercredentials .NewLocal ()
424
353
ctx := apirequest .NewContext ()
425
354
426
355
rt , err := rest .TransportFor (& rest.Config {})
427
356
if err != nil {
428
- return err
357
+ return nil , err
429
358
}
430
359
insecureRT , err := rest .TransportFor (& rest.Config {TLSClientConfig : rest.TLSClientConfig {Insecure : true }})
431
360
if err != nil {
432
- return err
361
+ return nil , err
433
362
}
434
363
srcClient := registryclient .NewContext (rt , insecureRT ).WithCredentials (creds )
435
364
toContext := registryclient .NewContext (rt , insecureRT ).WithActions ("pull" , "push" )
436
365
437
- var errs []error
366
+ plan := & plan {}
367
+
438
368
for _ , src := range tree {
439
369
srcRepo , err := srcClient .Repository (ctx , src .ref .DockerClientDefaults ().RegistryURL (), src .ref .RepositoryName (), o .Insecure )
440
370
if err != nil {
441
- errs = append ( errs , retrieverError {err : fmt .Errorf ("unable to connect to %s: %v" , src .ref , err ), src : src .ref })
371
+ plan . AddError ( retrieverError {err : fmt .Errorf ("unable to connect to %s: %v" , src .ref , err ), src : src .ref })
442
372
continue
443
373
}
444
374
445
375
manifests , err := srcRepo .Manifests (ctx )
446
376
if err != nil {
447
- errs = append ( errs , retrieverError {src : src .ref , err : fmt .Errorf ("unable to access source image %s manifests: %v" , src .ref , err )})
377
+ plan . AddError ( retrieverError {src : src .ref , err : fmt .Errorf ("unable to access source image %s manifests: %v" , src .ref , err )})
448
378
continue
449
379
}
450
380
451
- var tagErrs []retrieverError
452
- var digestErrs []retrieverError
453
-
454
381
// convert source tags to digests
455
382
for srcTag , pushTargets := range src .tags {
456
383
desc , err := srcRepo .Tags (ctx ).Get (ctx , srcTag )
457
384
if err != nil {
458
- tagErrs = append ( tagErrs , retrieverError {src : src .ref , err : fmt .Errorf ("unable to retrieve source image %s by tag: %v" , src .ref , err )})
385
+ plan . AddError ( retrieverError {src : src .ref , err : fmt .Errorf ("unable to retrieve source image %s by tag: %v" , src .ref , err )})
459
386
continue
460
387
}
461
388
srcDigest := desc .Digest
@@ -470,14 +397,14 @@ func (o *pushOptions) Run() error {
470
397
srcDigest := godigest .Digest (srcDigestString )
471
398
srcManifest , err := manifests .Get (ctx , godigest .Digest (srcDigest ), schema2ManifestOnly )
472
399
if err != nil {
473
- digestErrs = append ( digestErrs , retrieverError {src : src .ref , err : fmt .Errorf ("unable to retrieve source image %s manifest: %v" , src .ref , err )})
400
+ plan . AddError ( retrieverError {src : src .ref , err : fmt .Errorf ("unable to retrieve source image %s manifest: %v" , src .ref , err )})
474
401
continue
475
402
}
476
403
477
404
// filter or load manifest list as appropriate
478
405
srcManifests , srcManifest , srcDigest , err := processManifestList (ctx , srcDigest , srcManifest , manifests , src .ref , o .includeDescriptor )
479
406
if err != nil {
480
- digestErrs = append ( digestErrs , retrieverError {src : src .ref , err : err })
407
+ plan . AddError ( retrieverError {src : src .ref , err : err })
481
408
continue
482
409
}
483
410
if len (srcManifests ) == 0 {
@@ -493,14 +420,18 @@ func (o *pushOptions) Run() error {
493
420
494
421
toRepo , err := o .Repository (ctx , toContext , creds , dst .t , dst .ref )
495
422
if err != nil {
496
- digestErrs = append ( digestErrs , retrieverError {src : src .ref , dst : dst .ref , err : fmt .Errorf ("unable to connect to %s: %v" , dst .ref , err )})
423
+ plan . AddError ( retrieverError {src : src .ref , dst : dst .ref , err : fmt .Errorf ("unable to connect to %s: %v" , dst .ref , err )})
497
424
continue
498
425
}
499
426
500
427
canonicalTo := toRepo .Named ()
428
+
429
+ repoPlan := plan .RegistryPlan (dst .ref .Registry ).RepositoryPlan (canonicalTo .String ())
430
+ blobPlan := repoPlan .Blobs (src .ref .String ())
431
+
501
432
toManifests , err := toRepo .Manifests (ctx )
502
433
if err != nil {
503
- digestErrs = append ( digestErrs , retrieverError {src : src .ref , dst : dst .ref , err : fmt .Errorf ("unable to access destination image %s manifests: %v" , src .ref , err )})
434
+ repoPlan . AddError ( retrieverError {src : src .ref , dst : dst .ref , err : fmt .Errorf ("unable to access destination image %s manifests: %v" , src .ref , err )})
504
435
continue
505
436
}
506
437
@@ -513,29 +444,24 @@ func (o *pushOptions) Run() error {
513
444
default :
514
445
if _ , err := toManifests .Get (ctx , srcDigest ); err != nil {
515
446
mustCopyLayers = true
447
+ blobPlan .AlreadyExists (distribution.Descriptor {Digest : srcDigest })
516
448
} else {
517
449
glog .V (4 ).Infof ("Manifest exists in %s, no need to copy layers without --force" , dst .ref )
518
450
}
519
451
}
520
452
521
453
if mustCopyLayers {
522
- if errs := uploadBlobs (ctx , dst , srcRepo , toRepo , srcManifests , src .ref , srcDigest , canonicalFrom , o .Force , o . SkipMount , o . ErrOut ); len (errs ) > 0 {
523
- digestErrs = append ( digestErrs , errs ... )
454
+ if errs := planBlobs (ctx , blobPlan , dst , srcRepo , toRepo , srcManifests , src .ref , o .Force ); len (errs ) > 0 {
455
+ repoPlan . AddError ( errs ... )
524
456
continue
525
457
}
526
458
}
527
459
528
- if errs := uploadAndTagManifests (ctx , dst , srcManifest , src .ref , toManifests , o .Out , toRepo .Blobs (ctx ), canonicalTo ); len (errs ) > 0 {
529
- digestErrs = append (digestErrs , errs ... )
530
- continue
531
- }
460
+ repoPlan .Manifests ().Copy (srcDigest , srcManifest , dst .tags , toManifests )
532
461
}
533
462
}
534
- for _ , err := range append (tagErrs , digestErrs ... ) {
535
- errs = append (errs , err )
536
- }
537
463
}
538
- return kerrors . NewAggregate ( errs )
464
+ return plan , nil
539
465
}
540
466
541
467
func processManifestList (ctx apirequest.Context , srcDigest godigest.Digest , srcManifest distribution.Manifest , manifests distribution.ManifestService , ref imageapi.DockerImageReference , filterFn func (* manifestlist.ManifestDescriptor ) bool ) ([]distribution.Manifest , distribution.Manifest , godigest.Digest , error ) {
@@ -601,6 +527,54 @@ func processManifestList(ctx apirequest.Context, srcDigest godigest.Digest, srcM
601
527
}
602
528
}
603
529
530
+ func planBlobs (
531
+ ctx apirequest.Context ,
532
+ plan * repositoryBlobCopy ,
533
+ dst destination ,
534
+ srcRepo , toRepo distribution.Repository ,
535
+ srcManifests []distribution.Manifest ,
536
+ srcRef imageapi.DockerImageReference ,
537
+ force bool ,
538
+ ) []error {
539
+
540
+ // upload all the blobs
541
+ toBlobs := toRepo .Blobs (ctx )
542
+ srcBlobs := srcRepo .Blobs (ctx )
543
+
544
+ var errs []error
545
+
546
+ // upload the each manifest
547
+ for _ , srcManifest := range srcManifests {
548
+ switch srcManifest .(type ) {
549
+ case * schema2.DeserializedManifest :
550
+ case * manifestlist.DeserializedManifestList :
551
+ // we do not need to upload layers in a manifestlist
552
+ continue
553
+ default :
554
+ errs = append (errs , retrieverError {src : srcRef , dst : dst .ref , err : fmt .Errorf ("the manifest type %T is not supported" , srcManifest )})
555
+ continue
556
+ }
557
+
558
+ for _ , blob := range srcManifest .References () {
559
+ // if we aren't forcing upload, skip the blob copy
560
+ if ! force {
561
+ _ , err := toBlobs .Stat (ctx , blob .Digest )
562
+ if err == nil {
563
+ // blob exists, skip
564
+ plan .AlreadyExists (blob )
565
+ glog .V (5 ).Infof ("Server reports blob exists %#v" , blob )
566
+ continue
567
+ }
568
+ if err != distribution .ErrBlobUnknown {
569
+ glog .V (5 ).Infof ("Server was unable to check whether blob exists %s: %v" , blob .Digest , err )
570
+ }
571
+ }
572
+ plan .Copy (blob , srcBlobs , toBlobs )
573
+ }
574
+ }
575
+ return errs
576
+ }
577
+
604
578
func uploadBlobs (
605
579
ctx apirequest.Context ,
606
580
dst destination ,
0 commit comments