Merge pull request #2814 from fuweid/support_legacy_media_type

bugfix: support application/octet-stream during pull
This commit is contained in:
Phil Estes 2019-01-14 17:29:14 -05:00 committed by GitHub
commit c171b615ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 120 additions and 6 deletions

View File

@ -403,12 +403,22 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
} }
var ( var (
schema1Converter *schema1.Converter handler images.Handler
handler images.Handler
isConvertible bool
converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error)
) )
if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 { if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 {
schema1Converter = schema1.NewConverter(store, fetcher) schema1Converter := schema1.NewConverter(store, fetcher)
handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...) handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...)
isConvertible = true
converterFunc = func(ctx context.Context, _ ocispec.Descriptor) (ocispec.Descriptor, error) {
return schema1Converter.Convert(ctx)
}
} else { } else {
// Get all the children for a descriptor // Get all the children for a descriptor
childrenHandler := images.ChildrenHandler(store) childrenHandler := images.ChildrenHandler(store)
@ -421,18 +431,34 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit) childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit)
} }
// set isConvertible to true if there is application/octet-stream media type
convertibleHandler := images.HandlerFunc(
func(_ context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.MediaType == docker.LegacyConfigMediaType {
isConvertible = true
}
return []ocispec.Descriptor{}, nil
},
)
handler = images.Handlers(append(rCtx.BaseHandlers, handler = images.Handlers(append(rCtx.BaseHandlers,
remotes.FetchHandler(store, fetcher), remotes.FetchHandler(store, fetcher),
convertibleHandler,
childrenHandler, childrenHandler,
)...) )...)
converterFunc = func(ctx context.Context, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
return docker.ConvertManifest(ctx, store, desc)
}
} }
if err := images.Dispatch(ctx, handler, desc); err != nil { if err := images.Dispatch(ctx, handler, desc); err != nil {
return images.Image{}, err return images.Image{}, err
} }
if schema1Converter != nil {
desc, err = schema1Converter.Convert(ctx) if isConvertible {
if err != nil { if desc, err = converterFunc(ctx, desc); err != nil {
return images.Image{}, err return images.Image{}, err
} }
} }

View File

@ -0,0 +1,88 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package docker
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/remotes"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// LegacyConfigMediaType should be replaced by OCI image spec.
//
// More detail: docker/distribution#1622
const LegacyConfigMediaType = "application/octet-stream"
// ConvertManifest changes application/octet-stream to schema2 config media type if need.
//
// NOTE:
// 1. original manifest will be deleted by next gc round.
// 2. don't cover manifest list.
func ConvertManifest(ctx context.Context, store content.Store, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
if !(desc.MediaType == images.MediaTypeDockerSchema2Manifest ||
desc.MediaType == ocispec.MediaTypeImageManifest) {
log.G(ctx).Warnf("do nothing for media type: %s", desc.MediaType)
return desc, nil
}
// read manifest data
mb, err := content.ReadBlob(ctx, store, desc)
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to read index data")
}
var manifest ocispec.Manifest
if err := json.Unmarshal(mb, &manifest); err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to unmarshal data into manifest")
}
// check config media type
if manifest.Config.MediaType != LegacyConfigMediaType {
return desc, nil
}
manifest.Config.MediaType = images.MediaTypeDockerSchema2Config
data, err := json.MarshalIndent(manifest, "", " ")
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal manifest")
}
// update manifest with gc labels
desc.Digest = digest.Canonical.FromBytes(data)
desc.Size = int64(len(data))
labels := map[string]string{}
for i, c := range append([]ocispec.Descriptor{manifest.Config}, manifest.Layers...) {
labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = c.Digest.String()
}
ref := remotes.MakeRefKey(ctx, desc)
if err := content.WriteBlob(ctx, store, ref, bytes.NewReader(data), desc, content.WithLabels(labels)); err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to update content")
}
return desc, nil
}