Skip to content

Commit d483c42

Browse files
authored
Merge pull request #70 from chmeliik/annotations-file
image build: add --annotations-file
2 parents c585ac5 + 07371e0 commit d483c42

File tree

3 files changed

+187
-34
lines changed

3 files changed

+187
-34
lines changed

integration_tests/build_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type BuildParams struct {
3737
Envs []string
3838
Labels []string
3939
Annotations []string
40+
AnnotationsFile string
4041
ImageSource string
4142
ImageRevision string
4243
LegacyBuildTimestamp string
@@ -181,6 +182,9 @@ func runBuildWithOutput(container *TestRunnerContainer, buildParams BuildParams)
181182
args = append(args, "--annotations")
182183
args = append(args, buildParams.Annotations...)
183184
}
185+
if buildParams.AnnotationsFile != "" {
186+
args = append(args, "--annotations-file", buildParams.AnnotationsFile)
187+
}
184188
if buildParams.ImageSource != "" {
185189
args = append(args, "--image-source", buildParams.ImageSource)
186190
}
@@ -898,6 +902,53 @@ LABEL test.label="envs-test"
898902
))
899903
})
900904

905+
t.Run("AnnotationsFile", func(t *testing.T) {
906+
contextDir := setupTestContext(t)
907+
908+
writeContainerfile(contextDir, `FROM scratch`)
909+
910+
annotationsFileContent := `
911+
# overrides default annotation
912+
org.opencontainers.image.created=never
913+
914+
annotation.from.file=overriden-below
915+
annotation.from.file=annotation-from-file
916+
917+
common.annotation=overriden-by-cli-annotation
918+
`
919+
testutil.WriteFileTree(t, contextDir, map[string]string{
920+
"annotations.cfg": annotationsFileContent,
921+
})
922+
923+
outputRef := "localhost/test-annotations-file:" + GenerateUniqueTag(t)
924+
925+
buildParams := BuildParams{
926+
Context: contextDir,
927+
OutputRef: outputRef,
928+
Push: false,
929+
Annotations: []string{
930+
"annotation.from.cli=annotation-from-CLI",
931+
"common.annotation=common-annotation-from-CLI",
932+
},
933+
AnnotationsFile: "/workspace/annotations.cfg",
934+
}
935+
936+
container := setupBuildContainerWithCleanup(t, buildParams, nil)
937+
938+
err := runBuild(container, buildParams)
939+
Expect(err).ToNot(HaveOccurred())
940+
941+
imageMeta := getImageMeta(container, outputRef)
942+
943+
imageAnnotations := formatAsKeyValuePairs(imageMeta.annotations)
944+
Expect(imageAnnotations).To(ContainElements(
945+
"org.opencontainers.image.created=never",
946+
"annotation.from.file=annotation-from-file",
947+
"annotation.from.cli=annotation-from-CLI",
948+
"common.annotation=common-annotation-from-CLI",
949+
))
950+
})
951+
901952
t.Run("OverrideDefaultLabelsAndAnnotations", func(t *testing.T) {
902953
contextDir := setupTestContext(t)
903954

pkg/commands/build.go

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"path/filepath"
99
"reflect"
1010
"runtime"
11+
"slices"
1112
"strconv"
1213
"strings"
1314
"time"
@@ -105,6 +106,13 @@ var BuildParamsConfig = map[string]common.Parameter{
105106
TypeKind: reflect.Slice,
106107
Usage: "Annotations to apply to the image using buildah's --annotation option.",
107108
},
109+
"annotations-file": {
110+
Name: "annotations-file",
111+
ShortName: "",
112+
EnvVarName: "KBC_BUILD_ANNOTATIONS_FILE",
113+
TypeKind: reflect.String,
114+
Usage: "Path to a file with annotations, same file format as --build-args-file.",
115+
},
108116
"image-source": {
109117
Name: "image-source",
110118
ShortName: "",
@@ -177,6 +185,7 @@ type BuildParams struct {
177185
Envs []string `paramName:"envs"`
178186
Labels []string `paramName:"labels"`
179187
Annotations []string `paramName:"annotations"`
188+
AnnotationsFile string `paramName:"annotations-file"`
180189
ImageSource string `paramName:"image-source"`
181190
ImageRevision string `paramName:"image-revision"`
182191
LegacyBuildTimestamp string `paramName:"legacy-build-timestamp"`
@@ -205,6 +214,8 @@ type Build struct {
205214

206215
containerfilePath string
207216
buildahSecrets []cliWrappers.BuildahSecret
217+
mergedLabels []string
218+
mergedAnnotations []string
208219
}
209220

210221
func NewBuild(cmd *cobra.Command, extraArgs []string) (*Build, error) {
@@ -255,6 +266,10 @@ func (c *Build) Run() error {
255266
return err
256267
}
257268

269+
if err := c.processLabelsAndAnnotations(); err != nil {
270+
return err
271+
}
272+
258273
if err := c.setSecretArgs(); err != nil {
259274
return err
260275
}
@@ -317,6 +332,9 @@ func (c *Build) logParams() {
317332
if len(c.Params.Annotations) > 0 {
318333
l.Logger.Infof("[param] Annotations: %v", c.Params.Annotations)
319334
}
335+
if c.Params.AnnotationsFile != "" {
336+
l.Logger.Infof("[param] AnnotationsFile: %s", c.Params.AnnotationsFile)
337+
}
320338
if c.Params.ImageSource != "" {
321339
l.Logger.Infof("[param] ImageSource: %s", c.Params.ImageSource)
322340
}
@@ -609,7 +627,7 @@ func processKeyValueEnvs(args []string) map[string]string {
609627
return values
610628
}
611629

612-
// Prepends default labels and annotations to the user-provided arrays.
630+
// Prepends default labels and annotations to the user-provided values.
613631
// User-provided values override defaults via buildah's "last value wins" behavior.
614632
//
615633
// The default annotations are primarily based on the OCI annotation spec:
@@ -621,13 +639,13 @@ func processKeyValueEnvs(args []string) map[string]string {
621639
//
622640
// In addition to the OCI annotations (and labels), if AddLegacyLabels is enabled,
623641
// adds labels based on https://github.com/projectatomic/ContainerApplicationGenericLabels.
624-
func (c *Build) mergeDefaultLabelsAndAnnotations() ([]string, []string, error) {
642+
func (c *Build) processLabelsAndAnnotations() error {
625643
var defaultLabels []string
626644
var defaultAnnotations []string
627645

628646
buildTimeStr, err := c.getBuildTimeRFC3339()
629647
if err != nil {
630-
return nil, nil, fmt.Errorf("determining build timestamp: %w", err)
648+
return fmt.Errorf("determining build timestamp: %w", err)
631649
}
632650
ociCreated := "org.opencontainers.image.created=" + buildTimeStr
633651
defaultAnnotations = append(defaultAnnotations, ociCreated)
@@ -674,8 +692,22 @@ func (c *Build) mergeDefaultLabelsAndAnnotations() ([]string, []string, error) {
674692
}
675693

676694
mergedLabels := append(defaultLabels, c.Params.Labels...)
677-
mergedAnnotations := append(defaultAnnotations, c.Params.Annotations...)
678-
return mergedLabels, mergedAnnotations, nil
695+
696+
mergedAnnotations := defaultAnnotations
697+
if c.Params.AnnotationsFile != "" {
698+
fileAnnotations, err := parseAnnotationsFile(c.Params.AnnotationsFile)
699+
if err != nil {
700+
return fmt.Errorf("parsing annotations file: %w", err)
701+
}
702+
// --annotations-file takes precedence over defaults
703+
mergedAnnotations = append(mergedAnnotations, fileAnnotations...)
704+
}
705+
// --annotations take precedence over --annotations-file
706+
mergedAnnotations = append(mergedAnnotations, c.Params.Annotations...)
707+
708+
c.mergedLabels = mergedLabels
709+
c.mergedAnnotations = mergedAnnotations
710+
return nil
679711
}
680712

681713
func (c *Build) getBuildTimeRFC3339() (string, error) {
@@ -698,6 +730,19 @@ func (c *Build) getBuildTimeRFC3339() (string, error) {
698730
return buildTime.Format(time.RFC3339), nil
699731
}
700732

733+
func parseAnnotationsFile(filePath string) ([]string, error) {
734+
annotations, err := buildargs.ParseBuildArgFile(filePath)
735+
if err != nil {
736+
return nil, err
737+
}
738+
annotationStrings := []string{}
739+
for k, v := range annotations {
740+
annotationStrings = append(annotationStrings, k+"="+v)
741+
}
742+
slices.Sort(annotationStrings)
743+
return annotationStrings, nil
744+
}
745+
701746
// Convert Go's GOARCH value to the value used for the 'architecture' label.
702747
//
703748
// Historically, the 'architecture' label used the RPM architecture names rather
@@ -725,11 +770,6 @@ func (c *Build) buildImage() error {
725770
}
726771
defer os.Chdir(originalCwd)
727772

728-
mergedLabels, mergedAnnotations, err := c.mergeDefaultLabelsAndAnnotations()
729-
if err != nil {
730-
return err
731-
}
732-
733773
buildArgs := &cliWrappers.BuildahBuildArgs{
734774
Containerfile: c.containerfilePath,
735775
ContextDir: c.Params.Context,
@@ -738,8 +778,8 @@ func (c *Build) buildImage() error {
738778
BuildArgs: c.Params.BuildArgs,
739779
BuildArgsFile: c.Params.BuildArgsFile,
740780
Envs: c.Params.Envs,
741-
Labels: mergedLabels,
742-
Annotations: mergedAnnotations,
781+
Labels: c.mergedLabels,
782+
Annotations: c.mergedAnnotations,
743783
SourceDateEpoch: c.Params.SourceDateEpoch,
744784
RewriteTimestamp: c.Params.RewriteTimestamp,
745785
ExtraArgs: c.Params.ExtraArgs,

0 commit comments

Comments
 (0)