mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-29 17:05:46 +08:00
Merge pull request #2020 from jedevc/build-annotation-flags
This commit is contained in:
commit
66e6dab26b
17
bake/bake.go
17
bake/bake.go
@ -589,6 +589,7 @@ type Target struct {
|
|||||||
// Inherits is the only field that cannot be overridden with --set
|
// Inherits is the only field that cannot be overridden with --set
|
||||||
Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional" cty:"inherits"`
|
Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional" cty:"inherits"`
|
||||||
|
|
||||||
|
Annotations []string `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
|
||||||
Attest []string `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
|
Attest []string `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
|
||||||
Context *string `json:"context,omitempty" hcl:"context,optional" cty:"context"`
|
Context *string `json:"context,omitempty" hcl:"context,optional" cty:"context"`
|
||||||
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
|
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
|
||||||
@ -620,6 +621,7 @@ var _ hclparser.WithEvalContexts = &Group{}
|
|||||||
var _ hclparser.WithGetName = &Group{}
|
var _ hclparser.WithGetName = &Group{}
|
||||||
|
|
||||||
func (t *Target) normalize() {
|
func (t *Target) normalize() {
|
||||||
|
t.Annotations = removeDupes(t.Annotations)
|
||||||
t.Attest = removeAttestDupes(t.Attest)
|
t.Attest = removeAttestDupes(t.Attest)
|
||||||
t.Tags = removeDupes(t.Tags)
|
t.Tags = removeDupes(t.Tags)
|
||||||
t.Secrets = removeDupes(t.Secrets)
|
t.Secrets = removeDupes(t.Secrets)
|
||||||
@ -680,6 +682,9 @@ func (t *Target) Merge(t2 *Target) {
|
|||||||
if t2.Target != nil {
|
if t2.Target != nil {
|
||||||
t.Target = t2.Target
|
t.Target = t2.Target
|
||||||
}
|
}
|
||||||
|
if t2.Annotations != nil { // merge
|
||||||
|
t.Annotations = append(t.Annotations, t2.Annotations...)
|
||||||
|
}
|
||||||
if t2.Attest != nil { // merge
|
if t2.Attest != nil { // merge
|
||||||
t.Attest = append(t.Attest, t2.Attest...)
|
t.Attest = append(t.Attest, t2.Attest...)
|
||||||
t.Attest = removeAttestDupes(t.Attest)
|
t.Attest = removeAttestDupes(t.Attest)
|
||||||
@ -766,6 +771,8 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
|||||||
t.Platforms = o.ArrValue
|
t.Platforms = o.ArrValue
|
||||||
case "output":
|
case "output":
|
||||||
t.Outputs = o.ArrValue
|
t.Outputs = o.ArrValue
|
||||||
|
case "annotations":
|
||||||
|
t.Annotations = append(t.Annotations, o.ArrValue...)
|
||||||
case "attest":
|
case "attest":
|
||||||
t.Attest = append(t.Attest, o.ArrValue...)
|
t.Attest = append(t.Attest, o.ArrValue...)
|
||||||
case "no-cache":
|
case "no-cache":
|
||||||
@ -1164,6 +1171,16 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
annotations, err := buildflags.ParseAnnotations(t.Annotations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, e := range bo.Exports {
|
||||||
|
for k, v := range annotations {
|
||||||
|
e.Attrs[k.String()] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
attests, err := buildflags.ParseAttests(t.Attest)
|
attests, err := buildflags.ParseAttests(t.Attest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1460,3 +1460,31 @@ func TestAttestDuplicates(t *testing.T) {
|
|||||||
"provenance": ptrstr("type=provenance,mode=max"),
|
"provenance": ptrstr("type=provenance,mode=max"),
|
||||||
}, opts["default"].Attests)
|
}, opts["default"].Attests)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnnotations(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=image,name=foo"]
|
||||||
|
annotations = ["manifest[linux/amd64]:foo=bar"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
ctx := context.TODO()
|
||||||
|
m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bo, err := TargetsToBuildOpt(m, &Input{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(g))
|
||||||
|
require.Equal(t, []string{"app"}, g["default"].Targets)
|
||||||
|
|
||||||
|
require.Equal(t, 1, len(m))
|
||||||
|
require.Contains(t, m, "app")
|
||||||
|
require.Equal(t, "type=image,name=foo", m["app"].Outputs[0])
|
||||||
|
require.Equal(t, "manifest[linux/amd64]:foo=bar", m["app"].Annotations[0])
|
||||||
|
|
||||||
|
require.Len(t, bo["app"].Exports, 1)
|
||||||
|
require.Equal(t, "bar", bo["app"].Exports[0].Attrs["annotation-manifest[linux/amd64].foo"])
|
||||||
|
}
|
||||||
|
@ -54,6 +54,7 @@ import (
|
|||||||
|
|
||||||
type buildOptions struct {
|
type buildOptions struct {
|
||||||
allow []string
|
allow []string
|
||||||
|
annotations []string
|
||||||
buildArgs []string
|
buildArgs []string
|
||||||
cacheFrom []string
|
cacheFrom []string
|
||||||
cacheTo []string
|
cacheTo []string
|
||||||
@ -159,6 +160,16 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
annotations, err := buildflags.ParseAnnotations(o.annotations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, e := range opts.Exports {
|
||||||
|
for k, v := range annotations {
|
||||||
|
e.Attrs[k.String()] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opts.CacheFrom, err = buildflags.ParseCacheEntry(o.cacheFrom)
|
opts.CacheFrom, err = buildflags.ParseCacheEntry(o.cacheFrom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -458,6 +469,8 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
|
|
||||||
flags.StringSliceVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
|
flags.StringSliceVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
|
||||||
|
|
||||||
|
flags.StringArrayVarP(&options.annotations, "annotation", "", []string{}, "Add annotation to the image")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.buildArgs, "build-arg", []string{}, "Set build-time variables")
|
flags.StringArrayVar(&options.buildArgs, "build-arg", []string{}, "Set build-time variables")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.cacheFrom, "cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`)
|
flags.StringArrayVar(&options.cacheFrom, "cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`)
|
||||||
|
@ -83,11 +83,6 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
|
|||||||
return errors.Errorf("no repositories specified, please set a reference in tag or source")
|
return errors.Errorf("no repositories specified, please set a reference in tag or source")
|
||||||
}
|
}
|
||||||
|
|
||||||
ann, err := parseAnnotations(in.annotations)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultRepo *string
|
var defaultRepo *string
|
||||||
if len(repos) == 1 {
|
if len(repos) == 1 {
|
||||||
for repo := range repos {
|
for repo := range repos {
|
||||||
@ -160,7 +155,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dt, desc, err := r.Combine(ctx, srcs, ann)
|
dt, desc, err := r.Combine(ctx, srcs, in.annotations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -270,18 +265,6 @@ func parseSource(in string) (*imagetools.Source, error) {
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAnnotations(in []string) (map[string]string, error) {
|
|
||||||
out := make(map[string]string)
|
|
||||||
for _, i := range in {
|
|
||||||
kv := strings.SplitN(i, "=", 2)
|
|
||||||
if len(kv) != 2 {
|
|
||||||
return nil, errors.Errorf("invalid annotation %q, expected key=value", in)
|
|
||||||
}
|
|
||||||
out[kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
|
func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
|
||||||
var options createOptions
|
var options createOptions
|
||||||
|
|
||||||
|
@ -115,6 +115,7 @@ The following table shows the complete list of attributes that you can assign to
|
|||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ----------------------------------------------- | ------- | -------------------------------------------------------------------- |
|
| ----------------------------------------------- | ------- | -------------------------------------------------------------------- |
|
||||||
| [`args`](#targetargs) | Map | Build arguments |
|
| [`args`](#targetargs) | Map | Build arguments |
|
||||||
|
| [`annotations`](#targetannotations) | List | Exporter annotations |
|
||||||
| [`attest`](#targetattest) | List | Build attestations |
|
| [`attest`](#targetattest) | List | Build attestations |
|
||||||
| [`cache-from`](#targetcache-from) | List | External cache sources |
|
| [`cache-from`](#targetcache-from) | List | External cache sources |
|
||||||
| [`cache-to`](#targetcache-to) | List | External cache destinations |
|
| [`cache-to`](#targetcache-to) | List | External cache destinations |
|
||||||
@ -171,6 +172,26 @@ target "db" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `target.annotations`
|
||||||
|
|
||||||
|
The `annotations` attribute is a shortcut to allow you to easily set a list of
|
||||||
|
annotations on the target.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
target "default" {
|
||||||
|
output = ["type=image,name=foo"]
|
||||||
|
annotations = ["key=value"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
is the same as
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
target "default" {
|
||||||
|
output = ["type=image,name=foo,annotation.key=value"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `target.attest`
|
### `target.attest`
|
||||||
|
|
||||||
The `attest` attribute lets you apply [build attestations][attestations] to the target.
|
The `attest` attribute lets you apply [build attestations][attestations] to the target.
|
||||||
|
@ -17,6 +17,7 @@ Start a build
|
|||||||
|:-------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------|:----------|:----------------------------------------------------------------------------------------------------|
|
|:-------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------|:----------|:----------------------------------------------------------------------------------------------------|
|
||||||
| [`--add-host`](https://docs.docker.com/engine/reference/commandline/build/#add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
|
| [`--add-host`](https://docs.docker.com/engine/reference/commandline/build/#add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
|
||||||
| [`--allow`](#allow) | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
|
| [`--allow`](#allow) | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
|
||||||
|
| `--annotation` | `stringArray` | | Add annotation to the image |
|
||||||
| [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
| [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
||||||
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
|
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
|
||||||
| [`--build-context`](#build-context) | `stringArray` | | Additional build contexts (e.g., name=path) |
|
| [`--build-context`](#build-context) | `stringArray` | | Additional build contexts (e.g., name=path) |
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/moby/buildkit/util/testutil/integration"
|
"github.com/moby/buildkit/util/testutil/integration"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
|
|||||||
testBuildMobyFromLocalImage,
|
testBuildMobyFromLocalImage,
|
||||||
testBuildDetailsLink,
|
testBuildDetailsLink,
|
||||||
testBuildProgress,
|
testBuildProgress,
|
||||||
|
testBuildAnnotations,
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBuild(t *testing.T, sb integration.Sandbox) {
|
func testBuild(t *testing.T, sb integration.Sandbox) {
|
||||||
@ -313,3 +315,46 @@ func testBuildProgress(t *testing.T, sb integration.Sandbox) {
|
|||||||
require.Contains(t, string(plainOutput), "[internal] load build definition from Dockerfile")
|
require.Contains(t, string(plainOutput), "[internal] load build definition from Dockerfile")
|
||||||
require.Contains(t, string(plainOutput), "[base 1/3] FROM docker.io/library/busybox:latest")
|
require.Contains(t, string(plainOutput), "[base 1/3] FROM docker.io/library/busybox:latest")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testBuildAnnotations(t *testing.T, sb integration.Sandbox) {
|
||||||
|
if sb.Name() == "docker" {
|
||||||
|
t.Skip("annotations not supported on docker worker")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := createTestProject(t)
|
||||||
|
|
||||||
|
registry, err := sb.NewRegistry()
|
||||||
|
if errors.Is(err, integration.ErrRequirements) {
|
||||||
|
t.Skip(err.Error())
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
target := registry + "/buildx/registry:latest"
|
||||||
|
|
||||||
|
annotations := []string{
|
||||||
|
"--annotation", "example1=www",
|
||||||
|
"--annotation", "index:example2=xxx",
|
||||||
|
"--annotation", "manifest:example3=yyy",
|
||||||
|
"--annotation", "manifest-descriptor[" + platforms.DefaultString() + "]:example4=zzz",
|
||||||
|
}
|
||||||
|
out, err := buildCmd(sb, withArgs(annotations...), withArgs(fmt.Sprintf("--output=type=image,name=%s,push=true", target), dir))
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
|
||||||
|
desc, provider, err := contentutil.ProviderFromRef(target)
|
||||||
|
require.NoError(t, err)
|
||||||
|
imgs, err := testutil.ReadImages(sb.Context(), provider, desc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pk := platforms.Format(platforms.Normalize(platforms.DefaultSpec()))
|
||||||
|
img := imgs.Find(pk)
|
||||||
|
require.NotNil(t, img)
|
||||||
|
|
||||||
|
require.NotNil(t, imgs.Index)
|
||||||
|
assert.Equal(t, "xxx", imgs.Index.Annotations["example2"])
|
||||||
|
|
||||||
|
require.NotNil(t, img.Manifest)
|
||||||
|
assert.Equal(t, "www", img.Manifest.Annotations["example1"])
|
||||||
|
assert.Equal(t, "yyy", img.Manifest.Annotations["example3"])
|
||||||
|
|
||||||
|
require.NotNil(t, img.Desc)
|
||||||
|
assert.Equal(t, "zzz", img.Desc.Annotations["example4"])
|
||||||
|
}
|
||||||
|
@ -2,10 +2,14 @@ package buildflags
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
controllerapi "github.com/docker/buildx/controller/pb"
|
controllerapi "github.com/docker/buildx/controller/pb"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
|
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||||
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,3 +78,45 @@ func ParseExports(inp []string) ([]*controllerapi.ExportEntry, error) {
|
|||||||
}
|
}
|
||||||
return outs, nil
|
return outs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseAnnotations(inp []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+)$`)
|
||||||
|
annotations := make(map[exptypes.AnnotationKey]string)
|
||||||
|
for _, inp := range inp {
|
||||||
|
k, v, ok := strings.Cut(inp, "=")
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("invalid annotation %q, expected key=value", inp)
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := annotationRegexp.FindStringSubmatch(k)
|
||||||
|
if groups == nil {
|
||||||
|
return nil, errors.Errorf("invalid annotation format, expected <type>:<key>=<value>, got %q", inp)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ, platform, key := groups[1], groups[2], groups[3]
|
||||||
|
switch typ {
|
||||||
|
case "":
|
||||||
|
case exptypes.AnnotationIndex, exptypes.AnnotationIndexDescriptor, exptypes.AnnotationManifest, exptypes.AnnotationManifestDescriptor:
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("unknown annotation type %q", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ociPlatform *ocispecs.Platform
|
||||||
|
if platform != "" {
|
||||||
|
p, err := platforms.Parse(platform)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "invalid platform %q", platform)
|
||||||
|
}
|
||||||
|
ociPlatform = &p
|
||||||
|
}
|
||||||
|
|
||||||
|
ak := exptypes.AnnotationKey{
|
||||||
|
Type: typ,
|
||||||
|
Platform: ociPlatform,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
annotations[ak] = v
|
||||||
|
}
|
||||||
|
return annotations, nil
|
||||||
|
}
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
"github.com/containerd/containerd/remotes"
|
"github.com/containerd/containerd/remotes"
|
||||||
"github.com/distribution/reference"
|
"github.com/distribution/reference"
|
||||||
|
"github.com/docker/buildx/util/buildflags"
|
||||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
"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"
|
||||||
@ -28,7 +28,7 @@ type Source struct {
|
|||||||
Ref reference.Named
|
Ref reference.Named
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[string]string) ([]byte, ocispec.Descriptor, error) {
|
func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann []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))
|
||||||
@ -143,25 +143,27 @@ func (r *Resolver) Combine(ctx context.Context, srcs []*Source, ann map[string]s
|
|||||||
// annotations are only allowed on OCI indexes
|
// annotations are only allowed on OCI indexes
|
||||||
indexAnnotation := make(map[string]string)
|
indexAnnotation := make(map[string]string)
|
||||||
if mt == ocispec.MediaTypeImageIndex {
|
if mt == ocispec.MediaTypeImageIndex {
|
||||||
annotations, err := parseAnnotations(ann)
|
annotations, err := buildflags.ParseAnnotations(ann)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ocispec.Descriptor{}, err
|
return nil, ocispec.Descriptor{}, err
|
||||||
}
|
}
|
||||||
if len(annotations[exptypes.AnnotationIndex]) > 0 {
|
for k, v := range annotations {
|
||||||
for k, v := range annotations[exptypes.AnnotationIndex] {
|
switch k.Type {
|
||||||
|
case exptypes.AnnotationIndex:
|
||||||
indexAnnotation[k.Key] = v
|
indexAnnotation[k.Key] = v
|
||||||
}
|
case exptypes.AnnotationManifestDescriptor:
|
||||||
}
|
for i := 0; i < len(newDescs); i++ {
|
||||||
if len(annotations[exptypes.AnnotationManifestDescriptor]) > 0 {
|
if newDescs[i].Annotations == nil {
|
||||||
for i := 0; i < len(newDescs); i++ {
|
newDescs[i].Annotations = map[string]string{}
|
||||||
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) {
|
if k.Platform == nil || k.PlatformString() == platforms.Format(*newDescs[i].Platform) {
|
||||||
newDescs[i].Annotations[k.Key] = v
|
newDescs[i].Annotations[k.Key] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case exptypes.AnnotationManifest, "":
|
||||||
|
return nil, ocispec.Descriptor{}, errors.Errorf("%q annotations are not supported yet", k.Type)
|
||||||
|
case exptypes.AnnotationIndexDescriptor:
|
||||||
|
return nil, ocispec.Descriptor{}, errors.Errorf("%q annotations are invalid while creating an image", k.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,52 +297,3 @@ 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
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user