Skip to content

Commit

Permalink
Adds -run as an input to the analyzer
Browse files Browse the repository at this point in the history
Signed-off-by: Natalie Arellano <narellano@vmware.com>
  • Loading branch information
natalieparellano committed Feb 9, 2023
1 parent 5382f32 commit ed81ef4
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 56 deletions.
37 changes: 29 additions & 8 deletions acceptance/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
})
})

when("group path is provided", func() {
it("uses the provided group path", func() {
when("called with group (on older platforms)", func() {
it("uses the provided group.toml path", func() {
h.SkipIf(t, api.MustParse(platformAPI).AtLeast("0.7"), "Platform API >= 0.7 does not accept a -group flag")

h.DockerSeedRunAndCopy(t,
Expand Down Expand Up @@ -228,8 +228,8 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
})
})

when("analyzed path is provided", func() {
it("uses the provided analyzed path", func() {
when("called with analyzed", func() {
it("uses the provided analyzed.toml path", func() {
analyzeFlags := []string{"-analyzed", ctrPath("/some-dir/some-analyzed.toml")}
if api.MustParse(platformAPI).AtLeast("0.7") {
analyzeFlags = append(analyzeFlags, "-run-image", analyzeRegFixtures.ReadOnlyRunImage)
Expand All @@ -256,6 +256,27 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
})
})

when("called with run", func() {
it("uses the provided run.toml path", func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.12"), "Platform API < 0.12 does not accept -run")
cmd := exec.Command(
"docker", "run", "--rm",
"--env", "CNB_PLATFORM_API="+platformAPI,
"--env", "CNB_REGISTRY_AUTH="+analyzeRegAuthConfig,
"--network", analyzeRegNetwork,
analyzeImage,
ctrPath(analyzerPath),
"-run", "/layers/run.toml",
analyzeRegFixtures.SomeAppImage,
) // #nosec G204
output, err := cmd.CombinedOutput()

h.AssertNotNil(t, err)
expected := "ensure registry read access to some-run-image-from-run-toml"
h.AssertStringContains(t, string(output), expected)
})
})

it("drops privileges", func() {
h.SkipIf(t, runtime.GOOS == "windows", "Not relevant on Windows")

Expand Down Expand Up @@ -357,7 +378,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
})

when("app image exists", func() {
it("does not restore app metadata", func() {
it("does not restore app metadata to the layers directory", func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.7"), "Platform API < 0.7 restores app metadata")

analyzeFlags := []string{"-daemon", "-run-image", "some-run-image"}
Expand All @@ -381,7 +402,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
assertNoRestoreOfAppMetadata(t, copyDir, output)
})

it("restores app metadata", func() {
it("restores app metadata to the layers directory (on older platforms)", func() {
h.SkipIf(t, api.MustParse(platformAPI).AtLeast("0.7"), "Platform API >= 0.7 does not restore app metadata")
output := h.DockerRunAndCopy(t,
containerName,
Expand Down Expand Up @@ -428,7 +449,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
})
})

when("cache is provided", func() {
when("cache is provided (on older platforms)", func() {
when("cache image case", func() {
when("cache image is in a daemon", func() {
it("ignores the cache", func() {
Expand Down Expand Up @@ -830,7 +851,7 @@ func testAnalyzerFunc(platformAPI string) func(t *testing.T, when spec.G, it spe
})
})

when("cache is provided", func() {
when("cache is provided (on older platforms)", func() {
when("cache image case", func() {
when("auth registry", func() {
when("registry creds are provided in CNB_REGISTRY_AUTH", func() {
Expand Down
5 changes: 5 additions & 0 deletions acceptance/testdata/analyzer/container/layers/run.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[[image]]
image = "some-run-image-from-run-toml"

[[image]]
image = "some-other-run-image"
2 changes: 1 addition & 1 deletion api/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

var (
Platform = newApisMustParse([]string{"0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "0.10", "0.11"}, []string{"0.3", "0.4", "0.5", "0.6"})
Platform = newApisMustParse([]string{"0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "0.10", "0.11", "0.12"}, []string{"0.3", "0.4", "0.5", "0.6"})
Buildpack = newApisMustParse([]string{"0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9"}, []string{"0.2", "0.3", "0.4", "0.5", "0.6"})
)

Expand Down
18 changes: 6 additions & 12 deletions cmd/lifecycle/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,14 @@ type analyzeCmd struct {

// DefineFlags defines the flags that are considered valid and reads their values (if provided).
func (a *analyzeCmd) DefineFlags() {
switch {
case a.PlatformAPI.AtLeast("0.9"):
cli.FlagAnalyzedPath(&a.AnalyzedPath)
cli.FlagCacheImage(&a.CacheImageRef)
cli.FlagGID(&a.GID)
if a.PlatformAPI.AtLeast("0.12") {
cli.FlagRunPath(&a.RunPath)
}
if a.PlatformAPI.AtLeast("0.9") {
cli.FlagLaunchCacheDir(&a.LaunchCacheDir)
cli.FlagLayersDir(&a.LayersDir)
cli.FlagPreviousImage(&a.PreviousImageRef)
cli.FlagRunImage(&a.RunImageRef)
cli.FlagSkipLayers(&a.SkipLayers)
cli.FlagStackPath(&a.StackPath)
cli.FlagTags(&a.AdditionalTags)
cli.FlagUID(&a.UID)
cli.FlagUseDaemon(&a.UseDaemon)
}
switch {
case a.PlatformAPI.AtLeast("0.7"):
cli.FlagAnalyzedPath(&a.AnalyzedPath)
cli.FlagCacheImage(&a.CacheImageRef)
Expand Down
4 changes: 4 additions & 0 deletions cmd/lifecycle/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ func FlagRunImage(runImage *string) {
flagSet.StringVar(runImage, "run-image", *runImage, "reference to run image")
}

func FlagRunPath(runPath *string) {
flagSet.StringVar(runPath, "run", *runPath, "path to run.toml")
}

func FlagSkipLayers(skipLayers *bool) {
flagSet.BoolVar(skipLayers, "skip-layers", *skipLayers, "do not provide layer metadata to buildpacks")
}
Expand Down
2 changes: 1 addition & 1 deletion exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ func testExporter(t *testing.T, when spec.G, it spec.S) {

it("saves run image metadata to the resulting image", func() {
opts.Stack = platform.StackMetadata{
RunImage: platform.StackRunImageMetadata{
RunImage: platform.RunImageMetadata{
Image: "some/run",
Mirrors: []string{"registry.example.com/some/run", "other.example.com/some/run"},
},
Expand Down
11 changes: 10 additions & 1 deletion platform/analyze_inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ func DefaultAnalyzeInputs(platformAPI *api.Version) LifecycleInputs {
}

func defaultAnalyzeInputs() LifecycleInputs {
ai := defaultAnalyzeInputs08To011()
ai.RunPath = os.Getenv(EnvRunPath)
return ai
}

func defaultAnalyzeInputs08To011() LifecycleInputs {
ai := defaultAnalyzeInputs07()
ai.LaunchCacheDir = os.Getenv(EnvLaunchCacheDir)
return ai
Expand Down Expand Up @@ -72,5 +78,8 @@ func FillAnalyzeImages(i *LifecycleInputs, logger log.Logger) error {
if i.PlatformAPI.LessThan("0.7") {
return nil
}
return fillRunImageFromStackTOMLIfNeeded(i, logger)
if i.PlatformAPI.LessThan("0.12") {
return fillRunImageFromStackTOMLIfNeeded(i, logger)
}
return fillRunImageFromRunTOMLIfNeeded(i, logger)
}
43 changes: 37 additions & 6 deletions platform/analyze_inputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,27 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp

when("latest Platform API(s)", func() {
it.Before(func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.7"), "")
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.12"), "")
inputs.RunImageRef = "some-run-image" // satisfy validation
})

when("run image", func() {
when("not provided", func() {
it("falls back to stack.toml", func() {
it("falls back to run.toml", func() {
inputs.RunImageRef = ""
inputs.StackPath = filepath.Join("testdata", "layers", "stack.toml")
inputs.RunPath = filepath.Join("testdata", "layers", "run.toml")
err := platform.ResolveInputs(platform.Analyze, &inputs, logger)
h.AssertNil(t, err)
h.AssertEq(t, inputs.RunImageRef, "some-run-image")
})

when("stack.toml not present", func() {
when("run.toml not present", func() {
it("errors", func() {
inputs.RunImageRef = ""
inputs.StackPath = "not-exist-stack.toml"
inputs.StackPath = "not-exist-run.toml"
err := platform.ResolveInputs(platform.Analyze, &inputs, logger)
h.AssertNotNil(t, err)
expected := "-run-image is required when there is no stack metadata available"
expected := "-run-image is required when there is no run metadata available"
h.AssertStringContains(t, err.Error(), expected)
})
})
Expand Down Expand Up @@ -90,6 +90,37 @@ func testAnalyzeInputs(platformAPI string) func(t *testing.T, when spec.G, it sp
})
})

when("Platform API 0.7 to 0.11", func() {
it.Before(func() {
h.SkipIf(t, api.MustParse(platformAPI).LessThan("0.7"), "")
h.SkipIf(t, api.MustParse(platformAPI).AtLeast("0.12"), "")
inputs.RunImageRef = "some-run-image" // satisfy validation
})

when("run image", func() {
when("not provided", func() {
it("falls back to stack.toml", func() {
inputs.RunImageRef = ""
inputs.StackPath = filepath.Join("testdata", "layers", "stack.toml")
err := platform.ResolveInputs(platform.Analyze, &inputs, logger)
h.AssertNil(t, err)
h.AssertEq(t, inputs.RunImageRef, "some-run-image")
})

when("stack.toml not present", func() {
it("errors", func() {
inputs.RunImageRef = ""
inputs.StackPath = "not-exist-stack.toml"
err := platform.ResolveInputs(platform.Analyze, &inputs, logger)
h.AssertNotNil(t, err)
expected := "-run-image is required when there is no stack metadata available"
h.AssertStringContains(t, err.Error(), expected)
})
})
})
})
})

when("Platform API < 0.7", func() {
it.Before(func() {
h.SkipIf(t, api.MustParse(platformAPI).AtLeast("0.7"), "")
Expand Down
4 changes: 3 additions & 1 deletion platform/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ const (
EnvOrderPath = "CNB_ORDER_PATH"
DefaultOrderFile = "order.toml"

// EnvStackPath is the location of the stack file, which contains information about the runtime base image.
// EnvRunPath is the location of the run file, which contains information about the runtime base image.
EnvRunPath = "CNB_RUN_PATH"
// EnvStackPath (deprecated) is the location of the stack file, which contains information about the runtime base image.
EnvStackPath = "CNB_STACK_PATH"
)

Expand Down
68 changes: 45 additions & 23 deletions platform/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,28 @@ func ReadAnalyzed(analyzedPath string, logger log.Logger) (AnalyzedMetadata, err

// NOTE: This struct MUST be kept in sync with `LayersMetadataCompat`
type LayersMetadata struct {
App []LayerMetadata `json:"app" toml:"app"`
BOM *LayerMetadata `json:"sbom,omitempty" toml:"sbom,omitempty"`
Buildpacks []buildpack.LayersMetadata `json:"buildpacks" toml:"buildpacks"`
Config LayerMetadata `json:"config" toml:"config"`
Launcher LayerMetadata `json:"launcher" toml:"launcher"`
ProcessTypes LayerMetadata `json:"process-types" toml:"process-types"`
RunImage RunImageMetadata `json:"runImage" toml:"run-image"`
Stack StackMetadata `json:"stack" toml:"stack"`
App []LayerMetadata `json:"app" toml:"app"`
BOM *LayerMetadata `json:"sbom,omitempty" toml:"sbom,omitempty"`
Buildpacks []buildpack.LayersMetadata `json:"buildpacks" toml:"buildpacks"`
Config LayerMetadata `json:"config" toml:"config"`
Launcher LayerMetadata `json:"launcher" toml:"launcher"`
ProcessTypes LayerMetadata `json:"process-types" toml:"process-types"`
RunImage PreviousImageRunImageMetadata `json:"runImage" toml:"run-image"`
Stack StackMetadata `json:"stack" toml:"stack"`
}

// NOTE: This struct MUST be kept in sync with `LayersMetadata`.
// It exists for situations where the `App` field type cannot be
// guaranteed, yet the original struct data must be maintained.
type LayersMetadataCompat struct {
App interface{} `json:"app" toml:"app"`
BOM *LayerMetadata `json:"sbom,omitempty" toml:"sbom,omitempty"`
Buildpacks []buildpack.LayersMetadata `json:"buildpacks" toml:"buildpacks"`
Config LayerMetadata `json:"config" toml:"config"`
Launcher LayerMetadata `json:"launcher" toml:"launcher"`
ProcessTypes LayerMetadata `json:"process-types" toml:"process-types"`
RunImage RunImageMetadata `json:"runImage" toml:"run-image"`
Stack StackMetadata `json:"stack" toml:"stack"`
App interface{} `json:"app" toml:"app"`
BOM *LayerMetadata `json:"sbom,omitempty" toml:"sbom,omitempty"`
Buildpacks []buildpack.LayersMetadata `json:"buildpacks" toml:"buildpacks"`
Config LayerMetadata `json:"config" toml:"config"`
Launcher LayerMetadata `json:"launcher" toml:"launcher"`
ProcessTypes LayerMetadata `json:"process-types" toml:"process-types"`
RunImage PreviousImageRunImageMetadata `json:"runImage" toml:"run-image"`
Stack StackMetadata `json:"stack" toml:"stack"`
}

func (m *LayersMetadata) MetadataForBuildpack(id string) buildpack.LayersMetadata {
Expand All @@ -82,7 +82,7 @@ type LayerMetadata struct {
SHA string `json:"sha" toml:"sha"`
}

type RunImageMetadata struct {
type PreviousImageRunImageMetadata struct {
TopLayer string `json:"topLayer" toml:"top-layer"`
Reference string `json:"reference" toml:"reference"`
}
Expand Down Expand Up @@ -250,30 +250,52 @@ type ImageReport struct {
ManifestSize int64 `toml:"manifest-size,omitzero"`
}

// run.toml

type RunMetadata struct {
Images []RunImageMetadata `json:"-" toml:"image"`
}

func ReadRun(runPath string, logger log.Logger) (RunMetadata, error) {
var runMD RunMetadata
if _, err := toml.DecodeFile(runPath, &runMD); err != nil {
if os.IsNotExist(err) {
logger.Infof("no run metadata found at path '%s'\n", runPath)
return RunMetadata{}, nil
}
return RunMetadata{}, err
}
return runMD, nil
}

// stack.toml

type StackMetadata struct {
RunImage StackRunImageMetadata `json:"runImage" toml:"run-image"`
RunImage RunImageMetadata `json:"runImage" toml:"run-image"`
}

type StackRunImageMetadata struct {
type RunImageMetadata struct {
Image string `toml:"image" json:"image"`
Mirrors []string `toml:"mirrors" json:"mirrors,omitempty"`
}

func (sm *StackMetadata) BestRunImageMirror(registry string) (string, error) {
if sm.RunImage.Image == "" {
func (rm *RunImageMetadata) BestRunImageMirror(registry string) (string, error) {
if rm.Image == "" {
return "", errors.New("missing run-image metadata")
}
runImageMirrors := []string{sm.RunImage.Image}
runImageMirrors = append(runImageMirrors, sm.RunImage.Mirrors...)
runImageMirrors := []string{rm.Image}
runImageMirrors = append(runImageMirrors, rm.Mirrors...)
runImageRef, err := byRegistry(registry, runImageMirrors)
if err != nil {
return "", errors.Wrap(err, "failed to find run image")
}
return runImageRef, nil
}

func (sm *StackMetadata) BestRunImageMirror(registry string) (string, error) {
return sm.RunImage.BestRunImageMirror(registry)
}

func byRegistry(reg string, imgs []string) (string, error) {
if len(imgs) < 1 {
return "", errors.New("no images provided to search")
Expand Down
2 changes: 1 addition & 1 deletion platform/files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func testFiles(t *testing.T, when spec.G, it spec.S) {
var stackMD *platform.StackMetadata

it.Before(func() {
stackMD = &platform.StackMetadata{RunImage: platform.StackRunImageMetadata{
stackMD = &platform.StackMetadata{RunImage: platform.RunImageMetadata{
Image: "first.com/org/repo",
Mirrors: []string{
"myorg/myrepo",
Expand Down
Loading

0 comments on commit ed81ef4

Please sign in to comment.