@@ -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"
@@ -150,6 +151,32 @@ func NewResolver(options ResolverOptions) remotes.Resolver {
150151 }
151152}
152153
154+ func getManifestMediaType (resp * http.Response ) string {
155+ // Strip encoding data (manifests should always be ascii JSON)
156+ contentType := resp .Header .Get ("Content-Type" )
157+ if sp := strings .IndexByte (contentType , ';' ); sp != - 1 {
158+ contentType = contentType [0 :sp ]
159+ }
160+
161+ // As of Apr 30 2019 the registry.access.redhat.com registry does not specify
162+ // the content type of any data but uses schema1 manifests.
163+ if contentType == "text/plain" {
164+ contentType = images .MediaTypeDockerSchema1Manifest
165+ }
166+ return contentType
167+ }
168+
169+ type countingReader struct {
170+ reader io.Reader
171+ bytesRead int64
172+ }
173+
174+ func (r * countingReader ) Read (p []byte ) (int , error ) {
175+ n , err := r .reader .Read (p )
176+ r .bytesRead += int64 (n )
177+ return n , err
178+ }
179+
153180var _ remotes.Resolver = & dockerResolver {}
154181
155182func (r * dockerResolver ) Resolve (ctx context.Context , ref string ) (string , ocispec.Descriptor , error ) {
@@ -220,40 +247,56 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
220247 }
221248 return "" , ocispec.Descriptor {}, errors .Errorf ("unexpected status code %v: %v" , u , resp .Status )
222249 }
250+ size := resp .ContentLength
223251
224252 // this is the only point at which we trust the registry. we use the
225253 // content headers to assemble a descriptor for the name. when this becomes
226254 // more robust, we mostly get this information from a secure trust store.
227255 dgstHeader := digest .Digest (resp .Header .Get ("Docker-Content-Digest" ))
256+ contentType := getManifestMediaType (resp )
228257
229- if dgstHeader != "" {
258+ if dgstHeader != "" && size != - 1 {
230259 if err := dgstHeader .Validate (); err != nil {
231260 return "" , ocispec.Descriptor {}, errors .Wrapf (err , "%q in header not a valid digest" , dgstHeader )
232261 }
233262 dgst = dgstHeader
234- }
235-
236- if dgst == "" {
237- return "" , ocispec.Descriptor {}, errors .Errorf ("could not resolve digest for %v" , ref )
238- }
263+ } else {
264+ log .G (ctx ).Debug ("no Docker-Content-Digest header, fetching manifest instead" )
239265
240- var (
241- size int64
242- sizeHeader = resp .Header .Get ("Content-Length" )
243- )
244-
245- size , err = strconv .ParseInt (sizeHeader , 10 , 64 )
246- if err != nil {
266+ req , err := http .NewRequest (http .MethodGet , u , nil )
267+ if err != nil {
268+ return "" , ocispec.Descriptor {}, err
269+ }
270+ req .Header = r .headers
247271
248- return "" , ocispec.Descriptor {}, errors .Wrapf (err , "invalid size header: %q" , sizeHeader )
249- }
250- if size < 0 {
251- return "" , ocispec.Descriptor {}, errors .Errorf ("%q in header not a valid size" , sizeHeader )
272+ resp , err := fetcher .doRequestWithRetries (ctx , req , nil )
273+ if err != nil {
274+ return "" , ocispec.Descriptor {}, err
275+ }
276+ defer resp .Body .Close ()
277+
278+ bodyReader := countingReader {reader : resp .Body }
279+
280+ contentType = getManifestMediaType (resp )
281+ if contentType == images .MediaTypeDockerSchema1Manifest {
282+ b , err := schema1 .ReadStripSignature (& bodyReader )
283+ if err != nil {
284+ return "" , ocispec.Descriptor {}, err
285+ }
286+
287+ dgst = digest .FromBytes (b )
288+ } else {
289+ dgst , err = digest .FromReader (& bodyReader )
290+ if err != nil {
291+ return "" , ocispec.Descriptor {}, err
292+ }
293+ }
294+ size = bodyReader .bytesRead
252295 }
253296
254297 desc := ocispec.Descriptor {
255298 Digest : dgst ,
256- MediaType : resp . Header . Get ( "Content-Type" ), // need to strip disposition?
299+ MediaType : contentType ,
257300 Size : size ,
258301 }
259302
0 commit comments