Skip to content

Commit

Permalink
Write sbom.legacy.json files for newer platform api
Browse files Browse the repository at this point in the history
Signed-off-by: Natalie Arellano <[email protected]>
  • Loading branch information
natalieparellano committed Mar 10, 2022
1 parent 82298b7 commit 7707bea
Show file tree
Hide file tree
Showing 7 changed files with 556 additions and 307 deletions.
32 changes: 23 additions & 9 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package lifecycle

import (
"fmt"
"io"
goio "io"
"os"
"path/filepath"
"sort"
Expand All @@ -12,7 +12,8 @@ import (
"github.com/buildpacks/lifecycle/api"
"github.com/buildpacks/lifecycle/buildpack"
"github.com/buildpacks/lifecycle/env"
io2 "github.com/buildpacks/lifecycle/internal/io"
"github.com/buildpacks/lifecycle/internal/encoding"
"github.com/buildpacks/lifecycle/internal/io"
"github.com/buildpacks/lifecycle/launch"
"github.com/buildpacks/lifecycle/layers"
"github.com/buildpacks/lifecycle/platform"
Expand Down Expand Up @@ -42,7 +43,7 @@ type Builder struct {
Platform Platform
Group buildpack.Group
Plan platform.BuildPlan
Out, Err io.Writer
Out, Err goio.Writer
Logger Logger
BuildpackStore BuildpackStore
}
Expand All @@ -62,7 +63,8 @@ func (b *Builder) Build() (*platform.BuildMetadata, error) {

processMap := newProcessMap()
plan := b.Plan
var bom []buildpack.BOMEntry
var buildBOM []buildpack.BOMEntry
var launchBOM []buildpack.BOMEntry
var bomFiles []buildpack.BOMFile
var slices []layers.Slice
var labels []buildpack.Label
Expand All @@ -89,7 +91,8 @@ func (b *Builder) Build() (*platform.BuildMetadata, error) {
b.Logger.Debug("Updating buildpack processes")
updateDefaultProcesses(br.Processes, api.MustParse(bp.API), b.Platform.API())

bom = append(bom, br.BOM...)
buildBOM = append(buildBOM, br.BuildBOM...)
launchBOM = append(launchBOM, br.LaunchBOM...)
bomFiles = append(bomFiles, br.BOMFiles...)
labels = append(labels, br.Labels...)
plan = plan.Filter(br.MetRequires)
Expand All @@ -107,8 +110,8 @@ func (b *Builder) Build() (*platform.BuildMetadata, error) {

if b.Platform.API().LessThan("0.4") {
config.Logger.Debug("Updating BOM entries")
for i := range bom {
bom[i].ConvertMetadataToVersion()
for i := range launchBOM {
launchBOM[i].ConvertMetadataToVersion()
}
}

Expand All @@ -120,12 +123,23 @@ func (b *Builder) Build() (*platform.BuildMetadata, error) {
}
}

if b.Platform.API().AtLeast("0.9") {
b.Logger.Debug("Creating sBOM files for legacy BOM")
if err := encoding.WriteJSON(filepath.Join(b.LayersDir, "sbom", "launch", "sbom.legacy.json"), launchBOM); err != nil {
return nil, errors.Wrap(err, "encoding launch bom")
}
launchBOM = []buildpack.BOMEntry{} // zero out bom in <layers>/config/metadata.toml
if err := encoding.WriteJSON(filepath.Join(b.LayersDir, "sbom", "build", "sbom.legacy.json"), buildBOM); err != nil {
return nil, errors.Wrap(err, "encoding build bom")
}
}

b.Logger.Debug("Listing processes")
procList := processMap.list()

b.Logger.Debug("Finished build")
return &platform.BuildMetadata{
BOM: bom,
BOM: launchBOM,
Buildpacks: b.Group.Group,
Labels: labels,
Processes: procList,
Expand Down Expand Up @@ -172,7 +186,7 @@ func (b *Builder) copyBOMFiles(layersDir string, bomFiles []buildpack.BOMFile) e
return err
}

return io2.Copy(bomFile.Path, filepath.Join(targetDir, name))
return io.Copy(bomFile.Path, filepath.Join(targetDir, name))
}
)

Expand Down
148 changes: 137 additions & 11 deletions builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lifecycle_test

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
Expand Down Expand Up @@ -295,7 +296,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {

when("build metadata", func() {
when("bom", func() {
it("should aggregate BOM from each buildpack", func() {
it("should be empty", func() {
builder.Group.Group = []buildpack.GroupBuildpack{
{ID: "A", Version: "v1", API: "0.5", Homepage: "Buildpack A Homepage"},
{ID: "B", Version: "v2", API: "0.2"},
Expand All @@ -304,10 +305,19 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
bpA := testmock.NewMockBuildpack(mockCtrl)
buildpackStore.EXPECT().Lookup("A", "v1").Return(bpA, nil)
bpA.EXPECT().Build(gomock.Any(), config, gomock.Any()).Return(buildpack.BuildResult{
BOM: []buildpack.BOMEntry{
BuildBOM: []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "dep1",
Name: "build-dep1",
Metadata: map[string]interface{}{"version": "v1"},
},
Buildpack: buildpack.GroupBuildpack{ID: "A", Version: "v1"},
},
},
LaunchBOM: []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "launch-dep1",
Metadata: map[string]interface{}{"version": "v1"},
},
Buildpack: buildpack.GroupBuildpack{ID: "A", Version: "v1"},
Expand All @@ -317,10 +327,19 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
bpB := testmock.NewMockBuildpack(mockCtrl)
buildpackStore.EXPECT().Lookup("B", "v2").Return(bpB, nil)
bpB.EXPECT().Build(gomock.Any(), config, gomock.Any()).Return(buildpack.BuildResult{
BOM: []buildpack.BOMEntry{
BuildBOM: []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "dep2",
Name: "build-dep2",
Metadata: map[string]interface{}{"version": "v1"},
},
Buildpack: buildpack.GroupBuildpack{ID: "B", Version: "v2"},
},
},
LaunchBOM: []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "launch-dep2",
Metadata: map[string]interface{}{"version": "v1"},
},
Buildpack: buildpack.GroupBuildpack{ID: "B", Version: "v2"},
Expand All @@ -332,26 +351,59 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
if err != nil {
t.Fatalf("Unexpected error:\n%s\n", err)
}
if s := cmp.Diff(metadata.BOM, []buildpack.BOMEntry{
if s := cmp.Diff(metadata.BOM, []buildpack.BOMEntry{}); s != "" {
t.Fatalf("Unexpected:\n%s\n", s)
}

t.Log("saves the aggregated legacy launch bom to <layers>/sbom/launch/sbom.legacy.json")
var foundLaunch []buildpack.BOMEntry
launchContents, err := ioutil.ReadFile(filepath.Join(builder.LayersDir, "sbom", "launch", "sbom.legacy.json"))
h.AssertNil(t, err)
h.AssertNil(t, json.Unmarshal(launchContents, &foundLaunch))
expectedLaunch := []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "dep1",
Name: "launch-dep1",
Version: "",
Metadata: map[string]interface{}{"version": string("v1")},
},
Buildpack: buildpack.GroupBuildpack{ID: "A", Version: "v1"},
},
{
Require: buildpack.Require{
Name: "dep2",
Name: "launch-dep2",
Version: "",
Metadata: map[string]interface{}{"version": string("v1")},
},
Buildpack: buildpack.GroupBuildpack{ID: "B", Version: "v2"},
},
}
h.AssertEq(t, foundLaunch, expectedLaunch)

t.Log("saves the aggregated legacy build bom to <layers>/sbom/build/sbom.legacy.json")
var foundBuild []buildpack.BOMEntry
buildContents, err := ioutil.ReadFile(filepath.Join(builder.LayersDir, "sbom", "build", "sbom.legacy.json"))
h.AssertNil(t, err)
h.AssertNil(t, json.Unmarshal(buildContents, &foundBuild))
expectedBuild := []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "build-dep1",
Version: "",
Metadata: map[string]interface{}{"version": string("v1")},
},
Buildpack: buildpack.GroupBuildpack{ID: "A", Version: "v1"},
},
{
Require: buildpack.Require{
Name: "build-dep2",
Version: "",
Metadata: map[string]interface{}{"version": string("v1")},
},
Buildpack: buildpack.GroupBuildpack{ID: "B", Version: "v2"},
},
}); s != "" {
t.Fatalf("Unexpected:\n%s\n", s)
}
h.AssertEq(t, foundBuild, expectedBuild)
})
})

Expand Down Expand Up @@ -835,7 +887,7 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
bpA := testmock.NewMockBuildpack(mockCtrl)
buildpackStore.EXPECT().Lookup("A", "v1").Return(bpA, nil)
bpA.EXPECT().Build(gomock.Any(), config, gomock.Any()).Return(buildpack.BuildResult{
BOM: []buildpack.BOMEntry{
LaunchBOM: []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "dep1",
Expand Down Expand Up @@ -966,6 +1018,80 @@ func testBuilder(t *testing.T, when spec.G, it spec.S) {
})
})
})

