mirror of
https://github.com/ko-build/ko.git
synced 2025-07-12 23:50:31 +02:00
allow setting annotations
This adds a new build flag for setting annotations on image indexes and manifests. Annotations are no longer copied from the base image. `org.opencontainers.image.base.digest` and `org.opencontainers.image.base.name` are always set to the resolved values. Usage example: ```sh ko build --image-annotation foo=bar,fizz=buzz . ``` Fixes #1090 Fixes #1090 Fixes #1231 Fixes #1235 Fixes #1395
This commit is contained in:
@ -51,7 +51,8 @@ ko apply -f FILENAME [flags]
|
|||||||
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
||||||
-f, --filename strings Filename, directory, or URL to files to use to create the resource
|
-f, --filename strings Filename, directory, or URL to files to use to create the resource
|
||||||
-h, --help help for apply
|
-h, --help help for apply
|
||||||
--image-label strings Which labels (key=value) to add to the image.
|
--image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest.
|
||||||
|
--image-label strings Which labels (key=value[,key=value]) to add to the image.
|
||||||
--image-refs string Path to file where a list of the published image references will be written.
|
--image-refs string Path to file where a list of the published image references will be written.
|
||||||
--insecure-registry Whether to skip TLS verification on the registry
|
--insecure-registry Whether to skip TLS verification on the registry
|
||||||
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
||||||
|
@ -47,7 +47,8 @@ ko build IMPORTPATH... [flags]
|
|||||||
--debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.
|
--debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.
|
||||||
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
||||||
-h, --help help for build
|
-h, --help help for build
|
||||||
--image-label strings Which labels (key=value) to add to the image.
|
--image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest.
|
||||||
|
--image-label strings Which labels (key=value[,key=value]) to add to the image.
|
||||||
--image-refs string Path to file where a list of the published image references will be written.
|
--image-refs string Path to file where a list of the published image references will be written.
|
||||||
--insecure-registry Whether to skip TLS verification on the registry
|
--insecure-registry Whether to skip TLS verification on the registry
|
||||||
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
||||||
|
@ -51,7 +51,8 @@ ko create -f FILENAME [flags]
|
|||||||
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
||||||
-f, --filename strings Filename, directory, or URL to files to use to create the resource
|
-f, --filename strings Filename, directory, or URL to files to use to create the resource
|
||||||
-h, --help help for create
|
-h, --help help for create
|
||||||
--image-label strings Which labels (key=value) to add to the image.
|
--image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest.
|
||||||
|
--image-label strings Which labels (key=value[,key=value]) to add to the image.
|
||||||
--image-refs string Path to file where a list of the published image references will be written.
|
--image-refs string Path to file where a list of the published image references will be written.
|
||||||
--insecure-registry Whether to skip TLS verification on the registry
|
--insecure-registry Whether to skip TLS verification on the registry
|
||||||
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
||||||
|
@ -44,7 +44,8 @@ ko resolve -f FILENAME [flags]
|
|||||||
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
||||||
-f, --filename strings Filename, directory, or URL to files to use to create the resource
|
-f, --filename strings Filename, directory, or URL to files to use to create the resource
|
||||||
-h, --help help for resolve
|
-h, --help help for resolve
|
||||||
--image-label strings Which labels (key=value) to add to the image.
|
--image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest.
|
||||||
|
--image-label strings Which labels (key=value[,key=value]) to add to the image.
|
||||||
--image-refs string Path to file where a list of the published image references will be written.
|
--image-refs string Path to file where a list of the published image references will be written.
|
||||||
--insecure-registry Whether to skip TLS verification on the registry
|
--insecure-registry Whether to skip TLS verification on the registry
|
||||||
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
||||||
|
@ -35,7 +35,8 @@ ko run IMPORTPATH [flags]
|
|||||||
--debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.
|
--debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.
|
||||||
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
--disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
|
||||||
-h, --help help for run
|
-h, --help help for run
|
||||||
--image-label strings Which labels (key=value) to add to the image.
|
--image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest.
|
||||||
|
--image-label strings Which labels (key=value[,key=value]) to add to the image.
|
||||||
--image-refs string Path to file where a list of the published image references will be written.
|
--image-refs string Path to file where a list of the published image references will be written.
|
||||||
--insecure-registry Whether to skip TLS verification on the registry
|
--insecure-registry Whether to skip TLS verification on the registry
|
||||||
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
-j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
gb "go/build"
|
gb "go/build"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@ -101,6 +102,7 @@ type gobuild struct {
|
|||||||
platformMatcher *platformMatcher
|
platformMatcher *platformMatcher
|
||||||
dir string
|
dir string
|
||||||
labels map[string]string
|
labels map[string]string
|
||||||
|
annotations map[string]string
|
||||||
debug bool
|
debug bool
|
||||||
semaphore *semaphore.Weighted
|
semaphore *semaphore.Weighted
|
||||||
|
|
||||||
@ -126,6 +128,7 @@ type gobuildOpener struct {
|
|||||||
defaultLdflags []string
|
defaultLdflags []string
|
||||||
platforms []string
|
platforms []string
|
||||||
labels map[string]string
|
labels map[string]string
|
||||||
|
annotations map[string]string
|
||||||
dir string
|
dir string
|
||||||
jobs int
|
jobs int
|
||||||
debug bool
|
debug bool
|
||||||
@ -142,6 +145,9 @@ func (gbo *gobuildOpener) Open() (Interface, error) {
|
|||||||
if gbo.jobs == 0 {
|
if gbo.jobs == 0 {
|
||||||
gbo.jobs = runtime.GOMAXPROCS(0)
|
gbo.jobs = runtime.GOMAXPROCS(0)
|
||||||
}
|
}
|
||||||
|
if gbo.annotations == nil {
|
||||||
|
gbo.annotations = map[string]string{}
|
||||||
|
}
|
||||||
return &gobuild{
|
return &gobuild{
|
||||||
ctx: gbo.ctx,
|
ctx: gbo.ctx,
|
||||||
getBase: gbo.getBase,
|
getBase: gbo.getBase,
|
||||||
@ -157,6 +163,7 @@ func (gbo *gobuildOpener) Open() (Interface, error) {
|
|||||||
defaultFlags: gbo.defaultFlags,
|
defaultFlags: gbo.defaultFlags,
|
||||||
defaultLdflags: gbo.defaultLdflags,
|
defaultLdflags: gbo.defaultLdflags,
|
||||||
labels: gbo.labels,
|
labels: gbo.labels,
|
||||||
|
annotations: gbo.annotations,
|
||||||
dir: gbo.dir,
|
dir: gbo.dir,
|
||||||
debug: gbo.debug,
|
debug: gbo.debug,
|
||||||
platformMatcher: matcher,
|
platformMatcher: matcher,
|
||||||
@ -1259,11 +1266,10 @@ func (g *gobuild) Build(ctx context.Context, s string) (Result, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
anns := map[string]string{
|
annotations := maps.Clone(g.annotations)
|
||||||
specsv1.AnnotationBaseImageDigest: baseDigest.String(),
|
annotations[specsv1.AnnotationBaseImageDigest] = baseDigest.String()
|
||||||
specsv1.AnnotationBaseImageName: baseRef.Name(),
|
annotations[specsv1.AnnotationBaseImageName] = baseRef.Name()
|
||||||
}
|
base = mutate.Annotations(base, annotations).(Result)
|
||||||
base = mutate.Annotations(base, anns).(Result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch mt {
|
switch mt {
|
||||||
@ -1311,15 +1317,20 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting matching image from index: %w", err)
|
return nil, fmt.Errorf("error getting matching image from index: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
annotations := maps.Clone(g.annotations)
|
||||||
// Decorate the image with the ref of the index, and the matching
|
// Decorate the image with the ref of the index, and the matching
|
||||||
// platform's digest.
|
// platform's digest.
|
||||||
img = mutate.Annotations(img, map[string]string{
|
annotations[specsv1.AnnotationBaseImageDigest] = matches[0].Digest.String()
|
||||||
specsv1.AnnotationBaseImageDigest: matches[0].Digest.String(),
|
annotations[specsv1.AnnotationBaseImageName] = baseRef.Name()
|
||||||
specsv1.AnnotationBaseImageName: baseRef.Name(),
|
img = mutate.Annotations(img, annotations).(v1.Image)
|
||||||
}).(v1.Image)
|
|
||||||
return g.buildOne(ctx, ref, img, matches[0].Platform)
|
return g.buildOne(ctx, ref, img, matches[0].Platform)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.annotations[specsv1.AnnotationBaseImageName] = baseRef.Name()
|
||||||
|
baseDigest, _ := baseIndex.Digest()
|
||||||
|
g.annotations[specsv1.AnnotationBaseImageDigest] = baseDigest.String()
|
||||||
|
|
||||||
// Build an image for each matching platform from the base and append
|
// Build an image for each matching platform from the base and append
|
||||||
// it to a new index to produce the result. We use the indices to
|
// it to a new index to produce the result. We use the indices to
|
||||||
// preserve the base image ordering here.
|
// preserve the base image ordering here.
|
||||||
@ -1333,6 +1344,7 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
annotations := maps.Clone(g.annotations)
|
||||||
// Decorate the image with the ref of the index, and the matching
|
// Decorate the image with the ref of the index, and the matching
|
||||||
// platform's digest. The ref of the index encodes the critical
|
// platform's digest. The ref of the index encodes the critical
|
||||||
// repository information for fetching the base image's digest, but
|
// repository information for fetching the base image's digest, but
|
||||||
@ -1350,10 +1362,10 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen
|
|||||||
// no-ops for us because we didn't record the digest of the actual
|
// no-ops for us because we didn't record the digest of the actual
|
||||||
// image we used, and we would potentially end up doing Nx more work
|
// image we used, and we would potentially end up doing Nx more work
|
||||||
// than we really need to do.
|
// than we really need to do.
|
||||||
baseImage = mutate.Annotations(baseImage, map[string]string{
|
annotations[specsv1.AnnotationBaseImageDigest] = desc.Digest.String()
|
||||||
specsv1.AnnotationBaseImageDigest: desc.Digest.String(),
|
annotations[specsv1.AnnotationBaseImageName] = baseRef.Name()
|
||||||
specsv1.AnnotationBaseImageName: baseRef.Name(),
|
|
||||||
}).(v1.Image)
|
baseImage = mutate.Annotations(baseImage, annotations).(v1.Image)
|
||||||
|
|
||||||
img, err := g.buildOne(gctx, ref, baseImage, desc.Platform)
|
img, err := g.buildOne(gctx, ref, baseImage, desc.Platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1383,7 +1395,7 @@ func (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Referen
|
|||||||
idx := ocimutate.AppendManifests(
|
idx := ocimutate.AppendManifests(
|
||||||
mutate.Annotations(
|
mutate.Annotations(
|
||||||
mutate.IndexMediaType(empty.Index, baseType),
|
mutate.IndexMediaType(empty.Index, baseType),
|
||||||
im.Annotations).(v1.ImageIndex),
|
g.annotations).(v1.ImageIndex),
|
||||||
adds...)
|
adds...)
|
||||||
|
|
||||||
if g.sbom != nil {
|
if g.sbom != nil {
|
||||||
|
@ -844,6 +844,8 @@ func TestGoBuild(t *testing.T) {
|
|||||||
withSBOMber(fauxSBOM),
|
withSBOMber(fauxSBOM),
|
||||||
WithLabel("foo", "bar"),
|
WithLabel("foo", "bar"),
|
||||||
WithLabel("hello", "world"),
|
WithLabel("hello", "world"),
|
||||||
|
WithAnnotation("fizz", "buzz"),
|
||||||
|
WithAnnotation("goodbye", "world"),
|
||||||
WithPlatforms("all"),
|
WithPlatforms("all"),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -898,6 +900,27 @@ func TestGoBuild(t *testing.T) {
|
|||||||
t.Fatalf("Labels diff (-got,+want): %s", d)
|
t.Fatalf("Labels diff (-got,+want): %s", d)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("check annotations", func(t *testing.T) {
|
||||||
|
baseDigest, err := base.Digest()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("base.Digest() = %v", err)
|
||||||
|
}
|
||||||
|
man, err := img.Manifest()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Manifest() = %v", err)
|
||||||
|
}
|
||||||
|
want := map[string]string{
|
||||||
|
specsv1.AnnotationBaseImageName: baseRef.Name(),
|
||||||
|
specsv1.AnnotationBaseImageDigest: baseDigest.String(),
|
||||||
|
"fizz": "buzz",
|
||||||
|
"goodbye": "world",
|
||||||
|
}
|
||||||
|
got := man.Annotations
|
||||||
|
if d := cmp.Diff(got, want); d != "" {
|
||||||
|
t.Fatalf("Annotations diff (-got,+want): %s", d)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGoBuild_Defaults(t *testing.T) {
|
func TestGoBuild_Defaults(t *testing.T) {
|
||||||
|
@ -142,6 +142,17 @@ func WithLabel(k, v string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithAnnotation is a functional option for adding annotations to built manifests
|
||||||
|
func WithAnnotation(k, v string) Option {
|
||||||
|
return func(gbo *gobuildOpener) error {
|
||||||
|
if gbo.annotations == nil {
|
||||||
|
gbo.annotations = map[string]string{}
|
||||||
|
}
|
||||||
|
gbo.annotations[k] = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// withBuilder is a functional option for overriding the way go binaries
|
// withBuilder is a functional option for overriding the way go binaries
|
||||||
// are built.
|
// are built.
|
||||||
func withBuilder(b builder) Option {
|
func withBuilder(b builder) Option {
|
||||||
|
@ -65,6 +65,7 @@ type BuildOptions struct {
|
|||||||
SBOMDir string
|
SBOMDir string
|
||||||
Platforms []string
|
Platforms []string
|
||||||
Labels []string
|
Labels []string
|
||||||
|
Annotations []string
|
||||||
Debug bool
|
Debug bool
|
||||||
// UserAgent enables overriding the default value of the `User-Agent` HTTP
|
// UserAgent enables overriding the default value of the `User-Agent` HTTP
|
||||||
// request header used when retrieving the base image.
|
// request header used when retrieving the base image.
|
||||||
@ -94,7 +95,9 @@ func AddBuildOptions(cmd *cobra.Command, bo *BuildOptions) {
|
|||||||
cmd.Flags().StringSliceVar(&bo.Platforms, "platform", []string{},
|
cmd.Flags().StringSliceVar(&bo.Platforms, "platform", []string{},
|
||||||
"Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*")
|
"Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*")
|
||||||
cmd.Flags().StringSliceVar(&bo.Labels, "image-label", []string{},
|
cmd.Flags().StringSliceVar(&bo.Labels, "image-label", []string{},
|
||||||
"Which labels (key=value) to add to the image.")
|
"Which labels (key=value[,key=value]) to add to the image.")
|
||||||
|
cmd.Flags().StringSliceVar(&bo.Annotations, "image-annotation", []string{},
|
||||||
|
"Which annotations (key=value[,key=value]) to add to the OCI manifest.")
|
||||||
cmd.Flags().BoolVar(&bo.Debug, "debug", bo.Debug,
|
cmd.Flags().BoolVar(&bo.Debug, "debug", bo.Debug,
|
||||||
"Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.")
|
"Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.")
|
||||||
bo.Trimpath = true
|
bo.Trimpath = true
|
||||||
|
@ -119,6 +119,13 @@ func gobuildOptions(bo *options.BuildOptions) ([]build.Option, error) {
|
|||||||
}
|
}
|
||||||
opts = append(opts, build.WithLabel(parts[0], parts[1]))
|
opts = append(opts, build.WithLabel(parts[0], parts[1]))
|
||||||
}
|
}
|
||||||
|
for _, an := range bo.Annotations {
|
||||||
|
k, v, ok := strings.Cut(an, "=")
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("missing '=' in annotation: %s", an)
|
||||||
|
}
|
||||||
|
opts = append(opts, build.WithAnnotation(k, v))
|
||||||
|
}
|
||||||
|
|
||||||
if bo.BuildConfigs != nil {
|
if bo.BuildConfigs != nil {
|
||||||
opts = append(opts, build.WithConfig(bo.BuildConfigs))
|
opts = append(opts, build.WithConfig(bo.BuildConfigs))
|
||||||
|
Reference in New Issue
Block a user