@@ -47,6 +47,9 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) {
47
47
}
48
48
49
49
if err = p .pullV2Repository (ctx , ref ); err != nil {
50
+ if _ , ok := err .(fallbackError ); ok {
51
+ return err
52
+ }
50
53
if registry .ContinueOnError (err ) {
51
54
logrus .Debugf ("Error trying v2 registry: %v" , err )
52
55
return fallbackError {err : err , confirmedV2 : p .confirmedV2 }
@@ -56,9 +59,13 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) {
56
59
}
57
60
58
61
func (p * v2Puller ) pullV2Repository (ctx context.Context , ref reference.Named ) (err error ) {
59
- var refs []reference. Named
62
+ var layersDownloaded bool
60
63
if ! reference .IsNameOnly (ref ) {
61
- refs = []reference.Named {ref }
64
+ var err error
65
+ layersDownloaded , err = p .pullV2Tag (ctx , ref )
66
+ if err != nil {
67
+ return err
68
+ }
62
69
} else {
63
70
manSvc , err := p .repo .Manifests (ctx )
64
71
if err != nil {
@@ -67,11 +74,14 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
67
74
68
75
tags , err := manSvc .Tags ()
69
76
if err != nil {
70
- return err
77
+ // If this repository doesn't exist on V2, we should
78
+ // permit a fallback to V1.
79
+ return allowV1Fallback (err )
71
80
}
72
81
73
- // If this call succeeded, we can be confident that the
74
- // registry on the other side speaks the v2 protocol.
82
+ // The v2 registry knows about this repository, so we will not
83
+ // allow fallback to the v1 protocol even if we encounter an
84
+ // error later on.
75
85
p .confirmedV2 = true
76
86
77
87
// This probably becomes a lot nicer after the manifest
@@ -81,19 +91,20 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
81
91
if err != nil {
82
92
return err
83
93
}
84
- refs = append (refs , tagRef )
85
- }
86
- }
87
-
88
- var layersDownloaded bool
89
- for _ , pullRef := range refs {
90
- // pulledNew is true if either new layers were downloaded OR if existing images were newly tagged
91
- // TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus?
92
- pulledNew , err := p .pullV2Tag (ctx , pullRef )
93
- if err != nil {
94
- return err
94
+ pulledNew , err := p .pullV2Tag (ctx , tagRef )
95
+ if err != nil {
96
+ // Since this is the pull-all-tags case, don't
97
+ // allow an error pulling a particular tag to
98
+ // make the whole pull fall back to v1.
99
+ if fallbackErr , ok := err .(fallbackError ); ok {
100
+ return fallbackErr .err
101
+ }
102
+ return err
103
+ }
104
+ // pulledNew is true if either new layers were downloaded OR if existing images were newly tagged
105
+ // TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus?
106
+ layersDownloaded = layersDownloaded || pulledNew
95
107
}
96
- layersDownloaded = layersDownloaded || pulledNew
97
108
}
98
109
99
110
writeStatus (ref .String (), p .config .ProgressOutput , layersDownloaded )
@@ -214,20 +225,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
214
225
// fallback to the v1 protocol, because dual-version setups may
215
226
// not host all manifests with the v2 protocol. We may also get
216
227
// a "not authorized" error if the manifest doesn't exist.
217
- switch v := err .(type ) {
218
- case errcode.Errors :
219
- if len (v ) != 0 {
220
- if v0 , ok := v [0 ].(errcode.Error ); ok && registry .ShouldV2Fallback (v0 ) {
221
- p .confirmedV2 = false
222
- }
223
- }
224
- case errcode.Error :
225
- if registry .ShouldV2Fallback (v ) {
226
- p .confirmedV2 = false
227
- }
228
- }
229
-
230
- return false , err
228
+ return false , allowV1Fallback (err )
231
229
}
232
230
if unverifiedManifest == nil {
233
231
return false , fmt .Errorf ("image manifest does not exist for tag or digest %q" , tagOrDigest )
@@ -334,6 +332,27 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
334
332
return true , nil
335
333
}
336
334
335
+ // allowV1Fallback checks if the error is a possible reason to fallback to v1
336
+ // (even if confirmedV2 has been set already), and if so, wraps the error in
337
+ // a fallbackError with confirmedV2 set to false. Otherwise, it returns the
338
+ // error unmodified.
339
+ func allowV1Fallback (err error ) error {
340
+ switch v := err .(type ) {
341
+ case errcode.Errors :
342
+ if len (v ) != 0 {
343
+ if v0 , ok := v [0 ].(errcode.Error ); ok && registry .ShouldV2Fallback (v0 ) {
344
+ return fallbackError {err : err , confirmedV2 : false }
345
+ }
346
+ }
347
+ case errcode.Error :
348
+ if registry .ShouldV2Fallback (v ) {
349
+ return fallbackError {err : err , confirmedV2 : false }
350
+ }
351
+ }
352
+
353
+ return err
354
+ }
355
+
337
356
func verifyManifest (signedManifest * schema1.SignedManifest , ref reference.Named ) (m * schema1.Manifest , err error ) {
338
357
// If pull by digest, then verify the manifest digest. NOTE: It is
339
358
// important to do this first, before any other content validation. If the
0 commit comments