Skip to content

Commit

Permalink
Target envvars (#1078)
Browse files Browse the repository at this point in the history
* CNB_TARGET_ env vars

- allowed to be passed through in the env vars allow-list
- threaded through builder
- threaded through detector
- threaded through generator

Signed-off-by: Joe Kimmel <jkimmel@vmware.com>

* Update buildpack/generate.go

Co-authored-by: Natalie Arellano <narellano@vmware.com>
Signed-off-by: Joe Kimmel <86852107+joe-kimmel-vmw@users.noreply.github.com>

* Update buildpack/generate.go

Co-authored-by: Natalie Arellano <narellano@vmware.com>
Signed-off-by: Joe Kimmel <86852107+joe-kimmel-vmw@users.noreply.github.com>

* Update cmd/lifecycle/builder.go

Co-authored-by: Natalie Arellano <narellano@vmware.com>
Signed-off-by: Joe Kimmel <86852107+joe-kimmel-vmw@users.noreply.github.com>

---------

Signed-off-by: Joe Kimmel <jkimmel@vmware.com>
Signed-off-by: Joe Kimmel <86852107+joe-kimmel-vmw@users.noreply.github.com>
Co-authored-by: Natalie Arellano <narellano@vmware.com>
  • Loading branch information
joe-kimmel-vmw and natalieparellano authored May 11, 2023
1 parent a2dfc78 commit 1057837
Show file tree
Hide file tree
Showing 23 changed files with 340 additions and 29 deletions.
22 changes: 22 additions & 0 deletions acceptance/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,28 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
h.AssertStringContains(t, string(output), expected)
})
})

when("It runs", func() {
it("sets CNB_TARGET_* vars", func() {
command := exec.Command(
"docker",
"run",
"--rm",
"--env", "CNB_PLATFORM_API="+latestPlatformAPI,
"--env", "CNB_LAYERS_DIR=/layers/03_layer",
"--env", "CNB_PLAN_PATH=/cnb/plan_tomls/always_detect_plan_buildpack_3.toml",
builderImage,
)
output, err := command.CombinedOutput()
fmt.Println(string(output))
h.AssertNil(t, err)
h.AssertStringContains(t, string(output), "CNB_TARGET_ARCH: amd64")
h.AssertStringContains(t, string(output), "CNB_TARGET_OS: linux")
h.AssertStringContains(t, string(output), "CNB_TARGET_VARIANT: some-variant")
h.AssertStringContains(t, string(output), "CNB_TARGET_DISTRO_NAME: ubuntu")
h.AssertStringContains(t, string(output), "CNB_TARGET_DISTRO_VERSION: some-cute-version")
})
})
}

func getBuilderMetadata(t *testing.T, path string) (string, *platform.BuildMetadata) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -eo pipefail

echo "---> Hello World 3 buildpack"

# INPUT ARGUMENTS
platform_dir=$2
env_dir=${platform_dir}/env
layers_dir=$1
plan_path=$3

# CNB_APP_DIR
echo "CNB_APP_DIR: ${PWD}"

# PLATFORM DIR
echo "PLATFORM_DIR: ${platform_dir}"

# LAYERS
echo "LAYERS_DIR: ${layers_dir}"

# PLAN
echo "PLAN_PATH: ${plan_path}"
echo "plan contents:"
cat ${plan_path}
echo

echo "CNB_TARGET_ARCH:" `printenv CNB_TARGET_ARCH`
echo "CNB_TARGET_OS:" `printenv CNB_TARGET_OS`
echo "CNB_TARGET_ID:" `printenv CNB_TARGET_ID`
echo "CNB_TARGET_VARIANT:" `printenv CNB_TARGET_VARIANT`
echo "CNB_TARGET_DISTRO_NAME:" `printenv CNB_TARGET_DISTRO_NAME`
echo "CNB_TARGET_DISTRO_VERSION:" `printenv CNB_TARGET_DISTRO_VERSION`

echo "---> Done"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Buildpack API version
api = "0.10"

# Buildpack ID and metadata
[buildpack]
id = "hello_world_3"
version = "0.0.3"
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[[entries]]

[[entries.providers]]
id = "hello_world_3"
version = "0.0.3"

[[entries.requires]]
name = "03_plan.toml_requires_subset_content_idk"
[entries.requires.metadata]
# arbitrary data describing the required dependency
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[run-image.target]
id = "my id"
os = "linux"
arch = "amd64"
arch-variant = "some-variant"
[run-image.target.distribution]
name = "ubuntu"
version = "some-cute-version"

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[[group]]
api = "0.10"
id = "hello_world_3"
version = "0.0.3"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[[entries]]

