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 , 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
+ }
117
123
}
118
124
119
125
path , err := s .ref .blobPath (info .Digest , s .sharedBlobDir )
@@ -140,34 +146,44 @@ func (s *ociImageSource) GetSignatures(ctx context.Context, instanceDigest *dige
140
146
return [][]byte {}, nil
141
147
}
142
148
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).
143
152
func (s * ociImageSource ) getExternalBlob (ctx context.Context , urls []string ) (io.ReadCloser , int64 , error ) {
144
153
if len (urls ) == 0 {
145
154
return nil , 0 , errors .New ("internal error: getExternalBlob called with no URLs" )
146
155
}
147
156
148
157
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 )
152
165
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 ())
154
167
continue
155
168
}
156
169
157
170
resp , err := s .client .Do (req )
158
171
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 ())
160
173
continue
161
174
}
162
175
163
176
if resp .StatusCode != http .StatusOK {
164
177
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 )
166
179
continue
167
180
}
168
181
169
182
return resp .Body , getBlobSize (resp ), nil
170
183
}
184
+ if ! hasSupportedURL {
185
+ return nil , 0 , nil // fallback to non-external blob
186
+ }
171
187
172
188
return nil , 0 , errWrap
173
189
}
0 commit comments