@@ -18,17 +18,18 @@ package docker
1818
1919import (
2020 "context"
21+ "io"
2122 "net/http"
2223 "net/url"
2324 "path"
24- "strconv"
2525 "strings"
2626
2727 "github.com/containerd/containerd/errdefs"
2828 "github.com/containerd/containerd/images"
2929 "github.com/containerd/containerd/log"
3030 "github.com/containerd/containerd/reference"
3131 "github.com/containerd/containerd/remotes"
32+ "github.com/containerd/containerd/remotes/docker/schema1"
3233 "github.com/containerd/containerd/version"
3334 digest "github.com/opencontainers/go-digest"
3435 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -157,6 +158,32 @@ func NewResolver(options ResolverOptions) remotes.Resolver {
157158 }
158159}
159160
161+ func getManifestMediaType (resp * http.Response ) string {
162+ // Strip encoding data (manifests should always be ascii JSON)
163+ contentType := resp .Header .Get ("Content-Type" )
164+ if sp := strings .IndexByte (contentType , ';' ); sp != - 1 {
165+ contentType = contentType [0 :sp ]
166+ }
167+
168+ // As of Apr 30 2019 the registry.access.redhat.com registry does not specify
169+ // the content type of any data but uses schema1 manifests.
170+ if contentType == "text/plain" {
171+ contentType = images .MediaTypeDockerSchema1Manifest
172+ }
173+ return contentType
174+ }
175+
176+ type countingReader struct {
177+ reader io.Reader
178+ bytesRead int64
179+ }
180+
181+ func (r * countingReader ) Read (p []byte ) (int , error ) {
182+ n , err := r .reader .Read (p )
183+ r .bytesRead += int64 (n )
184+ return n , err
185+ }
186+
160187var _ remotes.Resolver = & dockerResolver {}
161188
162189func (r * dockerResolver ) Resolve (ctx context.Context , ref string ) (string , ocispec.Descriptor , error ) {
@@ -227,40 +254,56 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
227254 }
228255 return "" , ocispec.Descriptor {}, errors .Errorf ("unexpected status code %v: %v" , u , resp .Status )
229256 }
257+ size := resp .ContentLength
230258
231259 // this is the only point at which we trust the registry. we use the
232260 // content headers to assemble a descriptor for the name. when this becomes
233261 // more robust, we mostly get this information from a secure trust store.
234262 dgstHeader := digest .Digest (resp .Header .Get ("Docker-Content-Digest" ))
263+ contentType := getManifestMediaType (resp )
235264
236- if dgstHeader != "" {
265+ if dgstHeader != "" && size != - 1 {
237266 if err := dgstHeader .Validate (); err != nil {
238267 return "" , ocispec.Descriptor {}, errors .Wrapf (err , "%q in header not a valid digest" , dgstHeader )
239268 }
240269 dgst = dgstHeader
241- }
242-
243- if dgst == "" {
244- return "" , ocispec.Descriptor {}, errors .Errorf ("could not resolve digest for %v" , ref )
245- }
270+ } else {
271+ log .G (ctx ).Debug ("no Docker-Content-Digest header, fetching manifest instead" )
246272
247- var (
248- size int64
249- sizeHeader = resp .Header .Get ("Content-Length" )
250- )
251-
252- size , err = strconv .ParseInt (sizeHeader , 10 , 64 )
253- if err != nil {
273+ req , err := http .NewRequest (http .MethodGet , u , nil )
274+ if err != nil {
275+ return "" , ocispec.Descriptor {}, err
276+ }
277+ req .Header = r .headers
254278
255- return "" , ocispec.Descriptor {}, errors .Wrapf (err , "invalid size header: %q" , sizeHeader )
256- }
257- if size < 0 {
258- return "" , ocispec.Descriptor {}, errors .Errorf ("%q in header not a valid size" , sizeHeader )
279+ resp , err := fetcher .doRequestWithRetries (ctx , req , nil )
280+ if err != nil {
281+ return "" , ocispec.Descriptor {}, err
282+ }
283+ defer resp .Body .Close ()
284+
285+ bodyReader := countingReader {reader : resp .Body }
286+
287+ contentType = getManifestMediaType (resp )
288+ if contentType == images .MediaTypeDockerSchema1Manifest {
289+ b , err := schema1 .ReadStripSignature (& bodyReader )
290+ if err != nil {
291+ return "" , ocispec.Descriptor {}, err
292+ }
293+
294+ dgst = digest .FromBytes (b )
295+ } else {
296+ dgst , err = digest .FromReader (& bodyReader )
297+ if err != nil {
298+ return "" , ocispec.Descriptor {}, err
299+ }
300+ }
301+ size = bodyReader .bytesRead
259302 }
260303
261304 desc := ocispec.Descriptor {
262305 Digest : dgst ,
263- MediaType : resp . Header . Get ( "Content-Type" ), // need to strip disposition?
306+ MediaType : contentType ,
264307 Size : size ,
265308 }
266309
0 commit comments