mirror of https://github.com/docker/buildx.git
Make multi-type annotation settings match docs
The Docker docs in multiple places describe passing an annotation at the command line like "index,manifest:com.example.name=my-cool-image", and say that this will result in the annotation being applied to both the index and the manifest. It doesn't seem like this was actually implemented, and instead it just results in an annotation key with "index,manifest:" at the beginning being applied to the manifest. This change splits the part of the key before the colon by comma, and creates an annotation for each type/platform given, so the implementation should now match the docs. Signed-off-by: Eli Treuherz <et@arenko.group>
This commit is contained in:
parent
ab835fd904
commit
b00001d8ac
2
go.mod
2
go.mod
|
@ -21,6 +21,7 @@ require (
|
|||
github.com/gofrs/flock v0.8.1
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20230405223818-a090f58aa992
|
||||
|
@ -98,7 +99,6 @@ require (
|
|||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
|
|
|
@ -81,7 +81,8 @@ func ParseExports(inp []string) ([]*controllerapi.ExportEntry, error) {
|
|||
|
||||
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+)$`)
|
||||
annotationRegexp := regexp.MustCompile(`^((?:[a-z-]+(?:\[[A-Za-z0-9_/-]+\])?)(?:,[a-z-]+(?:\[[A-Za-z0-9_/-]+\])?)*:)?(\S+)$`)
|
||||
annotationTypeRegexp := regexp.MustCompile(`^([a-z-]+)(?:\[([A-Za-z0-9_/-]+)\])?$`)
|
||||
annotations := make(map[exptypes.AnnotationKey]string)
|
||||
for _, inp := range inp {
|
||||
k, v, ok := strings.Cut(inp, "=")
|
||||
|
@ -94,7 +95,19 @@ func ParseAnnotations(inp []string) (map[exptypes.AnnotationKey]string, error) {
|
|||
return nil, errors.Errorf("invalid annotation format, expected <type>:<key>=<value>, got %q", inp)
|
||||
}
|
||||
|
||||
typ, platform, key := groups[1], groups[2], groups[3]
|
||||
types, key := groups[1], groups[2]
|
||||
|
||||
if types == "" {
|
||||
ak := exptypes.AnnotationKey{Key: key}
|
||||
annotations[ak] = v
|
||||
continue
|
||||
}
|
||||
|
||||
typesSplit := strings.Split(strings.TrimSuffix(types, ":"), ",")
|
||||
for _, typeAndPlatform := range typesSplit {
|
||||
groups := annotationTypeRegexp.FindStringSubmatch(typeAndPlatform)
|
||||
typ, platform := groups[1], groups[2]
|
||||
|
||||
switch typ {
|
||||
case "":
|
||||
case exptypes.AnnotationIndex, exptypes.AnnotationIndexDescriptor, exptypes.AnnotationManifest, exptypes.AnnotationManifestDescriptor:
|
||||
|
@ -118,5 +131,7 @@ func ParseAnnotations(inp []string) (map[exptypes.AnnotationKey]string, error) {
|
|||
}
|
||||
annotations[ak] = v
|
||||
}
|
||||
|
||||
}
|
||||
return annotations, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
package buildflags
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
gocmp "github.com/google/go-cmp/cmp"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseAnnotations(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in []string
|
||||
want map[exptypes.AnnotationKey]string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
in: []string{"a=b"},
|
||||
want: map[exptypes.AnnotationKey]string{
|
||||
{Key: "a"}: "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reverse-DNS key",
|
||||
in: []string{"com.example=a"},
|
||||
want: map[exptypes.AnnotationKey]string{
|
||||
{Key: "com.example"}: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "specify type",
|
||||
in: []string{"manifest:com.example=a"},
|
||||
want: map[exptypes.AnnotationKey]string{
|
||||
{Type: "manifest", Key: "com.example"}: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "specify bad type",
|
||||
in: []string{"bad:com.example=a"},
|
||||
wantErr: "unknown annotation type",
|
||||
},
|
||||
{
|
||||
name: "specify type and platform",
|
||||
in: []string{"manifest[plat/form]:com.example=a"},
|
||||
want: map[exptypes.AnnotationKey]string{
|
||||
{
|
||||
Type: "manifest",
|
||||
Platform: &ocispecs.Platform{
|
||||
OS: "plat",
|
||||
Architecture: "form",
|
||||
},
|
||||
Key: "com.example",
|
||||
}: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "specify multiple types",
|
||||
in: []string{"index,manifest:com.example=a"},
|
||||
want: map[exptypes.AnnotationKey]string{
|
||||
{Type: "index", Key: "com.example"}: "a",
|
||||
{Type: "manifest", Key: "com.example"}: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "specify multiple types and platform",
|
||||
in: []string{"index,manifest[plat/form]:com.example=a"},
|
||||
want: map[exptypes.AnnotationKey]string{
|
||||
{Type: "index", Key: "com.example"}: "a",
|
||||
{
|
||||
Type: "manifest",
|
||||
Platform: &ocispecs.Platform{
|
||||
OS: "plat",
|
||||
Architecture: "form",
|
||||
},
|
||||
Key: "com.example",
|
||||
}: "a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, gotErr := ParseAnnotations(test.in)
|
||||
if test.wantErr != "" {
|
||||
require.ErrorContains(t, gotErr, test.wantErr)
|
||||
} else {
|
||||
assert.NoError(t, gotErr)
|
||||
}
|
||||
|
||||
// Can't compare maps with pointer in their keys, need to extract and sort the map entries
|
||||
type kv struct {
|
||||
Key exptypes.AnnotationKey
|
||||
Val string
|
||||
}
|
||||
var wantKVs, gotKVs []kv
|
||||
for k, v := range test.want {
|
||||
wantKVs = append(wantKVs, kv{k, v})
|
||||
}
|
||||
for k, v := range got {
|
||||
gotKVs = append(gotKVs, kv{k, v})
|
||||
}
|
||||
|
||||
sortFunc := func(a, b kv) int { return cmp.Compare(a.Key.String(), b.Key.String()) }
|
||||
slices.SortFunc(wantKVs, sortFunc)
|
||||
slices.SortFunc(gotKVs, sortFunc)
|
||||
|
||||
if diff := gocmp.Diff(wantKVs, gotKVs); diff != "" {
|
||||
t.Error(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue