github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/remotes/docker/converter.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package docker
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  
    25  	"github.com/containerd/containerd/content"
    26  	"github.com/containerd/containerd/images"
    27  	"github.com/containerd/containerd/log"
    28  	"github.com/containerd/containerd/remotes"
    29  	digest "github.com/opencontainers/go-digest"
    30  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    31  	"github.com/pkg/errors"
    32  )
    33  
    34  // LegacyConfigMediaType should be replaced by OCI image spec.
    35  //
    36  // More detail: docker/distribution#1622
    37  const LegacyConfigMediaType = "application/octet-stream"
    38  
    39  // ConvertManifest changes application/octet-stream to schema2 config media type if need.
    40  //
    41  // NOTE:
    42  // 1. original manifest will be deleted by next gc round.
    43  // 2. don't cover manifest list.
    44  func ConvertManifest(ctx context.Context, store content.Store, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
    45  	if !(desc.MediaType == images.MediaTypeDockerSchema2Manifest ||
    46  		desc.MediaType == ocispec.MediaTypeImageManifest) {
    47  
    48  		log.G(ctx).Warnf("do nothing for media type: %s", desc.MediaType)
    49  		return desc, nil
    50  	}
    51  
    52  	// read manifest data
    53  	mb, err := content.ReadBlob(ctx, store, desc)
    54  	if err != nil {
    55  		return ocispec.Descriptor{}, errors.Wrap(err, "failed to read index data")
    56  	}
    57  
    58  	var manifest ocispec.Manifest
    59  	if err := json.Unmarshal(mb, &manifest); err != nil {
    60  		return ocispec.Descriptor{}, errors.Wrap(err, "failed to unmarshal data into manifest")
    61  	}
    62  
    63  	// check config media type
    64  	if manifest.Config.MediaType != LegacyConfigMediaType {
    65  		return desc, nil
    66  	}
    67  
    68  	manifest.Config.MediaType = images.MediaTypeDockerSchema2Config
    69  	data, err := json.MarshalIndent(manifest, "", "   ")
    70  	if err != nil {
    71  		return ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal manifest")
    72  	}
    73  
    74  	// update manifest with gc labels
    75  	desc.Digest = digest.Canonical.FromBytes(data)
    76  	desc.Size = int64(len(data))
    77  
    78  	labels := map[string]string{}
    79  	for i, c := range append([]ocispec.Descriptor{manifest.Config}, manifest.Layers...) {
    80  		labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = c.Digest.String()
    81  	}
    82  
    83  	ref := remotes.MakeRefKey(ctx, desc)
    84  	if err := content.WriteBlob(ctx, store, ref, bytes.NewReader(data), desc, content.WithLabels(labels)); err != nil {
    85  		return ocispec.Descriptor{}, errors.Wrap(err, "failed to update content")
    86  	}
    87  	return desc, nil
    88  }