Skip to content

Commit 0d6d883

Browse files
msg555thaJeztah
authored andcommitted
Compute manifest metadata when not provided.
This closes #3238 Signed-off-by: msg555 <[email protected]> (cherry picked from commit ee902af) Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent e6275a0 commit 0d6d883

2 files changed

Lines changed: 74 additions & 25 deletions

File tree

remotes/docker/resolver.go

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@ package docker
1818

1919
import (
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+
160187
var _ remotes.Resolver = &dockerResolver{}
161188

162189
func (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

remotes/docker/schema1/converter.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,17 @@ func (c *Converter) Convert(ctx context.Context, opts ...ConvertOpt) (ocispec.De
227227
return desc, nil
228228
}
229229

230+
// ReadStripSignature reads in a schema1 manifest and returns a byte array
231+
// with the "signatures" field stripped
232+
func ReadStripSignature(schema1Blob io.Reader) ([]byte, error) {
233+
b, err := ioutil.ReadAll(io.LimitReader(schema1Blob, manifestSizeLimit)) // limit to 8MB
234+
if err != nil {
235+
return nil, err
236+
}
237+
238+
return stripSignature(b)
239+
}
240+
230241
func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor) error {
231242
log.G(ctx).Debug("fetch schema 1")
232243

@@ -235,17 +246,12 @@ func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor)
235246
return err
236247
}
237248

238-
b, err := ioutil.ReadAll(io.LimitReader(rc, manifestSizeLimit)) // limit to 8MB
249+
b, err := ReadStripSignature(rc)
239250
rc.Close()
240251
if err != nil {
241252
return err
242253
}
243254

244-
b, err = stripSignature(b)
245-
if err != nil {
246-
return err
247-
}
248-
249255
var m manifest
250256
if err := json.Unmarshal(b, &m); err != nil {
251257
return err

0 commit comments

Comments
 (0)