[[entries.providers]]
id = "hello_world_3"
version = "0.0.3"

6 changes: 6 additions & 0 deletions acceptance/testdata/builder/container/layers/analyzed.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[run-image]
[target]
id = "software"
os = "linux"
arch = "amd64"

8 changes: 7 additions & 1 deletion builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type Builder struct {
Out, Err io.Writer
Plan platform.BuildPlan
PlatformAPI *api.Version
AnalyzeMD platform.AnalyzedMetadata
}

func (b *Builder) Build() (*platform.BuildMetadata, error) {
Expand All @@ -63,7 +64,12 @@ func (b *Builder) Build() (*platform.BuildMetadata, error) {
)
processMap := newProcessMap()
inputs := b.getBuildInputs()
inputs.Env = env.NewBuildEnv(os.Environ())
if b.AnalyzeMD.RunImage != nil && b.AnalyzeMD.RunImage.TargetMetadata != nil && b.PlatformAPI.AtLeast("0.12") {
inputs.Env = env.NewBuildEnv(append(os.Environ(), platform.EnvVarsFor(b.AnalyzeMD.RunImage.TargetMetadata)...))
} else {
inputs.Env = env.NewBuildEnv(os.Environ())
}

filteredPlan := b.Plan

for _, bp := range b.Group.Group {
Expand Down
72 changes: 72 additions & 0 deletions builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,81 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
_, err := builder.Build()
h.AssertNil(t, err)
})
it("gets the correct env vars", func() {
builder.AnalyzeMD.RunImage = &platform.RunImage{Reference: "foo", TargetMetadata: &platform.TargetMetadata{
OS: "linux",
Arch: "amd64",
}}

bpA := &buildpack.BpDescriptor{Buildpack: buildpack.BpInfo{BaseInfo: buildpack.BaseInfo{ID: "A", Version: "v1"}}}

executor.EXPECT().Build(*bpA, gomock.Any(), gomock.Any()).DoAndReturn(
func(_ buildpack.BpDescriptor, inputs buildpack.BuildInputs, logger llog.Logger) (buildpack.BuildOutputs, error) {
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_ARCH=amd64")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_OS=linux")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_VARIANT=")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_DISTRO_NAME=")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_DISTRO_VERSION=")
return buildpack.BuildOutputs{}, nil
},
)
bpB := &buildpack.BpDescriptor{Buildpack: buildpack.BpInfo{BaseInfo: buildpack.BaseInfo{ID: "B", Version: "v1"}}}

dirStore.EXPECT().LookupBp("A", "v1").Return(bpA, nil)
dirStore.EXPECT().LookupBp("B", "v2").Return(bpB, nil)
executor.EXPECT().Build(*bpB, gomock.Any(), gomock.Any()).Do(
func(_ buildpack.BpDescriptor, inputs buildpack.BuildInputs, _ llog.Logger) (buildpack.BuildOutputs, error) {
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_ARCH=amd64")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_OS=linux")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_VARIANT=")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_DISTRO_NAME=")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_DISTRO_VERSION=")
return buildpack.BuildOutputs{}, nil
})

_, err := builder.Build()
h.AssertNil(t, err)
})
it("doesnt gets the new env vars if its old", func() {
builder.PlatformAPI = api.MustParse("0.8")
builder.AnalyzeMD.RunImage = &platform.RunImage{Reference: "foo", TargetMetadata: &platform.TargetMetadata{
OS: "linux",
Arch: "amd64",
}}

bpA := &buildpack.BpDescriptor{Buildpack: buildpack.BpInfo{BaseInfo: buildpack.BaseInfo{ID: "A", Version: "v1"}}}

executor.EXPECT().Build(*bpA, gomock.Any(), gomock.Any()).DoAndReturn(
func(_ buildpack.BpDescriptor, inputs buildpack.BuildInputs, logger llog.Logger) (buildpack.BuildOutputs, error) {
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_ARCH=amd64")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_OS=linux")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_VARIANT=")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_DISTRO_NAME=")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_DISTRO_VERSION=")
return buildpack.BuildOutputs{}, nil
},
)
bpB := &buildpack.BpDescriptor{Buildpack: buildpack.BpInfo{BaseInfo: buildpack.BaseInfo{ID: "B", Version: "v1"}}}

