Skip to content

Commit 83efa75

Browse files
Also read distro information from /etc/os-release when checking target compat (#1352)
* Reorder functions in file Signed-off-by: Natalie Arellano <[email protected]> * Also read distro information from /etc/os-release when checking target compat #1347 reads the file when providing target env vars to buildpacks during detect, but we also need to consider this info when deciding whether or not to run detect for the buildpack Signed-off-by: Natalie Arellano <[email protected]> * Error if we don't find run image OS during analyze And remove checks for missing OS later in the build, as it should always be there Signed-off-by: Natalie Arellano <[email protected]> --------- Signed-off-by: Natalie Arellano <[email protected]>
1 parent c0590cd commit 83efa75

6 files changed

+193
-150
lines changed

phase/analyzer.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
"github.com/buildpacks/lifecycle/api"
88
"github.com/buildpacks/lifecycle/image"
9-
"github.com/buildpacks/lifecycle/internal/fsutil"
109
"github.com/buildpacks/lifecycle/internal/layer"
1110
"github.com/buildpacks/lifecycle/log"
1211
"github.com/buildpacks/lifecycle/platform"
@@ -88,7 +87,7 @@ func (a *Analyzer) Analyze() (files.Analyzed, error) {
8887
return files.Analyzed{}, errors.Wrap(err, "unpacking metadata from image")
8988
}
9089
if atm.OS == "" {
91-
platform.GetTargetOSFromFileSystem(&fsutil.Detect{}, atm, a.Logger)
90+
return files.Analyzed{}, errors.New("failed to find OS in run image config")
9291
}
9392
}
9493
}

phase/analyzer_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S)
484484

485485
h.AssertEq(t, md.RunImage.Reference, "s0m3D1g3sT")
486486
})
487+
487488
it("populates target metadata from the run image", func() {
488489
h.AssertNil(t, previousImage.SetLabel("io.buildpacks.base.id", "id software"))
489490
h.AssertNil(t, previousImage.SetOS("windows"))
@@ -508,6 +509,18 @@ func testAnalyzer(platformAPI string) func(t *testing.T, when spec.G, it spec.S)
508509
h.AssertEq(t, md.RunImage.TargetMetadata.Distro.Version, "Helpful Holstein")
509510
}
510511
})
512+
513+
when("run image is missing OS", func() {
514+
it("errors", func() {
515+
h.AssertNil(t, previousImage.SetOS(""))
516+
_, err := analyzer.Analyze()
517+
if api.MustParse(platformAPI).LessThan("0.12") {
518+
h.AssertNil(t, err)
519+
} else {
520+
h.AssertError(t, err, "failed to find OS")
521+
}
522+
})
523+
})
511524
})
512525
})
513526
}

phase/detector.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ func (d *Detector) detectGroup(group buildpack.Group, done []buildpack.GroupElem
206206
} else {
207207
for i := range descriptor.TargetsList() {
208208
d.Logger.Debugf("Checking for match against descriptor: %s", descriptor.TargetsList()[i])
209-
if platform.TargetSatisfiedForBuild(*d.AnalyzeMD.RunImage.TargetMetadata, descriptor.TargetsList()[i]) {
209+
if platform.TargetSatisfiedForBuild(&fsutil.Detect{}, *d.AnalyzeMD.RunImage.TargetMetadata, descriptor.TargetsList()[i], d.Logger) {
210210
targetMatch = true
211211
break
212212
}

platform/run_image_test.go

-54
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"path/filepath"
55
"testing"
66

7-
"github.com/apex/log"
8-
"github.com/apex/log/handlers/memory"
97
"github.com/google/go-containerregistry/pkg/authn"
108

119
"github.com/buildpacks/lifecycle/platform"
@@ -203,58 +201,6 @@ func testRunImage(t *testing.T, when spec.G, it spec.S) {
203201
})
204202
})
205203