when("platform api < 0.9", func() {
it.Before(func() {
builder.Platform = platform.NewPlatform("0.8")
})
when("build metadata", func() {
when("bom", func() {
it("returns the aggregated boms from each buildpack", func() {
builder.Group.Group = []buildpack.GroupBuildpack{
{ID: "A", Version: "v1", API: "0.5", Homepage: "Buildpack A Homepage"},
{ID: "B", Version: "v2", API: "0.2"},
}

bpA := testmock.NewMockBuildpack(mockCtrl)
buildpackStore.EXPECT().Lookup("A", "v1").Return(bpA, nil)
bpA.EXPECT().Build(gomock.Any(), config, gomock.Any()).Return(buildpack.BuildResult{
LaunchBOM: []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "dep1",
Metadata: map[string]interface{}{"version": "v1"},
},
Buildpack: buildpack.GroupBuildpack{ID: "A", Version: "v1"},
},
},
}, nil)
bpB := testmock.NewMockBuildpack(mockCtrl)
buildpackStore.EXPECT().Lookup("B", "v2").Return(bpB, nil)
bpB.EXPECT().Build(gomock.Any(), config, gomock.Any()).Return(buildpack.BuildResult{
LaunchBOM: []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "dep2",
Metadata: map[string]interface{}{"version": "v1"},
},
Buildpack: buildpack.GroupBuildpack{ID: "B", Version: "v2"},
},
},
}, nil)

metadata, err := builder.Build()
if err != nil {
t.Fatalf("Unexpected error:\n%s\n", err)
}
if s := cmp.Diff(metadata.BOM, []buildpack.BOMEntry{
{
Require: buildpack.Require{
Name: "dep1",
Version: "",
Metadata: map[string]interface{}{"version": string("v1")},
},
Buildpack: buildpack.GroupBuildpack{ID: "A", Version: "v1"},
},
{
Require: buildpack.Require{
Name: "dep2",
Version: "",
Metadata: map[string]interface{}{"version": string("v1")},
},
Buildpack: buildpack.GroupBuildpack{ID: "B", Version: "v2"},
},
}); s != "" {
t.Fatalf("Unexpected:\n%s\n", s)
}

t.Log("it does not save the aggregated legacy launch bom to <layers>/sbom/launch/sbom.legacy.json")
h.AssertPathDoesNotExist(t, filepath.Join(builder.LayersDir, "sbom", "launch", "sbom.legacy.json"))

t.Log("it does not save the aggregated legacy build bom to <layers>/sbom/build/sbom.legacy.json")
h.AssertPathDoesNotExist(t, filepath.Join(builder.LayersDir, "sbom", "build", "sbom.legacy.json"))
})
})
})
})
})
}