dirStore.EXPECT().LookupBp("A", "v1").Return(bpA, nil)
dirStore.EXPECT().LookupBp("B", "v2").Return(bpB, nil)
executor.EXPECT().Build(*bpB, gomock.Any(), gomock.Any()).Do(
func(_ buildpack.BpDescriptor, inputs buildpack.BuildInputs, _ llog.Logger) (buildpack.BuildOutputs, error) {
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_ARCH=amd64")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_OS=linux")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_VARIANT=")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_DISTRO_NAME=")
h.AssertDoesNotContain(t, inputs.Env.List(), "CNB_TARGET_DISTRO_VERSION=")
return buildpack.BuildOutputs{}, nil
})

_, err := builder.Build()
h.AssertNil(t, err)
})

it("provides the updated environment to the next buildpack", func() {
bpA := &buildpack.BpDescriptor{Buildpack: buildpack.BpInfo{BaseInfo: buildpack.BaseInfo{ID: "A", Version: "v1"}}}

executor.EXPECT().Build(*bpA, gomock.Any(), gomock.Any()).DoAndReturn(
func(_ buildpack.BpDescriptor, inputs buildpack.BuildInputs, logger llog.Logger) (buildpack.BuildOutputs, error) {
envPtr := inputs.Env.(*env.Env)
Expand Down
19 changes: 11 additions & 8 deletions cmd/lifecycle/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ type buildCmd struct {
// DefineFlags defines the flags that are considered valid and reads their values (if provided).
func (b *buildCmd) DefineFlags() {
switch {
case b.PlatformAPI.AtLeast("0.12"):
cli.FlagAnalyzedPath(&b.AnalyzedPath)
fallthrough
case b.PlatformAPI.AtLeast("0.11"):
cli.FlagAppDir(&b.AppDir)
cli.FlagBuildConfigDir(&b.BuildConfigDir)
cli.FlagBuildpacksDir(&b.BuildpacksDir)
cli.FlagGroupPath(&b.GroupPath)
cli.FlagLayersDir(&b.LayersDir)
cli.FlagPlanPath(&b.PlanPath)
cli.FlagPlatformDir(&b.PlatformDir)
fallthrough
default:
cli.FlagAppDir(&b.AppDir)
cli.FlagBuildpacksDir(&b.BuildpacksDir)
Expand Down Expand Up @@ -67,10 +65,14 @@ func (b *buildCmd) Exec() error {
if err = verifyBuildpackApis(group); err != nil {
return err
}
return b.build(group, plan)
amd, err := platform.ReadAnalyzed(b.AnalyzedPath, cmd.DefaultLogger)
if err != nil {
return unwrapErrorFailWithMessage(err, "reading analyzed.toml")
}
return b.build(group, plan, amd)
}

func (b *buildCmd) build(group buildpack.Group, plan platform.BuildPlan) error {
func (b *buildCmd) build(group buildpack.Group, plan platform.BuildPlan, analyzedMD platform.AnalyzedMetadata) error {
builder := &lifecycle.Builder{
AppDir: b.AppDir,
BuildConfigDir: b.BuildConfigDir,
Expand All @@ -84,6 +86,7 @@ func (b *buildCmd) build(group buildpack.Group, plan platform.BuildPlan) error {
Err: cmd.Stderr,
Plan: plan,
PlatformAPI: b.PlatformAPI,
AnalyzeMD: analyzedMD,
}
md, err := builder.Build()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/lifecycle/creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func (c *createCmd) Exec() error {
stopPinging := startPinging(c.docker) // send pings to docker daemon while building to prevent connection closure
cmd.DefaultLogger.Phase("BUILDING")
buildCmd := &buildCmd{Platform: c.Platform}
err = buildCmd.build(group, plan)
err = buildCmd.build(group, plan, analyzedMD)
stopPinging()
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions cmd/lifecycle/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ func (d *detectCmd) Exec() error {
group.GroupExtensions,
d.GeneratedDir,
plan,
d.PlatformAPI,
d.PlatformDir,
d.RunPath,
cmd.Stdout, cmd.Stderr,
Expand Down
6 changes: 5 additions & 1 deletion detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,11 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem
AppDir: d.AppDir,
BuildConfigDir: d.BuildConfigDir,
PlatformDir: d.PlatformDir,
Env: env.NewBuildEnv(os.Environ()),
}
if d.AnalyzeMD.RunImage != nil && d.AnalyzeMD.RunImage.TargetMetadata != nil && d.PlatformAPI.AtLeast("0.12") {
inputs.Env = env.NewBuildEnv(append(os.Environ(), platform.EnvVarsFor(d.AnalyzeMD.RunImage.TargetMetadata)...))
} else {
inputs.Env = env.NewBuildEnv(os.Environ())
}
d.Runs.Store(key, d.Executor.Detect(descriptor, inputs, d.Logger)) // this is where we finally invoke bin/detect
}
Expand Down
26 changes: 26 additions & 0 deletions detector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,32 @@ func testDetector(t *testing.T, when spec.G, it spec.S) {
_, _, _ = detector.Detect()
})

it("passes through the CNB_TARGET_* env vars", func() {
bpA1 := &buildpack.BpDescriptor{
WithAPI: "0.8",
Buildpack: buildpack.BpInfo{BaseInfo: buildpack.BaseInfo{ID: "A", Version: "v1"}},
}
detector.AnalyzeMD = platform.AnalyzedMetadata{RunImage: &platform.RunImage{TargetMetadata: &platform.TargetMetadata{OS: "linux", Arch: "amd64"}}}
dirStore.EXPECT().LookupBp("A", "v1").Return(bpA1, nil).AnyTimes()
executor.EXPECT().Detect(bpA1, gomock.Any(), gomock.Any()).Do(
func(_ buildpack.Descriptor, inputs buildpack.DetectInputs, _ log.Logger) buildpack.DetectOutputs {
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_ARCH=amd64")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_OS=linux")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_VARIANT=")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_DISTRO_NAME=")
h.AssertContains(t, inputs.Env.List(), "CNB_TARGET_DISTRO_VERSION=")
return buildpack.DetectOutputs{}
})

group := []buildpack.GroupElement{
{ID: bpA1.Buildpack.ID, Version: bpA1.Buildpack.Version, API: bpA1.WithAPI, Optional: true},
}
resolver.EXPECT().Resolve(group, detector.Runs)

detector.Order = buildpack.Order{{Group: group}}
_, _, _ = detector.Detect()
})

it("expands order-containing buildpack IDs", func() {
// This test doesn't use gomock.InOrder() because each call to Detect() happens in a go func.
// The order that other calls are written in is the order that they happen in.
Expand Down
6 changes: 6 additions & 0 deletions env/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import (
// BuildEnvIncludelist are env vars that, if set in the lifecycle's execution environment - either in a builder or by the platform, are passed-through to buildpack executables
var BuildEnvIncludelist = []string{
"CNB_STACK_ID", // deprecated as of api 0.12.0
"CNB_TARGET_OS",
"CNB_TARGET_ARCH",
"CNB_TARGET_ID",
"CNB_TARGET_VARIANT",
"CNB_TARGET_DISTRO_NAME",
"CNB_TARGET_DISTRO_VERSION",
"HOSTNAME",
"HOME",
"HTTPS_PROXY",
Expand Down
12 changes: 12 additions & 0 deletions env/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ func testBuildEnv(t *testing.T, when spec.G, it spec.S) {
it("includes expected vars", func() {
benv := env.NewBuildEnv([]string{
"CNB_STACK_ID=some-stack-id",
"CNB_TARGET_ARCH=st-louis",
"CNB_TARGET_OS=BeOS",
"CNB_TARGET_ID=tahr-jzay",
"CNB_TARGET_VARIANT=suburban",
"CNB_TARGET_DISTRO_NAME=web",
"CNB_TARGET_DISTRO_VERSION=3.0",
"HOSTNAME=some-hostname",
"HOME=some-home",
"HTTPS_PROXY=some-https-proxy",
Expand All @@ -54,6 +60,12 @@ func testBuildEnv(t *testing.T, when spec.G, it spec.S) {
sort.Strings(out)
expectedVars := []string{
"CNB_STACK_ID=some-stack-id",
"CNB_TARGET_ARCH=st-louis",
"CNB_TARGET_DISTRO_NAME=web",
"CNB_TARGET_DISTRO_VERSION=3.0",
"CNB_TARGET_ID=tahr-jzay",
"CNB_TARGET_OS=BeOS",
"CNB_TARGET_VARIANT=suburban",
"CPATH=some-cpath",
"HOME=some-home",
"HOSTNAME=some-hostname",
Expand Down
Loading

0 comments on commit 1057837

Please sign in to comment.