206-
when(".EnvVarsFor", func() {
207-
it("returns the right thing", func() {
208-
tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: &files.OSDistro{Name: "nix", Version: "22.11"}}
209-
d := &mockDetector{
210-
contents: "this is just test contents really",
211-
t: t,
212-
HasFile: false,
213-
}
214-
observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()})
215-
h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
216-
h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant)
217-
h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME="+tm.Distro.Name)
218-
h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION="+tm.Distro.Version)
219-
h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
220-
h.AssertEq(t, len(observed), 5)
221-
})
222-
223-
it("returns the right thing from /etc/os-release", func() {
224-
d := &mockDetector{
225-
contents: "this is just test contents really",
226-
t: t,
227-
HasFile: true,
228-
}
229-
tm := files.TargetMetadata{Arch: "pentium", ArchVariant: "mmx", ID: "my-id", OS: "linux", Distro: nil}
230-
observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()})
231-
h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
232-
h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT="+tm.ArchVariant)
233-
h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME=opensesame")
234-
h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION=3.14")
235-
h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
236-
h.AssertEq(t, len(observed), 5)
237-
})
238-
239-
it("does not return the wrong thing", func() {
240-
tm := files.TargetMetadata{Arch: "pentium", OS: "linux"}
241-
d := &mockDetector{
242-
contents: "this is just test contents really",
243-
t: t,
244-
HasFile: false,
245-
}
246-
observed := platform.EnvVarsFor(d, tm, &log.Logger{Handler: memory.New()})
247-
h.AssertContains(t, observed, "CNB_TARGET_ARCH="+tm.Arch)
248-
h.AssertContains(t, observed, "CNB_TARGET_OS="+tm.OS)
249-
// note: per the spec only the ID field is optional, so I guess the others should always be set: https://github.com/buildpacks/rfcs/blob/main/text/0096-remove-stacks-mixins.md#runtime-metadata
250-
// the empty ones:
251-
h.AssertContains(t, observed, "CNB_TARGET_ARCH_VARIANT=")
252-
h.AssertContains(t, observed, "CNB_TARGET_DISTRO_NAME=")
253-
h.AssertContains(t, observed, "CNB_TARGET_DISTRO_VERSION=")
254-
h.AssertEq(t, len(observed), 5)
255-
})
256-
})
257-
258204
when(".BestRunImageMirrorFor", func() {
259205
var (
260206
stackMD *files.Stack

platform/target_data.go

+48-42
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,6 @@ import (
99
"github.com/buildpacks/lifecycle/platform/files"
1010
)
1111

12-
// EnvVarsFor fulfills the prophecy set forth in https://github.com/buildpacks/rfcs/blob/b8abe33f2bdc58792acf0bd094dc4ce3c8a54dbb/text/0096-remove-stacks-mixins.md?plain=1#L97
13-
// by returning an array of "VARIABLE=value" strings suitable for inclusion in your environment or complete breakfast.
14-
func EnvVarsFor(d fsutil.Detector, tm files.TargetMetadata, logger log.Logger) []string {
15-
// we should always have os & arch,
16-
// if they are not populated try to get target information from the build-time base image
17-
if tm.OS == "" || tm.Distro == nil {
18-
logger.Info("target distro name/version labels not found, reading /etc/os-release file")
19-
GetTargetOSFromFileSystem(d, &tm, logger)
20-
}
21-
ret := []string{
22-
"CNB_TARGET_OS=" + tm.OS,
23-
"CNB_TARGET_ARCH=" + tm.Arch,
24-
"CNB_TARGET_ARCH_VARIANT=" + tm.ArchVariant,
25-
}
26-
var distName, distVersion string
27-
if tm.Distro != nil {
28-
distName = tm.Distro.Name
29-
distVersion = tm.Distro.Version
30-
}
31-
ret = append(ret, "CNB_TARGET_DISTRO_NAME="+distName)
32-
ret = append(ret, "CNB_TARGET_DISTRO_VERSION="+distVersion)
33-
return ret
34-
}
35-
3612
func GetTargetMetadata(fromImage imgutil.Image) (*files.TargetMetadata, error) {
3713
tm := files.TargetMetadata{}
3814
var err error
@@ -64,25 +40,14 @@ func GetTargetMetadata(fromImage imgutil.Image) (*files.TargetMetadata, error) {
6440
return &tm, nil
6541
}
6642

67-
// GetTargetOSFromFileSystem populates the target metadata you pass in if the information is available
68-
// returns a boolean indicating whether it populated any data.
69-
func GetTargetOSFromFileSystem(d fsutil.Detector, tm *files.TargetMetadata, logger log.Logger) {
70-
if d.HasSystemdFile() {
71-
contents, err := d.ReadSystemdFile()
72-
if err != nil {
73-
logger.Warnf("Encountered error trying to read /etc/os-release file: %s", err.Error())
74-
return
75-
}
76-
info := d.GetInfo(contents)
77-
if info.Version != "" || info.Name != "" {
78-
tm.OS = "linux"
79-
tm.Distro = &files.OSDistro{Name: info.Name, Version: info.Version}
80-
}
81-
}
82-
}
83-
8443
// TargetSatisfiedForBuild treats empty fields as wildcards and returns true if all populated fields match.
85-
func TargetSatisfiedForBuild(base files.TargetMetadata, module buildpack.TargetMetadata) bool {
44+
func TargetSatisfiedForBuild(d fsutil.Detector, base files.TargetMetadata, module buildpack.TargetMetadata, logger log.Logger) bool {
45+
// ensure we have all available data
46+
if base.Distro == nil {
47+
logger.Info("target distro name/version labels not found, reading /etc/os-release file")
48+
GetTargetOSFromFileSystem(d, &base, logger)
49+
}
50+
// check matches
8651
if !matches(base.OS, module.OS) {
8752
return false
8853
}
@@ -92,6 +57,7 @@ func TargetSatisfiedForBuild(base files.TargetMetadata, module buildpack.TargetM
9257
if !matches(base.ArchVariant, module.ArchVariant) {
9358
return false
9459
}
60+
// check distro
9561
if len(module.Distros) == 0 {
9662
return true
9763
}
@@ -115,6 +81,46 @@ func matches(target1, target2 string) bool {
11581
return target1 == target2
11682
}
11783

84+
// GetTargetOSFromFileSystem populates the target metadata you pass in if the information is available
85+
// returns a boolean indicating whether it populated any data.
86+
func GetTargetOSFromFileSystem(d fsutil.Detector, tm *files.TargetMetadata, logger log.Logger) {
87+
if d.HasSystemdFile() {
88+
contents, err := d.ReadSystemdFile()
89+
if err != nil {
90+
logger.Warnf("Encountered error trying to read /etc/os-release file: %s", err.Error())
91+
return
92+
}
93+
info := d.GetInfo(contents)
94+
if info.Version != "" || info.Name != "" {
95+
tm.Distro = &files.OSDistro{Name: info.Name, Version: info.Version}
96+
}
97+
}
98+
}
99+
100+
// EnvVarsFor fulfills the prophecy set forth in https://github.com/buildpacks/rfcs/blob/b8abe33f2bdc58792acf0bd094dc4ce3c8a54dbb/text/0096-remove-stacks-mixins.md?plain=1#L97
101+
// by returning an array of "VARIABLE=value" strings suitable for inclusion in your environment or complete breakfast.
102+
func EnvVarsFor(d fsutil.Detector, tm files.TargetMetadata, logger log.Logger) []string {
103+
// we should always have os & arch,
104+
// if they are not populated try to get target information from the build-time base image
105+
if tm.Distro == nil {
106+
logger.Info("target distro name/version labels not found, reading /etc/os-release file")
107+
GetTargetOSFromFileSystem(d, &tm, logger)
108+
}
109+
ret := []string{
110+
"CNB_TARGET_OS=" + tm.OS,
111+
"CNB_TARGET_ARCH=" + tm.Arch,
112+
"CNB_TARGET_ARCH_VARIANT=" + tm.ArchVariant,
113+
}
114+
var distName, distVersion string
115+
if tm.Distro != nil {
116+
distName = tm.Distro.Name
117+
distVersion = tm.Distro.Version
118+
}
119+
ret = append(ret, "CNB_TARGET_DISTRO_NAME="+distName)
120+
ret = append(ret, "CNB_TARGET_DISTRO_VERSION="+distVersion)
121+
return ret
122+
}
123+
118124
// TargetSatisfiedForRebase treats optional fields (ArchVariant and Distribution fields) as wildcards if empty, returns true if all populated fields match
119125
func TargetSatisfiedForRebase(t files.TargetMetadata, appTargetMetadata files.TargetMetadata) bool {
120126
if t.OS != appTargetMetadata.OS || t.Arch != appTargetMetadata.Arch {

0 commit comments

Comments
 (0)