Skip to content

Commit

Permalink
Merge pull request #1598 from joe-kimmel-vmw/1221-report-toml-for-the…
Browse files Browse the repository at this point in the history
…-peoples

adds flag for saving report toml to back build and rebase
  • Loading branch information
jkutner authored Jan 14, 2023
2 parents d1024e9 + ac117c3 commit 6c23a08
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 85 deletions.
6 changes: 6 additions & 0 deletions internal/build/lifecycle_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ func (l *LifecycleExecution) Create(ctx context.Context, buildCache, launchCache
If(l.opts.SBOMDestinationDir != "", WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOutTo(l.mountPaths.sbomDir(), l.opts.SBOMDestinationDir))),
If(l.opts.ReportDestinationDir != "", WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOutTo(l.mountPaths.reportPath(), l.opts.ReportDestinationDir))),
If(l.opts.Interactive, WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOut(l.opts.Termui.ReadLayers, l.mountPaths.layersDir(), l.mountPaths.appDir()))),
Expand Down Expand Up @@ -652,6 +655,9 @@ func (l *LifecycleExecution) Export(ctx context.Context, buildCache, launchCache
If(l.opts.SBOMDestinationDir != "", WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOutTo(l.mountPaths.sbomDir(), l.opts.SBOMDestinationDir))),
If(l.opts.ReportDestinationDir != "", WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOutTo(l.mountPaths.reportPath(), l.opts.ReportDestinationDir))),
If(l.opts.Interactive, WithPostContainerRunOperations(
EnsureVolumeAccess(l.opts.Builder.UID(), l.opts.Builder.GID(), l.os, l.layersVolume, l.appVolume),
CopyOut(l.opts.Termui.ReadLayers, l.mountPaths.layersDir(), l.mountPaths.appDir()))),
Expand Down
27 changes: 27 additions & 0 deletions internal/build/lifecycle_execution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,21 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
})
})

when("report destination directory is provided", func() {
lifecycleOps = append(lifecycleOps, func(opts *build.LifecycleOptions) {
opts.ReportDestinationDir = "a-destination-dir"
})

it("provides copy-sbom-func as a post container operation", func() {
h.AssertEq(t, fakePhase.CleanupCallCount, 1)
h.AssertEq(t, fakePhase.RunCallCount, 1)

h.AssertEq(t, len(configProvider.PostContainerRunOps()), 2)
h.AssertFunctionName(t, configProvider.PostContainerRunOps()[0], "EnsureVolumeAccess")
h.AssertFunctionName(t, configProvider.PostContainerRunOps()[1], "CopyOut")
})
})

when("--creation-time", func() {
when("platform < 0.9", func() {
platformAPI = api.MustParse("0.8")
Expand Down Expand Up @@ -1970,6 +1985,18 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
})
})

when("report destination directory is provided", func() {
lifecycleOps = append(lifecycleOps, func(opts *build.LifecycleOptions) {
opts.ReportDestinationDir = "a-destination-dir"
})

it("provides copy-sbom-func as a post container operation", func() {
h.AssertEq(t, len(configProvider.PostContainerRunOps()), 2)
h.AssertFunctionName(t, configProvider.PostContainerRunOps()[0], "EnsureVolumeAccess")
h.AssertFunctionName(t, configProvider.PostContainerRunOps()[1], "CopyOut")
})
})

