|
|
@ -5,6 +5,7 @@ import (
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/json"
|
|
|
|
"net/url"
|
|
|
|
"net/url"
|
|
|
|
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/containerd/containerd/content"
|
|
|
|
"github.com/containerd/containerd/content"
|
|
|
@ -13,6 +14,7 @@ import (
|
|
|
|
"github.com/containerd/containerd/platforms"
|
|
|
|
"github.com/containerd/containerd/platforms"
|
|
|
|
"github.com/containerd/containerd/remotes"
|
|
|
|
"github.com/containerd/containerd/remotes"
|
|
|
|
"github.com/docker/distribution/reference"
|
|
|
|
"github.com/docker/distribution/reference"
|
|
|
|
|
|
|
|
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
|
|
|
"github.com/moby/buildkit/util/contentutil"
|
|
|
|
"github.com/moby/buildkit/util/contentutil"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
|
|
"github.com/opencontainers/image-spec/specs-go"
|
|
|
|
"github.com/opencontainers/image-spec/specs-go"
|
|
|
@ -26,7 +28,7 @@ type Source struct {
|
|
|
|
Ref reference.Named
|
|
|
|
Ref reference.Named
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (r *Resolver) Combine(ctx context.Context, srcs []*Source) ([]byte, ocispec.Descriptor, error) {
|
|
|
|
func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[string]string) ([]byte, ocispec.Descriptor, error) {
|
|
|
|
eg, ctx := errgroup.WithContext(ctx)
|
|
|
|
eg, ctx := errgroup.WithContext(ctx)
|
|
|
|
|
|
|
|
|
|
|
|
dts := make([][]byte, len(srcs))
|
|
|
|
dts := make([][]byte, len(srcs))
|
|
|
@ -75,7 +77,7 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source) ([]byte, ocispec
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// on single source, return original bytes
|
|
|
|
// on single source, return original bytes
|
|
|
|
if len(srcs) == 1 {
|
|
|
|
if len(srcs) == 1 && len(ann) == 0 {
|
|
|
|
if mt := srcs[0].Desc.MediaType; mt == images.MediaTypeDockerSchema2ManifestList || mt == ocispec.MediaTypeImageIndex {
|
|
|
|
if mt := srcs[0].Desc.MediaType; mt == images.MediaTypeDockerSchema2ManifestList || mt == ocispec.MediaTypeImageIndex {
|
|
|
|
return dts[0], srcs[0].Desc, nil
|
|
|
|
return dts[0], srcs[0].Desc, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -138,12 +140,39 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source) ([]byte, ocispec
|
|
|
|
mt = ocispec.MediaTypeImageIndex
|
|
|
|
mt = ocispec.MediaTypeImageIndex
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// annotations are only allowed on OCI indexes
|
|
|
|
|
|
|
|
indexAnnotation := make(map[string]string)
|
|
|
|
|
|
|
|
if mt == ocispec.MediaTypeImageIndex {
|
|
|
|
|
|
|
|
annotations, err := parseAnnotations(ann)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, ocispec.Descriptor{}, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(annotations[exptypes.AnnotationIndex]) > 0 {
|
|
|
|
|
|
|
|
for k, v := range annotations[exptypes.AnnotationIndex] {
|
|
|
|
|
|
|
|
indexAnnotation[k.Key] = v
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(annotations[exptypes.AnnotationManifestDescriptor]) > 0 {
|
|
|
|
|
|
|
|
for i := 0; i < len(newDescs); i++ {
|
|
|
|
|
|
|
|
if newDescs[i].Annotations == nil {
|
|
|
|
|
|
|
|
newDescs[i].Annotations = map[string]string{}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range annotations[exptypes.AnnotationManifestDescriptor] {
|
|
|
|
|
|
|
|
if k.Platform == nil || k.PlatformString() == platforms.Format(*newDescs[i].Platform) {
|
|
|
|
|
|
|
|
newDescs[i].Annotations[k.Key] = v
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
idxBytes, err := json.MarshalIndent(ocispec.Index{
|
|
|
|
idxBytes, err := json.MarshalIndent(ocispec.Index{
|
|
|
|
MediaType: mt,
|
|
|
|
MediaType: mt,
|
|
|
|
Versioned: specs.Versioned{
|
|
|
|
Versioned: specs.Versioned{
|
|
|
|
SchemaVersion: 2,
|
|
|
|
SchemaVersion: 2,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Manifests: newDescs,
|
|
|
|
Manifests: newDescs,
|
|
|
|
|
|
|
|
Annotations: indexAnnotation,
|
|
|
|
}, "", " ")
|
|
|
|
}, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal index")
|
|
|
|
return nil, ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal index")
|
|
|
@ -266,3 +295,52 @@ func detectMediaType(dt []byte) (string, error) {
|
|
|
|
|
|
|
|
|
|
|
|
return images.MediaTypeDockerSchema2ManifestList, nil
|
|
|
|
return images.MediaTypeDockerSchema2ManifestList, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func parseAnnotations(ann map[string]string) (map[string]map[exptypes.AnnotationKey]string, error) {
|
|
|
|
|
|
|
|
// TODO: use buildkit's annotation parser once it supports setting custom prefix and ":" separator
|
|
|
|
|
|
|
|
annotationRegexp := regexp.MustCompile(`^([a-z-]+)(?:\[([A-Za-z0-9_/-]+)\])?:(\S+)$`)
|
|
|
|
|
|
|
|
indexAnnotations := make(map[exptypes.AnnotationKey]string)
|
|
|
|
|
|
|
|
manifestDescriptorAnnotations := make(map[exptypes.AnnotationKey]string)
|
|
|
|
|
|
|
|
for k, v := range ann {
|
|
|
|
|
|
|
|
groups := annotationRegexp.FindStringSubmatch(k)
|
|
|
|
|
|
|
|
if groups == nil {
|
|
|
|
|
|
|
|
return nil, errors.Errorf("invalid annotation format, expected <type>:<key>=<value>, got %q", k)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typ, platform, key := groups[1], groups[2], groups[3]
|
|
|
|
|
|
|
|
var ociPlatform *ocispec.Platform
|
|
|
|
|
|
|
|
if platform != "" {
|
|
|
|
|
|
|
|
p, err := platforms.Parse(platform)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(err, "invalid platform %q", platform)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ociPlatform = &p
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
switch typ {
|
|
|
|
|
|
|
|
case exptypes.AnnotationIndex:
|
|
|
|
|
|
|
|
ak := exptypes.AnnotationKey{
|
|
|
|
|
|
|
|
Type: typ,
|
|
|
|
|
|
|
|
Platform: ociPlatform,
|
|
|
|
|
|
|
|
Key: key,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
indexAnnotations[ak] = v
|
|
|
|
|
|
|
|
case exptypes.AnnotationManifestDescriptor:
|
|
|
|
|
|
|
|
ak := exptypes.AnnotationKey{
|
|
|
|
|
|
|
|
Type: typ,
|
|
|
|
|
|
|
|
Platform: ociPlatform,
|
|
|
|
|
|
|
|
Key: key,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
manifestDescriptorAnnotations[ak] = v
|
|
|
|
|
|
|
|
case exptypes.AnnotationManifest:
|
|
|
|
|
|
|
|
return nil, errors.Errorf("%q annotations are not supported yet", typ)
|
|
|
|
|
|
|
|
case exptypes.AnnotationIndexDescriptor:
|
|
|
|
|
|
|
|
return nil, errors.Errorf("%q annotations are invalid while creating an image", typ)
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
return nil, errors.Errorf("unknown annotation type %q", typ)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return map[string]map[exptypes.AnnotationKey]string{
|
|
|
|
|
|
|
|
exptypes.AnnotationIndex: indexAnnotations,
|
|
|
|
|
|
|
|
exptypes.AnnotationManifestDescriptor: manifestDescriptorAnnotations,
|
|
|
|
|
|
|
|
}, nil
|
|
|
|
|
|
|
|
}
|
|
|
|