Skip to content

Commit b9c485c

Browse files
Merge pull request containers#6239 from nalind/oci-created-annotation
build, commit: set the OCI ...created annotation on OCI images
2 parents 06abe5f + 5968d82 commit b9c485c

File tree

14 files changed

+201
-17
lines changed

14 files changed

+201
-17
lines changed

cmd/buildah/commit.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ type commitInputOptions struct {
6666
encryptLayers []int
6767
unsetenvs []string
6868
addFile []string
69+
unsetAnnotation []string
70+
annotation []string
71+
createdAnnotation bool
6972
}
7073

7174
func init() {
@@ -187,6 +190,11 @@ func commitListFlagSet(cmd *cobra.Command, opts *commitInputOptions) {
187190

188191
flags.StringSliceVar(&opts.unsetenvs, "unsetenv", nil, "unset env from final image")
189192
_ = cmd.RegisterFlagCompletionFunc("unsetenv", completion.AutocompleteNone)
193+
flags.StringSliceVar(&opts.unsetAnnotation, "unsetannotation", nil, "unset annotation when inheriting annotations from base image")
194+
_ = cmd.RegisterFlagCompletionFunc("unsetannotation", completion.AutocompleteNone)
195+
flags.StringArrayVar(&opts.annotation, "annotation", []string{}, "set metadata for an image (default [])")
196+
_ = cmd.RegisterFlagCompletionFunc("annotation", completion.AutocompleteNone)
197+
flags.BoolVar(&opts.createdAnnotation, "created-annotation", true, `set an "org.opencontainers.image.created" annotation in the image`)
190198
}
191199

192200
func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error {
@@ -311,6 +319,9 @@ func commitCmd(c *cobra.Command, args []string, iopts commitInputOptions) error
311319
OverrideChanges: iopts.changes,
312320
OverrideConfig: overrideConfig,
313321
ExtraImageContent: addFiles,
322+
UnsetAnnotations: iopts.unsetAnnotation,
323+
Annotations: iopts.annotation,
324+
CreatedAnnotation: types.NewOptionalBool(iopts.createdAnnotation),
314325
}
315326
exclusiveFlags := 0
316327
if c.Flag("reference-time").Changed {

commit.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ type CommitOptions struct {
104104
OmitLayerHistoryEntry bool
105105
// OmitTimestamp forces epoch 0 as created timestamp to allow for
106106
// deterministic, content-addressable builds.
107-
// Deprecated use HistoryTimestamp instead.
107+
// Deprecated: use HistoryTimestamp or SourceDateEpoch (possibly with
108+
// RewriteTimestamp) instead.
108109
OmitTimestamp bool
109110
// SignBy is the fingerprint of a GPG key to use for signing the image.
110111
SignBy string
@@ -130,7 +131,8 @@ type CommitOptions struct {
130131
// contents of a rootfs.
131132
ConfidentialWorkloadOptions ConfidentialWorkloadOptions
132133
// UnsetEnvs is a list of environments to not add to final image.
133-
// Deprecated: use UnsetEnv() before committing instead.
134+
// Deprecated: use UnsetEnv() before committing, or set OverrideChanges
135+
// instead.
134136
UnsetEnvs []string
135137
// OverrideConfig is an optional Schema2Config which can override parts
136138
// of the working container's configuration for the image that is being
@@ -167,6 +169,15 @@ type CommitOptions struct {
167169
// corresponding members in the Builder object, in the committed image
168170
// is not guaranteed.
169171
PrependedLinkedLayers, AppendedLinkedLayers []LinkedLayer
172+
// UnsetAnnotations is a list of annotations (names only) to withhold
173+
// from the image.
174+
UnsetAnnotations []string
175+
// Annotations is a list of annotations (in the form "key=value") to
176+
// add to the image.
177+
Annotations []string
178+
// CreatedAnnotation controls whether or not an "org.opencontainers.image.created"
179+
// annotation is present in the output image.
180+
CreatedAnnotation types.OptionalBool
170181
}
171182

172183
// LinkedLayer combines a history entry with the location of either a directory

define/build.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ type CommonBuildOptions struct {
4949
CPUSetMems string
5050
// HTTPProxy determines whether *_proxy env vars from the build host are passed into the container.
5151
HTTPProxy bool
52-
// IdentityLabel if set ensures that default `io.buildah.version` label is not applied to build image.
52+
// IdentityLabel if set controls whether or not a `io.buildah.version` label is added to the built image.
53+
// Setting this to false does not clear the label if it would be inherited from the base image.
5354
IdentityLabel types.OptionalBool
5455
// Memory is the upper limit (in bytes) on how much memory running containers can use.
5556
Memory int64
@@ -414,4 +415,7 @@ type BuildOptions struct {
414415
CompatLayerOmissions types.OptionalBool
415416
// NoPivotRoot inhibits the usage of pivot_root when setting up the rootfs
416417
NoPivotRoot bool
418+
// CreatedAnnotation controls whether or not an "org.opencontainers.image.created"
419+
// annotation is present in the output image.
420+
CreatedAnnotation types.OptionalBool
417421
}

docs/buildah-build.1.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,16 @@ If you have four memory nodes on your system (0-3), use `--cpuset-mems=0,1`
297297
then processes in your container will only use memory from the first
298298
two memory nodes.
299299

300+
**--created-annotation**
301+
302+
Add an image *annotation* (see also **--annotation**) to the image metadata
303+
setting "org.opencontainers.image.created" to the current time, or to the
304+
datestamp specified to the **--source-date-epoch** or **--timestamp** flag,
305+
if either was used. If *false*, no such annotation will be present in the
306+
written image.
307+
308+
Note: this information is not present in Docker image formats, so it is discarded when writing images in Docker formats.
309+
300310
**--creds** *creds*
301311

302312
The [username[:password]] to use to authenticate with the registry if required.
@@ -508,6 +518,8 @@ than once, attempting to use this option will trigger an error.
508518
**--inherit-annotations** *bool-value*
509519

510520
Inherit the annotations from the base image or base stages. (default true).
521+
Use cases which set this flag to *false* may need to do the same for the
522+
**--created-annotation** flag.
511523

512524
**--inherit-labels** *bool-value*
513525

docs/buildah-commit.1.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ will be used. The new file will be owned by UID 0, GID 0, have 0644
2929
permissions, and be given the timestamp specified to the **--timestamp** option
3030
if it is specified. This option can be specified multiple times.
3131

32+
**--annotation** *annotation[=value]*
33+
34+
Add an image *annotation* (e.g. annotation=*value*) to the image metadata. Can be used multiple times.
35+
If *annotation* is named, but neither `=` nor a `value` is provided, then the *annotation* is set to an empty value.
36+
37+
Note: this information is not present in Docker image formats, so it is discarded when writing images in Docker formats.
38+
3239
**--authfile** *path*
3340

3441
Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. See containers-auth.json(5) for more information. This file is created using `buildah login`.
@@ -55,6 +62,16 @@ Read a JSON-encoded version of an image configuration object from the specified
5562
file, and merge the values from it with the configuration of the image being
5663
committed.
5764

65+
**--created-annotation**
66+
67+
Add an image *annotation* (see also **--annotation**) to the image metadata
68+
setting "org.opencontainers.image.created" to the current time, or to the
69+
datestamp specified to the **--source-date-epoch** or **--timestamp** flag,
70+
if either was used. If *false*, no such annotation will be present in the
71+
written image.
72+
73+
Note: this information is not present in Docker image formats, so it is discarded when writing images in Docker formats.
74+
5875
**--creds** *creds*
5976

6077
The [username[:password]] to use to authenticate with the registry if required.
@@ -350,6 +367,10 @@ not affect the timestamps of layer contents.
350367

351368
Require HTTPS and verification of certificates when talking to container registries (defaults to true). TLS verification cannot be used when talking to an insecure registry.
352369

370+
**--unsetannotation** *annotation*
371+
372+
Unset the image annotation, causing the annotation not to be inherited from the base image.
373+
353374
**--unsetenv** *env*
354375

355376
Unset environment variables from the final image.

image.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ type containerImageRef struct {
104104
extraImageContent map[string]string
105105
compatSetParent types.OptionalBool
106106
layerExclusions []copier.ConditionalRemovePath
107+
unsetAnnotations []string
108+
setAnnotations []string
109+
createdAnnotation types.OptionalBool
107110
}
108111

109112
type blobLayerInfo struct {
@@ -590,6 +593,23 @@ func (i *containerImageRef) newOCIManifestBuilder() (manifestBuilder, error) {
590593
}
591594

592595
// Return partial manifest. The Layers lists will be populated later.
596+
annotations := make(map[string]string)
597+
maps.Copy(annotations, i.annotations)
598+
switch i.createdAnnotation {
599+
case types.OptionalBoolFalse:
600+
delete(annotations, v1.AnnotationCreated)
601+
default:
602+
fallthrough
603+
case types.OptionalBoolTrue, types.OptionalBoolUndefined:
604+
annotations[v1.AnnotationCreated] = created.UTC().Format(time.RFC3339Nano)
605+
}
606+
for _, k := range i.unsetAnnotations {
607+
delete(annotations, k)
608+
}
609+
for _, kv := range i.setAnnotations {
610+
k, v, _ := strings.Cut(kv, "=")
611+
annotations[k] = v
612+
}
593613
return &ociManifestBuilder{
594614
i: i,
595615
// The default layer media type assumes no compression.
@@ -604,7 +624,7 @@ func (i *containerImageRef) newOCIManifestBuilder() (manifestBuilder, error) {
604624
MediaType: v1.MediaTypeImageConfig,
605625
},
606626
Layers: []v1.Descriptor{},
607-
Annotations: i.annotations,
627+
Annotations: annotations,
608628
},
609629
}, nil
610630
}
@@ -1525,6 +1545,8 @@ func (b *Builder) makeContainerImageRef(options CommitOptions) (*containerImageR
15251545
layerLatestModTime: layerLatestModTime,
15261546
historyComment: b.HistoryComment(),
15271547
annotations: b.Annotations(),
1548+
setAnnotations: slices.Clone(options.Annotations),
1549+
unsetAnnotations: slices.Clone(options.UnsetAnnotations),
15281550
preferredManifestType: manifestType,
15291551
squash: options.Squash,
15301552
confidentialWorkload: options.ConfidentialWorkloadOptions,
@@ -1543,6 +1565,7 @@ func (b *Builder) makeContainerImageRef(options CommitOptions) (*containerImageR
15431565
extraImageContent: maps.Clone(options.ExtraImageContent),
15441566
compatSetParent: options.CompatSetParent,
15451567
layerExclusions: layerExclusions,
1568+
createdAnnotation: options.CreatedAnnotation,
15461569
}
15471570
if ref.created != nil {
15481571
for i := range ref.preEmptyLayers {

imagebuildah/executor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ type Executor struct {
171171
noPivotRoot bool
172172
sourceDateEpoch *time.Time
173173
rewriteTimestamp bool
174+
createdAnnotation types.OptionalBool
174175
}
175176

176177
type imageTypeAndHistoryAndDiffIDs struct {
@@ -342,6 +343,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
342343
noPivotRoot: options.NoPivotRoot,
343344
sourceDateEpoch: options.SourceDateEpoch,
344345
rewriteTimestamp: options.RewriteTimestamp,
346+
createdAnnotation: options.CreatedAnnotation,
345347
}
346348
// sort unsetAnnotations because we will later write these
347349
// values to the history of the image therefore we want to

imagebuildah/stage_executor.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,6 +2505,9 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
25052505
SourceDateEpoch: s.executor.sourceDateEpoch,
25062506
RewriteTimestamp: s.executor.rewriteTimestamp,
25072507
CompatLayerOmissions: s.executor.compatLayerOmissions,
2508+
UnsetAnnotations: s.executor.unsetAnnotations,
2509+
Annotations: s.executor.annotations,
2510+
CreatedAnnotation: s.executor.createdAnnotation,
25082511
}
25092512
if finalInstruction {
25102513
options.ConfidentialWorkloadOptions = s.executor.confidentialWorkload

pkg/cli/build.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
378378
Compression: compression,
379379
ConfigureNetwork: networkPolicy,
380380
ContextDirectory: contextDir,
381+
CreatedAnnotation: types.NewOptionalBool(iopts.CreatedAnnotation),
381382
Devices: iopts.Devices,
382383
DropCapabilities: iopts.CapDrop,
383384
Err: stderr,

pkg/cli/common.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ type BudResults struct {
127127
CompatVolumes bool
128128
SourceDateEpoch string
129129
RewriteTimestamp bool
130+
CreatedAnnotation bool
130131
}
131132

132133
// FromAndBugResults represents the results for common flags
@@ -240,6 +241,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
240241
fs.BoolVar(&flags.InheritLabels, "inherit-labels", true, "inherit the labels from the base image or base stages.")
241242
fs.BoolVar(&flags.InheritAnnotations, "inherit-annotations", true, "inherit the annotations from the base image or base stages.")
242243
fs.StringArrayVar(&flags.CPPFlags, "cpp-flag", []string{}, "set additional flag to pass to C preprocessor (cpp)")
244+
fs.BoolVar(&flags.CreatedAnnotation, "created-annotation", true, `set an "org.opencontainers.image.created" annotation in the image`)
243245
fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
244246
fs.StringVarP(&flags.CWOptions, "cw", "", "", "confidential workload `options`")
245247
fs.BoolVarP(&flags.DisableCompression, "disable-compression", "D", true, "don't compress layers by default")
@@ -326,7 +328,7 @@ newer: only pull base and SBOM scanner images when newer images exist on the r
326328
fs.String("variant", "", "override the `variant` of the specified image")
327329
fs.StringSliceVar(&flags.UnsetEnvs, "unsetenv", nil, "unset environment variable from final image")
328330
fs.StringSliceVar(&flags.UnsetLabels, "unsetlabel", nil, "unset label when inheriting labels from base image")
329-
fs.StringSliceVar(&flags.UnsetAnnotations, "unsetannotation", nil, "unset annotation when inheriting annotation from base image")
331+
fs.StringSliceVar(&flags.UnsetAnnotations, "unsetannotation", nil, "unset annotation when inheriting annotations from base image")
330332
return fs
331333
}
332334

0 commit comments

Comments
 (0)