Skip to content

Commit

Permalink
Merge pull request #1351 from aemengo/download-sbom
Browse files Browse the repository at this point in the history
Add new command: pack sbom download
Signed-off-by: David Freilich <[email protected]>
  • Loading branch information
dfreilich authored Feb 3, 2022
2 parents 9402d07 + 7db1ac5 commit 61dc8c7
Show file tree
Hide file tree
Showing 9 changed files with 428 additions and 26 deletions.
1 change: 1 addition & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func NewPackCommand(logger ConfigurableLogger) (*cobra.Command, error) {
rootCmd.AddCommand(commands.InspectImage(logger, imagewriter.NewFactory(), cfg, packClient))
rootCmd.AddCommand(commands.NewStackCommand(logger))
rootCmd.AddCommand(commands.Rebase(logger, cfg, packClient))
rootCmd.AddCommand(commands.NewSBOMCommand(logger, cfg, packClient))

rootCmd.AddCommand(commands.InspectBuildpack(logger, cfg, packClient))
rootCmd.AddCommand(commands.InspectBuilder(logger, cfg, packClient, builderwriter.NewFactory()))
Expand Down
1 change: 1 addition & 0 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type PackClient interface {
YankBuildpack(client.YankBuildpackOptions) error
InspectBuildpack(client.InspectBuildpackOptions) (*client.BuildpackInfo, error)
PullBuildpack(context.Context, client.PullBuildpackOptions) error
DownloadSBOM(name string, options client.DownloadSBOMOptions) error
}

func AddHelpFlag(cmd *cobra.Command, commandName string) {
Expand Down
40 changes: 40 additions & 0 deletions internal/commands/download_sbom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package commands

import (
"github.com/spf13/cobra"

cpkg "github.com/buildpacks/pack/pkg/client"
"github.com/buildpacks/pack/pkg/logging"
)

type DownloadSBOMFlags struct {
Remote bool
DestinationDir string
}

func DownloadSBOM(
logger logging.Logger,
client PackClient,
) *cobra.Command {
var flags DownloadSBOMFlags
cmd := &cobra.Command{
Use: "download <image-name>",
Args: cobra.ExactArgs(1),
Short: "Download SBoM from specified image",
Long: "Download layer containing Structured Bill of Materials (SBoM) from specified image",
Example: "pack sbom download buildpacksio/pack",
RunE: logError(logger, func(cmd *cobra.Command, args []string) error {
img := args[0]
options := cpkg.DownloadSBOMOptions{
Daemon: !flags.Remote,
DestinationDir: flags.DestinationDir,
}

return client.DownloadSBOM(img, options)
}),
}
AddHelpFlag(cmd, "download")
cmd.Flags().BoolVar(&flags.Remote, "remote", false, "Download SBoM of image in remote registry (without pulling image)")
cmd.Flags().StringVarP(&flags.DestinationDir, "output-dir", "o", ".", "Path to export SBoM contents.\nIt defaults export to the current working directory.")
return cmd
}
101 changes: 101 additions & 0 deletions internal/commands/download_sbom_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package commands_test

import (
"bytes"
"testing"

"github.com/golang/mock/gomock"
"github.com/heroku/color"
"github.com/pkg/errors"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"
"github.com/spf13/cobra"

"github.com/buildpacks/pack/internal/commands"
"github.com/buildpacks/pack/internal/commands/testmocks"
cpkg "github.com/buildpacks/pack/pkg/client"
"github.com/buildpacks/pack/pkg/logging"
h "github.com/buildpacks/pack/testhelpers"
)

func TestDownloadSBOMCommand(t *testing.T) {
color.Disable(true)
defer color.Disable(false)
spec.Run(t, "DownloadSBOMCommand", testDownloadSBOMCommand, spec.Parallel(), spec.Report(report.Terminal{}))
}

func testDownloadSBOMCommand(t *testing.T, when spec.G, it spec.S) {
var (
command *cobra.Command
logger logging.Logger
outBuf bytes.Buffer
mockController *gomock.Controller
mockClient *testmocks.MockPackClient
)

it.Before(func() {
mockController = gomock.NewController(t)
mockClient = testmocks.NewMockPackClient(mockController)
logger = logging.NewLogWithWriters(&outBuf, &outBuf)
command = commands.DownloadSBOM(logger, mockClient)
})

it.After(func() {
mockController.Finish()
})

when("#DownloadSBOM", func() {
when("happy path", func() {
it("returns no error", func() {
mockClient.EXPECT().DownloadSBOM("some/image", cpkg.DownloadSBOMOptions{
Daemon: true,
DestinationDir: ".",
})
command.SetArgs([]string{"some/image"})

err := command.Execute()
h.AssertNil(t, err)
})
})

when("the remote flag is specified", func() {
it("respects the remote flag", func() {
mockClient.EXPECT().DownloadSBOM("some/image", cpkg.DownloadSBOMOptions{
Daemon: false,
DestinationDir: ".",
})
command.SetArgs([]string{"some/image", "--remote"})

err := command.Execute()
h.AssertNil(t, err)
})
})

when("the output-dir flag is specified", func() {
it("respects the output-dir flag", func() {
mockClient.EXPECT().DownloadSBOM("some/image", cpkg.DownloadSBOMOptions{
Daemon: true,
DestinationDir: "some-destination-dir",
})
command.SetArgs([]string{"some/image", "--output-dir", "some-destination-dir"})

err := command.Execute()
h.AssertNil(t, err)
})
})

when("the client returns an error", func() {
it("returns the error", func() {
mockClient.EXPECT().DownloadSBOM("some/image", cpkg.DownloadSBOMOptions{
Daemon: true,
DestinationDir: ".",
}).Return(errors.New("some-error"))

command.SetArgs([]string{"some/image"})

err := command.Execute()
h.AssertError(t, err, "some-error")
})
})
})
}
4 changes: 4 additions & 0 deletions internal/commands/inspect_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ func InspectImage(
remote, remoteErr := client.InspectImage(img, false)
local, localErr := client.InspectImage(img, true)

if flags.BOM {
logger.Warn("Using the '--bom' flag with 'pack inspect-image <image-name>' is deprecated. Users are encouraged to use 'pack sbom download <image-name>'.")
}

if err := w.Print(logger, sharedImageInfo, local, remote, localErr, remoteErr); err != nil {
return err
}
Expand Down
20 changes: 20 additions & 0 deletions internal/commands/sbom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package commands

import (
"github.com/spf13/cobra"

"github.com/buildpacks/pack/internal/config"
"github.com/buildpacks/pack/pkg/logging"
)

func NewSBOMCommand(logger logging.Logger, cfg config.Config, client PackClient) *cobra.Command {
cmd := &cobra.Command{
Use: "sbom",
Short: "Interact with SBoM",
RunE: nil,
}

cmd.AddCommand(DownloadSBOM(logger, client))
AddHelpFlag(cmd, "sbom")
return cmd
}
Loading

0 comments on commit 61dc8c7

Please sign in to comment.