55 "io"
66 "io/ioutil"
77 "net/http"
8+ "net/url"
89 "os"
910 "strconv"
1011
@@ -113,7 +114,12 @@ func (s *ociImageSource) HasThreadSafeGetBlob() bool {
113114// May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location.
114115func (s * ociImageSource ) GetBlob (ctx context.Context , info types.BlobInfo , cache types.BlobInfoCache ) (io.ReadCloser , int64 , error ) {
115116 if len (info .URLs ) != 0 {
116- return s .getExternalBlob (ctx , info .URLs )
117+ r , s , err := s .getExternalBlob (ctx , info .URLs )
118+ if err != nil {
119+ return nil , 0 , err
120+ } else if r != nil {
121+ return r , s , nil
122+ }
117123 }
118124
119125 path , err := s .ref .blobPath (info .Digest , s .sharedBlobDir )
@@ -140,34 +146,44 @@ func (s *ociImageSource) GetSignatures(ctx context.Context, instanceDigest *dige
140146 return [][]byte {}, nil
141147}
142148
149+ // getExternalBlob returns the reader of the first available blob URL from urls, which must not be empty.
150+ // This function can return nil reader when no url is supported by this function. In this case, the caller
151+ // should fallback to fetch the non-external blob (i.e. pull from the registry).
143152func (s * ociImageSource ) getExternalBlob (ctx context.Context , urls []string ) (io.ReadCloser , int64 , error ) {
144153 if len (urls ) == 0 {
145154 return nil , 0 , errors .New ("internal error: getExternalBlob called with no URLs" )
146155 }
147156
148157 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 )
158+ hasSupportedURL := false
159+ for _ , u := range urls {
160+ if u , err := url .Parse (u ); err != nil || (u .Scheme != "http" && u .Scheme != "https" ) {
161+ continue // unsupported url. skip this url.
162+ }
163+ hasSupportedURL = true
164+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , u , nil )
152165 if err != nil {
153- errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , url , err .Error ())
166+ errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , u , err .Error ())
154167 continue
155168 }
156169
157170 resp , err := s .client .Do (req )
158171 if err != nil {
159- errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , url , err .Error ())
172+ errWrap = errors .Wrapf (errWrap , "fetching %s failed %s" , u , err .Error ())
160173 continue
161174 }
162175
163176 if resp .StatusCode != http .StatusOK {
164177 resp .Body .Close ()
165- errWrap = errors .Wrapf (errWrap , "fetching %s failed, response code not 200" , url )
178+ errWrap = errors .Wrapf (errWrap , "fetching %s failed, response code not 200" , u )
166179 continue
167180 }
168181
169182 return resp .Body , getBlobSize (resp ), nil
170183 }
184+ if ! hasSupportedURL {
185+ return nil , 0 , nil // fallback to non-external blob
186+ }
171187
172188 return nil , 0 , errWrap
173189}
0 commit comments