Skip to content

build: add support for --inherit-labels #6103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions define/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ type BuildOptions struct {
// ID mapping options to use if we're setting up our own user namespace
// when handling RUN instructions.
IDMappingOptions *IDMappingOptions
// InheritLabels controls whether or not built images will retain the labels
// which were set in their base images
InheritLabels types.OptionalBool
// AddCapabilities is a list of capabilities to add to the default set when
// handling RUN instructions.
AddCapabilities []string
Expand Down
4 changes: 4 additions & 0 deletions docs/buildah-build.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ Path to an alternative .containerignore (.dockerignore) file.
Write the built image's ID to the file. When `--platform` is specified more
than once, attempting to use this option will trigger an error.

**--inherit-labels** *bool-value*

Inherit the labels from the base image or base stages. (default true).

**--ipc** *how*

Sets the configuration for IPC namespaces when handling `RUN` instructions.
Expand Down
2 changes: 2 additions & 0 deletions imagebuildah/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type Executor struct {
additionalTags []string
log func(format string, args ...any) // can be nil
in io.Reader
inheritLabels types.OptionalBool
out io.Writer
err io.Writer
signaturePolicyPath string
Expand Down Expand Up @@ -261,6 +262,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o
err: options.Err,
reportWriter: writer,
isolation: options.Isolation,
inheritLabels: options.InheritLabels,
namespaceOptions: options.NamespaceOptions,
configureNetwork: options.ConfigureNetwork,
cniPluginPath: options.CNIPluginPath,
Expand Down
18 changes: 14 additions & 4 deletions imagebuildah/stage_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,11 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo
RootFS: rootfs,
}
dImage.Config = &dImage.ContainerConfig
if s.executor.inheritLabels == types.OptionalBoolFalse {
// If user has selected `--inherit-labels=false` let's not
// inherit labels from base image.
dImage.Config.Labels = nil
}
err = ib.FromImage(&dImage, node)
if err != nil {
if err2 := builder.Delete(); err2 != nil {
Expand Down Expand Up @@ -1872,6 +1877,11 @@ func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentSummary stri
if node == nil {
return "/bin/sh", nil
}
inheritLabels := ""
// If --inherit-label was manually set to false then update history.
if s.executor.inheritLabels == types.OptionalBoolFalse {
inheritLabels = "|inheritLabels=false"
}
switch strings.ToUpper(node.Value) {
case "ARG":
for _, variable := range strings.Fields(node.Original) {
Expand All @@ -1880,7 +1890,7 @@ func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentSummary stri
}
}
buildArgs := s.getBuildArgsKey()
return "/bin/sh -c #(nop) ARG " + buildArgs, nil
return "/bin/sh -c #(nop) ARG " + buildArgs + inheritLabels, nil
case "RUN":
shArg := ""
buildArgs := s.getBuildArgsResolvedForRun()
Expand Down Expand Up @@ -1960,16 +1970,16 @@ func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentSummary stri
if buildArgs != "" {
result = result + "|" + strconv.Itoa(len(strings.Split(buildArgs, " "))) + " " + buildArgs + " "
}
result = result + "/bin/sh -c " + shArg + heredoc + appendCheckSum
result = result + "/bin/sh -c " + shArg + heredoc + appendCheckSum + inheritLabels
return result, nil
case "ADD", "COPY":
destination := node
for destination.Next != nil {
destination = destination.Next
}
return "/bin/sh -c #(nop) " + strings.ToUpper(node.Value) + " " + addedContentSummary + " in " + destination.Value + " ", nil
return "/bin/sh -c #(nop) " + strings.ToUpper(node.Value) + " " + addedContentSummary + " in " + destination.Value + " " + inheritLabels, nil
default:
return "/bin/sh -c #(nop) " + node.Original, nil
return "/bin/sh -c #(nop) " + node.Original + inheritLabels, nil
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/cli/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
IIDFile: iopts.Iidfile,
IgnoreFile: iopts.IgnoreFile,
In: stdin,
InheritLabels: types.NewOptionalBool(iopts.InheritLabels),
Isolation: isolation,
Jobs: &iopts.Jobs,
Labels: iopts.Label,
Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type BudResults struct {
Format string
From string
Iidfile string
InheritLabels bool
Label []string
LayerLabel []string
Logfile string
Expand Down Expand Up @@ -231,6 +232,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry")
fs.BoolVar(&flags.Compress, "compress", false, "this is a legacy option, which has no effect on the image")
fs.BoolVar(&flags.CompatVolumes, "compat-volumes", false, "preserve the contents of VOLUMEs during RUN instructions")
fs.BoolVar(&flags.InheritLabels, "inherit-labels", true, "inherit the labels from the base image or base stages.")
fs.StringArrayVar(&flags.CPPFlags, "cpp-flag", []string{}, "set additional flag to pass to C preprocessor (cpp)")
fs.StringVar(&flags.Creds, "creds", "", "use `[username[:password]]` for accessing the registry")
fs.StringVarP(&flags.CWOptions, "cw", "", "", "confidential workload `options`")
Expand Down
74 changes: 74 additions & 0 deletions tests/bud.bats
Original file line number Diff line number Diff line change
Expand Up @@ -2670,6 +2670,80 @@ _EOF
expect_output "$want_output"
}

@test "bud and test inherit-labels" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing calls to _prefetch in here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests/bud/base-with-labels/Containerfile.multi-stage uses "alpine" as its base.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added _prefetch alpine too.

base=registry.fedoraproject.org/fedora-minimal
_prefetch $base
_prefetch alpine
run_buildah --version
local -a output_fields=($output)
buildah_version=${output_fields[2]}
run_buildah build $WITH_POLICY_JSON -t exp -f $BUDFILES/base-with-labels/Containerfile

run_buildah inspect --format '{{ index .Docker.Config.Labels "license"}}' exp
expect_output "MIT" "license must be MIT from fedora base image"
run_buildah inspect --format '{{ index .Docker.Config.Labels "name"}}' exp
expect_output "fedora-minimal" "name must be fedora from base image"

run_buildah build $WITH_POLICY_JSON --inherit-labels=false --label name=world -t exp -f $BUDFILES/base-with-labels/Containerfile
# no labels should be inherited from base image, only the buildah version label
# and `hello=world` which we just added using cli flag
want_output='map["io.buildah.version":"'$buildah_version'" "name":"world"]'
run_buildah inspect --format '{{printf "%q" .Docker.Config.Labels}}' exp
expect_output "$want_output"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to test builds with multiple layers (--layers=true) and ensure that the build cache doesn't use images that have been built with one value for this setting when the build is running with a different value, since the resulting configurations would differ.
It should probably also check running a multi-stage build where an earlier stage that's used as the base of a later stage sets a label, as I suspect that might not behave as expected in this iteration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be good to add a test just using --inherit-labels" to ensure the default works and doesn't get change in the future. I'm also a bit on the OCD side, I'd also like a --inherit-labels=true` test, but not completely necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added tests to work with --layers


# Try building another file with multiple layers
run_buildah build $WITH_POLICY_JSON --iidfile ${TEST_SCRATCH_DIR}/id1 --layers -t exp -f $BUDFILES/base-with-labels/Containerfile.layer
run_buildah inspect --format '{{ index .Docker.Config.Labels "license"}}' exp
expect_output "MIT" "license must be MIT from fedora base image"
run_buildah inspect --format '{{ index .Docker.Config.Labels "name"}}' exp
expect_output "world" "name must be world from Containerfile"

# Now build same file with --inherit-labels=false and verify if we are not using the cache again.
run_buildah build $WITH_POLICY_JSON --layers --inherit-labels=false --iidfile ${TEST_SCRATCH_DIR}/inherit_false_1 -t exp -f $BUDFILES/base-with-labels/Containerfile.layer
# Should not contain `Using cache` at all since
assert "$output" !~ "Using cache"
want_output='map["io.buildah.version":"'$buildah_version'" "name":"world"]'
run_buildah inspect --format '{{printf "%q" .Docker.Config.Labels}}' exp
expect_output "$want_output"

run_buildah build $WITH_POLICY_JSON --layers --inherit-labels=false --iidfile ${TEST_SCRATCH_DIR}/inherit_false_2 -t exp -f $BUDFILES/base-with-labels/Containerfile.layer
# Should contain `Using cache`
expect_output --substring " Using cache"
want_output='map["io.buildah.version":"'$buildah_version'" "name":"world"]'
run_buildah inspect --format '{{printf "%q" .Docker.Config.Labels}}' exp
expect_output "$want_output"
assert "$(cat ${TEST_SCRATCH_DIR}/inherit_false_1)" = "$(cat ${TEST_SCRATCH_DIR}/inherit_false_2)" "expected image ids to not change"

# Now build same file with --inherit-labels=true and verify if using the cache
run_buildah build $WITH_POLICY_JSON --iidfile ${TEST_SCRATCH_DIR}/id2 --layers --inherit-labels=true -t exp -f $BUDFILES/base-with-labels/Containerfile.layer
expect_output --substring " Using cache"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many times is "Using cache" expected to be printed? Would it be simpler to compare the IDs of the final images?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also added a check below to verify image id.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be checked for multi-stage builds, since for those we generally leave the final image for a stage around for use in caching.

run_buildah inspect --format '{{ index .Docker.Config.Labels "license"}}' exp
expect_output "MIT" "license must be MIT from fedora base image"
run_buildah inspect --format '{{ index .Docker.Config.Labels "name"}}' exp
expect_output "world" "name must be world from Containerfile"
# Final image id should be exactly same as the one image which was built in the past.
assert "$(cat ${TEST_SCRATCH_DIR}/id1)" = "$(cat ${TEST_SCRATCH_DIR}/id2)" "expected image ids to not change"

# Now build same file with --inherit-labels=false and verify if target stage did not inherit any labels from base stage.
run_buildah build $WITH_POLICY_JSON --layers --inherit-labels=false -t exp -f $BUDFILES/base-with-labels/Containerfile.multi-stage
want_output='map["io.buildah.version":"'$buildah_version'"]'
run_buildah inspect --format '{{printf "%q" .Docker.Config.Labels}}' exp
expect_output "$want_output"

# Now build same file with --inherit-labels=true and verify if target stage inherits labels from the base stage.
run_buildah build $WITH_POLICY_JSON --iidfile ${TEST_SCRATCH_DIR}/id3 --layers --inherit-labels=true -t exp -f $BUDFILES/base-with-labels/Containerfile.multi-stage
want_output='map["io.buildah.version":"'$buildah_version'" "name":"world"]'
run_buildah inspect --format '{{printf "%q" .Docker.Config.Labels}}' exp
expect_output "$want_output"

# Rebuild again with layers should not build image again at all.
run_buildah build $WITH_POLICY_JSON --iidfile ${TEST_SCRATCH_DIR}/id4 --layers --inherit-labels=true -t exp -f $BUDFILES/base-with-labels/Containerfile.multi-stage
want_output='map["io.buildah.version":"'$buildah_version'" "name":"world"]'
run_buildah inspect --format '{{printf "%q" .Docker.Config.Labels}}' exp
expect_output "$want_output"
assert "$(cat ${TEST_SCRATCH_DIR}/id3)" = "$(cat ${TEST_SCRATCH_DIR}/id4)" "expected image ids to not change"
}

@test "build using intermediate images should not inherit label" {
_prefetch alpine

Expand Down
4 changes: 4 additions & 0 deletions tests/bud/base-with-labels/Containerfile.layer
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM registry.fedoraproject.org/fedora-minimal
LABEL name world
RUN echo world
RUN echo hello
5 changes: 5 additions & 0 deletions tests/bud/base-with-labels/Containerfile.multi-stage
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM alpine as one
LABEL name world

FROM one
RUN echo world