when("--creation-time", func() {
when("platform < 0.9", func() {
platformAPI = api.MustParse("0.8")
Expand Down
59 changes: 30 additions & 29 deletions internal/build/lifecycle_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,35 +67,36 @@ func init() {
}

type LifecycleOptions struct {
AppPath string
Image name.Reference
Builder Builder
BuilderImage string // differs from Builder.Name() and Builder.Image().Name() in that it includes the registry context
LifecycleImage string
RunImage string
ProjectMetadata platform.ProjectMetadata
ClearCache bool
Publish bool
TrustBuilder bool
UseCreator bool
Interactive bool
Termui Termui
DockerHost string
Cache cache.CacheOpts
CacheImage string
HTTPProxy string
HTTPSProxy string
NoProxy string
Network string
AdditionalTags []string
Volumes []string
DefaultProcessType string
FileFilter func(string) bool
Workspace string
GID int
PreviousImage string
SBOMDestinationDir string
CreationTime *time.Time
AppPath string
Image name.Reference
Builder Builder
BuilderImage string // differs from Builder.Name() and Builder.Image().Name() in that it includes the registry context
LifecycleImage string
RunImage string
ProjectMetadata platform.ProjectMetadata
ClearCache bool
Publish bool
TrustBuilder bool
UseCreator bool
Interactive bool
Termui Termui
DockerHost string
Cache cache.CacheOpts
CacheImage string
HTTPProxy string
HTTPSProxy string
NoProxy string
Network string
AdditionalTags []string
Volumes []string
DefaultProcessType string
FileFilter func(string) bool
Workspace string
GID int
PreviousImage string
ReportDestinationDir string
SBOMDestinationDir string
CreationTime *time.Time
}

func NewLifecycleExecutor(logger logging.Logger, docker client.CommonAPIClient) *LifecycleExecutor {
Expand Down
4 changes: 4 additions & 0 deletions internal/build/mount_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func (m mountPaths) projectPath() string {
return m.join(m.layersDir(), "project-metadata.toml")
}

func (m mountPaths) reportPath() string {
return m.join(m.layersDir(), "report.toml")
}

func (m mountPaths) appDirName() string {
return m.workspace
}
Expand Down
55 changes: 29 additions & 26 deletions internal/commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,33 @@ import (
)

type BuildFlags struct {
Publish bool
ClearCache bool
TrustBuilder bool
Interactive bool
DockerHost string
CacheImage string
Cache cache.CacheOpts
AppPath string
Builder string
Registry string
RunImage string
Policy string
Network string
DescriptorPath string
DefaultProcessType string
LifecycleImage string
Env []string
EnvFiles []string
Buildpacks []string
Volumes []string
AdditionalTags []string
Workspace string
GID int
PreviousImage string
SBOMDestinationDir string
DateTime string
Publish bool
ClearCache bool
TrustBuilder bool
Interactive bool
DockerHost string
CacheImage string
Cache cache.CacheOpts
AppPath string
Builder string
Registry string
RunImage string
Policy string
Network string
DescriptorPath string
DefaultProcessType string
LifecycleImage string
Env []string
EnvFiles []string
Buildpacks []string
Volumes []string
AdditionalTags []string
Workspace string
GID int
PreviousImage string
SBOMDestinationDir string
ReportDestinationDir string
DateTime string
}

// Build an image from source code
Expand Down Expand Up @@ -171,6 +172,7 @@ func Build(logger logging.Logger, cfg config.Config, packClient PackClient) *cob
PreviousImage: flags.PreviousImage,
Interactive: flags.Interactive,
SBOMDestinationDir: flags.SBOMDestinationDir,
ReportDestinationDir: flags.ReportDestinationDir,
CreationTime: dateTime,
}); err != nil {
return errors.Wrap(err, "failed to build")
Expand Down Expand Up @@ -238,6 +240,7 @@ This option may set DOCKER_HOST environment variable for the build container if
cmd.Flags().IntVar(&buildFlags.GID, "gid", 0, `Override GID of user's group in the stack's build and run images. The provided value must be a positive number`)
cmd.Flags().StringVar(&buildFlags.PreviousImage, "previous-image", "", "Set previous image to a particular tag reference, digest reference, or (when performing a daemon build) image ID")
cmd.Flags().StringVar(&buildFlags.SBOMDestinationDir, "sbom-output-dir", "", "Path to export SBoM contents.\nOmitting the flag will yield no SBoM content.")
cmd.Flags().StringVar(&buildFlags.ReportDestinationDir, "report-output-dir", "", "Path to export build report.toml.\nOmitting the flag yield no report file.")
cmd.Flags().BoolVar(&buildFlags.Interactive, "interactive", false, "Launch a terminal UI to depict the build process")
if !cfg.Experimental {
cmd.Flags().MarkHidden("interactive")
Expand Down
1 change: 1 addition & 0 deletions internal/commands/rebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func Rebase(logger logging.Logger, cfg config.Config, pack PackClient) *cobra.Co
cmd.Flags().BoolVar(&opts.Publish, "publish", false, "Publish to registry")
cmd.Flags().StringVar(&opts.RunImage, "run-image", "", "Run image to use for rebasing")
cmd.Flags().StringVar(&policy, "pull-policy", "", "Pull policy to use. Accepted values are always, never, and if-not-present. The default is always")
cmd.Flags().StringVar(&opts.ReportDestinationDir, "report-output-dir", "", "Path to export build report.toml.\nOmitting the flag yield no report file.")

AddHelpFlag(cmd, "rebase")
return cmd
Expand Down
62 changes: 33 additions & 29 deletions pkg/client/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ type BuildOptions struct {
// Directory to output any SBOM artifacts
SBOMDestinationDir string

// Directory to output the report.toml metadata artifact
ReportDestinationDir string

// Desired create time in the output image config
CreationTime *time.Time
}
Expand Down Expand Up @@ -351,35 +354,36 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
}

lifecycleOpts := build.LifecycleOptions{
AppPath: appPath,
Image: imageRef,
Builder: ephemeralBuilder,
BuilderImage: builderRef.Name(),
LifecycleImage: ephemeralBuilder.Name(),
RunImage: runImageName,
ProjectMetadata: projectMetadata,
ClearCache: opts.ClearCache,
Publish: opts.Publish,
TrustBuilder: opts.TrustBuilder(opts.Builder),
UseCreator: false,
DockerHost: opts.DockerHost,
Cache: opts.Cache,
CacheImage: opts.CacheImage,
HTTPProxy: proxyConfig.HTTPProxy,
HTTPSProxy: proxyConfig.HTTPSProxy,
NoProxy: proxyConfig.NoProxy,
Network: opts.ContainerConfig.Network,
AdditionalTags: opts.AdditionalTags,
Volumes: processedVolumes,
DefaultProcessType: opts.DefaultProcessType,
FileFilter: fileFilter,
Workspace: opts.Workspace,
GID: opts.GroupID,
PreviousImage: opts.PreviousImage,
Interactive: opts.Interactive,
Termui: termui.NewTermui(imageRef.Name(), ephemeralBuilder, runImageName),
SBOMDestinationDir: opts.SBOMDestinationDir,
CreationTime: opts.CreationTime,
AppPath: appPath,
Image: imageRef,
Builder: ephemeralBuilder,
BuilderImage: builderRef.Name(),
LifecycleImage: ephemeralBuilder.Name(),
RunImage: runImageName,
ProjectMetadata: projectMetadata,
ClearCache: opts.ClearCache,
Publish: opts.Publish,
TrustBuilder: opts.TrustBuilder(opts.Builder),
UseCreator: false,
DockerHost: opts.DockerHost,
Cache: opts.Cache,
CacheImage: opts.CacheImage,
HTTPProxy: proxyConfig.HTTPProxy,
HTTPSProxy: proxyConfig.HTTPSProxy,
NoProxy: proxyConfig.NoProxy,
Network: opts.ContainerConfig.Network,
AdditionalTags: opts.AdditionalTags,
Volumes: processedVolumes,
DefaultProcessType: opts.DefaultProcessType,
FileFilter: fileFilter,
Workspace: opts.Workspace,
GID: opts.GroupID,
PreviousImage: opts.PreviousImage,
Interactive: opts.Interactive,
Termui: termui.NewTermui(imageRef.Name(), ephemeralBuilder, runImageName),
ReportDestinationDir: opts.ReportDestinationDir,
SBOMDestinationDir: opts.SBOMDestinationDir,
CreationTime: opts.CreationTime,
}

lifecycleVersion := ephemeralBuilder.LifecycleDescriptor().Info.Version
Expand Down
11 changes: 11 additions & 0 deletions pkg/client/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2516,6 +2516,17 @@ func testBuild(t *testing.T, when spec.G, it spec.S) {
})
})

when("report destination dir option", func() {
it("passthroughs to lifecycle", func() {
h.AssertNil(t, subject.Build(context.TODO(), BuildOptions{
Builder: defaultBuilderName,
Image: "example.com/some/repo:tag",
ReportDestinationDir: "a-destination-dir",
}))
h.AssertEq(t, fakeLifecycle.Opts.ReportDestinationDir, "a-destination-dir")
})
})

when("there are extensions", func() {
withExtensionsLabel = true

Expand Down
24 changes: 23 additions & 1 deletion pkg/client/rebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package client

import (
"context"
"os"
"path/filepath"

"github.com/BurntSushi/toml"
"github.com/buildpacks/lifecycle"
"github.com/buildpacks/lifecycle/platform"
"github.com/pkg/errors"
Expand Down Expand Up @@ -34,6 +37,9 @@ type RebaseOptions struct {
// AdditionalMirrors gives us inputs to recalculate the 'best' run image
// based on the registry we are publishing to.
AdditionalMirrors map[string][]string

// If provided, directory to which report.toml will be copied
ReportDestinationDir string
}

// Rebase updates the run image layers in an app image.
Expand Down Expand Up @@ -80,7 +86,7 @@ func (c *Client) Rebase(ctx context.Context, opts RebaseOptions) error {

c.logger.Infof("Rebasing %s on run image %s", style.Symbol(appImage.Name()), style.Symbol(baseImage.Name()))
rebaser := &lifecycle.Rebaser{Logger: c.logger, PlatformAPI: build.SupportedPlatformAPIVersions.Latest()}
_, err = rebaser.Rebase(appImage, baseImage, nil)
report, err := rebaser.Rebase(appImage, baseImage, nil)
if err != nil {
return err
}
Expand All @@ -91,5 +97,21 @@ func (c *Client) Rebase(ctx context.Context, opts RebaseOptions) error {
}

c.logger.Infof("Rebased Image: %s", style.Symbol(appImageIdentifier.String()))

if opts.ReportDestinationDir != "" {
reportPath := filepath.Join(opts.ReportDestinationDir, "report.toml")
reportFile, err := os.OpenFile(reportPath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
c.logger.Warnf("unable to open %s for writing rebase report", reportPath)
return err
}

defer reportFile.Close()
err = toml.NewEncoder(reportFile).Encode(report)
if err != nil {
c.logger.Warnf("unable to write rebase report to %s", reportPath)
return err
}
}
return nil
}
14 changes: 14 additions & 0 deletions pkg/client/rebase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package client
import (
"bytes"
"context"
"os"
"path/filepath"
"testing"

"github.com/buildpacks/imgutil/fakes"
Expand Down Expand Up @@ -200,6 +202,18 @@ func testRebase(t *testing.T, when spec.G, it spec.S) {
})
})

when("report directory is set", func() {
it("writes the report", func() {
tmpdir := t.TempDir()
h.AssertNil(t, subject.Rebase(context.TODO(), RebaseOptions{
RepoName: "some/app",
ReportDestinationDir: tmpdir,
}))
_, err := os.Stat(filepath.Join(tmpdir, "report.toml"))
h.AssertNil(t, err)
})
})

when("is true", func() {
it.Before(func() {
fakeImageFetcher.RemoteImages["some/app"] = fakeAppImage
Expand Down

0 comments on commit 6c23a08

Please sign in to comment.