5
5
"io"
6
6
"io/ioutil"
7
7
"net/http"
8
+ "net/url"
8
9
"os"
9
10
"strconv"
10
11
@@ -113,7 +114,12 @@ func (s *ociImageSource) HasThreadSafeGetBlob() bool {
113
114
// May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location.
114
115
func (s * ociImageSource ) GetBlob (ctx context.Context , info types.BlobInfo , cache types.BlobInfoCache ) (io.ReadCloser , int64 , error ) {
115
116
if len (info .URLs ) != 0 {
116
- return s .getExternalBlob (ctx , info .URLs )
117
+ r , s , fallback , err := s .getExternalBlob (ctx , info .URLs )
118
+ if err == nil {
119
+ return r , s , nil
120
+ } else if ! fallback {
121
+ return nil , 0 , err
122
+ }
117
123
}
118
124
119
125
path , err := s .ref .blobPath (info .Digest , s .sharedBlobDir )
@@ -140,36 +146,48 @@ func (s *ociImageSource) GetSignatures(ctx context.Context, instanceDigest *dige
140
146
return [][]byte {}, nil
141
147
}
142
148
143
- func (s * ociImageSource ) getExternalBlob (ctx context.Context , urls []string ) (io.ReadCloser , int64 , error ) {
149
+ // getExternalBlob returns the reader of the first available blob URL.
150
+ // This returns a bool value (named "fallback") which indicates whether the caller can fallback to the pull of
151
+ // the non-external blob (i.e. pull from the registry).
152
+ // This parameter becomes true only if no url is supported by this function.
153
+ // Do not pass zero-length "urls". This is considered a fatal error and returns an error without allowing fallback.
154
+ func (s * ociImageSource ) getExternalBlob (ctx context.Context , urls []string ) (_ io.ReadCloser , _ int64 , fallback bool , _ error ) {
144
155
if len (urls ) == 0 {
145
- return nil , 0 , errors .New ("internal error: getExternalBlob called with no URLs" )
156
+ return nil , 0 , false , errors .New ("internal error: getExternalBlob called with no URLs" ) // fatal error
146
157
}
147
158
148
159
errWrap := errors .New ("failed fetching external blob from all urls" )
149
- for _ , url := range urls {
150
-
151
- req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
160
+ noSupportedURL := true
161
+ for _ , u := range urls {
162
+ if u , err := url .Parse (u ); err != nil || (u .Scheme != "http" && u .Scheme != "https" ) {
163
+ continue // unsupported url. skip this url.
164
+ }
165
+ noSupportedURL = false
166
+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , u , nil )
152
167
if err != nil {
153
- errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , url , err .Error ())
168
+ errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , u , err .Error ())
154
169
continue
155
170
}
156
171
157
172
resp , err := s .client .Do (req )
158
173
if err != nil {
159
- errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , url , err .Error ())
174
+ errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , u , err .Error ())
160
175
continue
161
176
}
162
177
163
178
if resp .StatusCode != http .StatusOK {
164
179
resp .Body .Close ()
165
- errWrap = errors .Wrapf (errWrap , "fetching %s failed, response code not 200" , url )
180
+ errWrap = errors .Wrapf (errWrap , "fetching %s failed, response code not 200" , u )
166
181
continue
167
182
}
168
183
169
- return resp .Body , getBlobSize (resp ), nil
184
+ return resp .Body , getBlobSize (resp ), false , nil
185
+ }
186
+ if noSupportedURL {
187
+ return nil , 0 , true , errors .New ("no supported url is specified" ) // fallback to non-external blob
170
188
}
171
189
172
- return nil , 0 , errWrap
190
+ return nil , 0 , false , errWrap
173
191
}
174
192
175
193
// LayerInfosForCopy returns either nil (meaning the values in the manifest are fine), or updated values for the layer
0 commit comments