Skip to content

Commit

Permalink
Add new command: pack download-sbom
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony Emengo <aemengo@vmware.com>
  • Loading branch information
Anthony Emengo committed Dec 22, 2021
1 parent 0db2c77 commit e38643b
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 0 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.DownloadSBOM(logger, 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
41 changes: 41 additions & 0 deletions internal/commands/download_sbom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package commands

import (
"github.com/spf13/cobra"

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

type DownloadSBOMFlags struct {
Local bool
Remote bool
DestinationDir string
}

func DownloadSBOM(
logger logging.Logger,
client PackClient,
) *cobra.Command {
var flags DownloadSBOMFlags
cmd := &cobra.Command{
Use: "download-sbom <image-name>",
Args: cobra.ExactArgs(1),
Short: "Download SBoM from specified image",
Example: "pack download-sbom 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-sbom")
cmd.Flags().BoolVar(&flags.Local, "local", false, "Pull SBoM from local daemon (Default)")
cmd.Flags().BoolVar(&flags.Remote, "remote", false, "Pull SBoM from remote registry")
cmd.Flags().StringVar(&flags.DestinationDir, "output-dir", "", "Path to export SBoM contents.\nIt defaults export to 'layer' folder in current working directory.")
return cmd
}
57 changes: 57 additions & 0 deletions pkg/client/download_sbom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package client

import (
"context"

"github.com/buildpacks/lifecycle/layers"
"github.com/buildpacks/lifecycle/platform"
"github.com/pkg/errors"

"github.com/buildpacks/pack/pkg/dist"
"github.com/buildpacks/pack/pkg/image"
)

type DownloadSBOMOptions struct {
Daemon bool
DestinationDir string
}

// Deserialize just the subset of fields we need to avoid breaking changes
type sbomMetadata struct {
BOM *platform.LayerMetadata `json:"sbom" toml:"sbom"`
}

const (
Local = iota
Remote
)

// DownloadSBOM pulls SBOM layer from an image.
// It reads the SBOM metadata of an image then
// pulls the corresponding diffId, if it exists
func (c *Client) DownloadSBOM(name string, options DownloadSBOMOptions) error {
img, err := c.imageFetcher.Fetch(context.Background(), name, image.FetchOptions{Daemon: options.Daemon, PullPolicy: image.PullNever})
if err != nil {
if errors.Cause(err) == image.ErrNotFound {
// TODO: do something
return nil
}
return err
}

var sbomMD sbomMetadata
if _, err := dist.GetLabel(img, platform.LayerMetadataLabel, &sbomMD); err != nil {
return err
}

// TODO: if bom metadata isn't present
// do something; and return nil

rc, err := img.GetLayer(sbomMD.BOM.SHA)
if err != nil {
return err
}
defer rc.Close()

return layers.Extract(rc, options.DestinationDir)
}

0 comments on commit e38643b

Please sign in to comment.