Expand Down
21 changes: 13 additions & 8 deletions buildpack/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ type BuildConfig struct {
}

type BuildResult struct {
BOM []BOMEntry
BuildBOM []BOMEntry
LaunchBOM []BOMEntry
BOMFiles []BOMFile
Labels []Label
MetRequires []string
Expand Down Expand Up @@ -264,7 +265,7 @@ func (b *Descriptor) readOutputFiles(bpLayersDir, bpPlanPath string, bpPlanIn Pl
}

// set BOM and MetRequires
br.BOM, err = bomValidator.ValidateBOM(bpFromBpInfo, bpPlanOut.toBOM())
br.LaunchBOM, err = bomValidator.ValidateBOM(bpFromBpInfo, bpPlanOut.toBOM())
if err != nil {
return BuildResult{}, err
}
Expand All @@ -284,20 +285,24 @@ func (b *Descriptor) readOutputFiles(bpLayersDir, bpPlanPath string, bpPlanIn Pl
}
} else {
// read build.toml
var bpBuild BuildTOML
var buildTOML BuildTOML
buildPath := filepath.Join(bpLayersDir, "build.toml")
if _, err := toml.DecodeFile(buildPath, &bpBuild); err != nil && !os.IsNotExist(err) {
if _, err := toml.DecodeFile(buildPath, &buildTOML); err != nil && !os.IsNotExist(err) {
return BuildResult{}, err
}
if _, err := bomValidator.ValidateBOM(bpFromBpInfo, bpBuild.BOM); err != nil {
if _, err := bomValidator.ValidateBOM(bpFromBpInfo, buildTOML.BOM); err != nil {
return BuildResult{}, err
}
br.BuildBOM, err = bomValidator.ValidateBOM(bpFromBpInfo, buildTOML.BOM)
if err != nil {
return BuildResult{}, err
}

// set MetRequires
if err := validateUnmet(bpBuild.Unmet, bpPlanIn); err != nil {
if err := validateUnmet(buildTOML.Unmet, bpPlanIn); err != nil {
return BuildResult{}, err
}
br.MetRequires = names(bpPlanIn.filter(bpBuild.Unmet).Entries)
br.MetRequires = names(bpPlanIn.filter(buildTOML.Unmet).Entries)

// set BOM files
br.BOMFiles, err = b.processBOMFiles(bpLayersDir, bpFromBpInfo, bpLayers, logger)
Expand All @@ -313,7 +318,7 @@ func (b *Descriptor) readOutputFiles(bpLayersDir, bpPlanPath string, bpPlanIn Pl
}

// set BOM
br.BOM, err = bomValidator.ValidateBOM(bpFromBpInfo, launchTOML.BOM)
br.LaunchBOM, err = bomValidator.ValidateBOM(bpFromBpInfo, launchTOML.BOM)
if err != nil {
return BuildResult{}, err
}
Expand Down
Loading

0 comments on commit 7707bea

Please sign in to comment.