Skip to content

Commit

Permalink
refactor: packager inspect command (#2990)
Browse files Browse the repository at this point in the history
Signed-off-by: schristoff <28318173+schristoff@users.noreply.github.com>
  • Loading branch information
schristoff authored Sep 24, 2024
1 parent 000eee3 commit 5d4fc4b
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 54 deletions.
54 changes: 26 additions & 28 deletions src/cmd/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
"github.com/zarf-dev/zarf/src/pkg/message"
"github.com/zarf-dev/zarf/src/pkg/packager"
"github.com/zarf-dev/zarf/src/pkg/packager/filters"
"github.com/zarf-dev/zarf/src/pkg/packager/sources"
"github.com/zarf-dev/zarf/src/pkg/utils"
"github.com/zarf-dev/zarf/src/types"
)

Expand Down Expand Up @@ -188,26 +188,39 @@ var packageInspectCmd = &cobra.Command{
}
},
RunE: func(cmd *cobra.Command, args []string) error {
packageSource, err := choosePackage(args)
src, err := choosePackage(args)
if err != nil {
return err
}
pkgConfig.PkgOpts.PackageSource = packageSource
src, err := identifyAndFallbackToClusterSource()
if err != nil {
return err

cluster, _ := cluster.NewCluster()
inspectOpt := packager2.ZarfInspectOptions{
Source: src,
SkipSignatureValidation: pkgConfig.PkgOpts.SkipSignatureValidation,
Cluster: cluster,
ListImages: pkgConfig.InspectOpts.ListImages,
ViewSBOM: pkgConfig.InspectOpts.ViewSBOM,
SBOMOutputDir: pkgConfig.InspectOpts.SBOMOutputDir,
PublicKeyPath: pkgConfig.PkgOpts.PublicKeyPath,
}
pkgClient, err := packager.New(&pkgConfig, packager.WithSource(src))
if err != nil {
return err

if pkgConfig.InspectOpts.ListImages {
output, err := packager2.InspectList(cmd.Context(), inspectOpt)
if err != nil {
return fmt.Errorf("failed to inspect package: %w", err)
}
for _, image := range output {
fmt.Fprintln(os.Stdout, "-", image)
}
}
defer pkgClient.ClearTempPaths()
if err := pkgClient.Inspect(cmd.Context()); err != nil {

output, err := packager2.Inspect(cmd.Context(), inspectOpt)
if err != nil {
return fmt.Errorf("failed to inspect package: %w", err)
}
utils.ColorPrintYAML(output, nil, false)
return nil
},
ValidArgsFunction: getPackageCompletionArgs,
}

var packageListCmd = &cobra.Command{
Expand Down Expand Up @@ -280,6 +293,7 @@ var packageRemoveCmd = &cobra.Command{
Cluster: cluster,
Filter: filter,
SkipSignatureValidation: pkgConfig.PkgOpts.SkipSignatureValidation,
PublicKeyPath: pkgConfig.PkgOpts.PublicKeyPath,
}
err = packager2.Remove(cmd.Context(), removeOpt)
if err != nil {
Expand Down Expand Up @@ -384,22 +398,6 @@ func choosePackage(args []string) (string, error) {
return path, nil
}

// NOTE: If the source is identified nil is returned because packager will create the source if it is nil.
// If it can't be identified the cluster source is used causing packager to ignore the configured package source.
// Use of cluster package source is limited to a few functions which is why this is not the default behavior.
func identifyAndFallbackToClusterSource() (sources.PackageSource, error) {
identifiedSrc := sources.Identify(pkgConfig.PkgOpts.PackageSource)
if identifiedSrc == "" {
message.Debugf(lang.CmdPackageClusterSourceFallback, pkgConfig.PkgOpts.PackageSource)
src, err := sources.NewClusterSource(&pkgConfig.PkgOpts)
if err != nil {
return nil, fmt.Errorf("unable to identify source from %s: %w", pkgConfig.PkgOpts.PackageSource, err)
}
return src, nil
}
return nil, nil
}

func getPackageCompletionArgs(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
var pkgCandidates []string

Expand Down
115 changes: 115 additions & 0 deletions src/internal/packager2/inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package packager2 contains functions for inspecting packages.
package packager2

import (
"context"
"fmt"

"github.com/defenseunicorns/pkg/helpers/v2"
"github.com/zarf-dev/zarf/src/api/v1alpha1"
"github.com/zarf-dev/zarf/src/internal/packager/sbom"
"github.com/zarf-dev/zarf/src/pkg/cluster"
"github.com/zarf-dev/zarf/src/pkg/packager/filters"
)

// ZarfInspectOptions tracks the user-defined preferences during a package inspection.
type ZarfInspectOptions struct {
Source string
Cluster *cluster.Cluster
ViewSBOM bool
SBOMOutputDir string
ListImages bool
SkipSignatureValidation bool
PublicKeyPath string
}

// Inspect list the contents of a package.
func Inspect(ctx context.Context, opt ZarfInspectOptions) (v1alpha1.ZarfPackage, error) {
var err error
pkg, err := getPackageMetadata(ctx, opt)
if err != nil {
return pkg, err
}

if getSBOM(opt.ViewSBOM, opt.SBOMOutputDir) {
err = handleSBOMOptions(ctx, pkg, opt)
if err != nil {
return pkg, err
}
return pkg, nil
}
return pkg, nil
}

// InspectList lists the images in a component action
func InspectList(ctx context.Context, opt ZarfInspectOptions) ([]string, error) {
var imageList []string
pkg, err := getPackageMetadata(ctx, opt)
if err != nil {
return nil, err
}
// Only list images if we have have components
if len(pkg.Components) > 0 {
for _, component := range pkg.Components {
imageList = append(imageList, component.Images...)
}
if len(imageList) > 0 {
imageList = helpers.Unique(imageList)
return imageList, nil
}
return nil, fmt.Errorf("failed listing images: list of images found in components: %d", len(imageList))
}

return imageList, err
}

func getPackageMetadata(ctx context.Context, opt ZarfInspectOptions) (v1alpha1.ZarfPackage, error) {
pkg, err := packageFromSourceOrCluster(ctx, opt.Cluster, opt.Source, opt.SkipSignatureValidation, opt.PublicKeyPath)
if err != nil {
return pkg, err
}

return pkg, nil
}

func handleSBOMOptions(ctx context.Context, pkg v1alpha1.ZarfPackage, opt ZarfInspectOptions) error {
loadOpt := LoadOptions{
Source: opt.Source,
SkipSignatureValidation: opt.SkipSignatureValidation,
Filter: filters.Empty(),
PublicKeyPath: opt.PublicKeyPath,
}
layout, err := LoadPackage(ctx, loadOpt)
if err != nil {
return err
}
if opt.SBOMOutputDir != "" {
out, err := layout.SBOMs.OutputSBOMFiles(opt.SBOMOutputDir, pkg.Metadata.Name)
if err != nil {
return err
}
if opt.ViewSBOM {
err := sbom.ViewSBOMFiles(out)
if err != nil {
return err
}
}
} else if opt.ViewSBOM {
err := sbom.ViewSBOMFiles(layout.SBOMs.Path)
if err != nil {
return err
}
return err
}
return nil
}

func getSBOM(viewSBOM bool, SBOMOutputDir string) bool {
if viewSBOM || SBOMOutputDir != "" {
return true
}
return false
}
5 changes: 3 additions & 2 deletions src/internal/packager2/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func LoadPackage(ctx context.Context, opt LoadOptions) (*layout.PackagePaths, er
if err := sources.ValidatePackageIntegrity(pkgPaths, pkg.Metadata.AggregateChecksum, isPartial); err != nil {
return nil, err
}
if opt.SkipSignatureValidation {
if !opt.SkipSignatureValidation {
if err := sources.ValidatePackageSignature(ctx, pkgPaths, opt.PublicKeyPath); err != nil {
return nil, err
}
Expand Down Expand Up @@ -227,7 +227,7 @@ func assembleSplitTar(src, tarPath string) error {
return nil
}

func packageFromSourceOrCluster(ctx context.Context, cluster *cluster.Cluster, src string, skipSignatureValidation bool) (v1alpha1.ZarfPackage, error) {
func packageFromSourceOrCluster(ctx context.Context, cluster *cluster.Cluster, src string, skipSignatureValidation bool, publicKeyPath string) (v1alpha1.ZarfPackage, error) {
_, err := identifySource(src)
if err != nil {
if cluster == nil {
Expand All @@ -244,6 +244,7 @@ func packageFromSourceOrCluster(ctx context.Context, cluster *cluster.Cluster, s
Source: src,
SkipSignatureValidation: skipSignatureValidation,
Filter: filters.Empty(),
PublicKeyPath: publicKeyPath,
}
pkgPaths, err := LoadPackage(ctx, loadOpt)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions src/internal/packager2/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ func TestPackageFromSourceOrCluster(t *testing.T) {

ctx := testutil.TestContext(t)

_, err := packageFromSourceOrCluster(ctx, nil, "test", false)
_, err := packageFromSourceOrCluster(ctx, nil, "test", false, "")
require.EqualError(t, err, "cannot get Zarf package from Kubernetes without configuration")

pkg, err := packageFromSourceOrCluster(ctx, nil, "./testdata/zarf-package-test-amd64-0.0.1.tar.zst", false)
pkg, err := packageFromSourceOrCluster(ctx, nil, "./testdata/zarf-package-test-amd64-0.0.1.tar.zst", false, "")
require.NoError(t, err)
require.Equal(t, "test", pkg.Metadata.Name)

Expand All @@ -154,7 +154,7 @@ func TestPackageFromSourceOrCluster(t *testing.T) {
}
_, err = c.RecordPackageDeployment(ctx, pkg, nil, 1)
require.NoError(t, err)
pkg, err = packageFromSourceOrCluster(ctx, c, "test", false)
pkg, err = packageFromSourceOrCluster(ctx, c, "test", false, "")
require.NoError(t, err)
require.Equal(t, "test", pkg.Metadata.Name)
}
3 changes: 2 additions & 1 deletion src/internal/packager2/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ type RemoveOptions struct {
Cluster *cluster.Cluster
Filter filters.ComponentFilterStrategy
SkipSignatureValidation bool
PublicKeyPath string
}

// Remove removes a package that was already deployed onto a cluster, uninstalling all installed helm charts.
func Remove(ctx context.Context, opt RemoveOptions) error {
pkg, err := packageFromSourceOrCluster(ctx, opt.Cluster, opt.Source, opt.SkipSignatureValidation)
pkg, err := packageFromSourceOrCluster(ctx, opt.Cluster, opt.Source, opt.SkipSignatureValidation, opt.PublicKeyPath)
if err != nil {
return err
}
Expand Down
10 changes: 0 additions & 10 deletions src/test/e2e/00_use_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,6 @@ func TestUseCLI(t *testing.T) {
require.Greater(t, len(files), 1)
})

// TODO: Refactor test as it depends on debug log output for validation.
t.Run("zarf package inspect with tmpdir", func(t *testing.T) {
t.Parallel()
path := fmt.Sprintf("build/zarf-package-component-actions-%s.tar.zst", e2e.Arch)
tmpdir := t.TempDir()
stdOut, stdErr, err := e2e.Zarf(t, "package", "inspect", path, "--tmpdir", tmpdir, "--log-level=debug")
require.Contains(t, stdErr, tmpdir, "The other tmp path should show as being created")
require.NoError(t, err, stdOut, stdErr)
})

// TODO: Refactor test as it depends on debug log output for validation.
t.Run("zarf package deploy with tmpdir", func(t *testing.T) {
t.Parallel()
Expand Down
2 changes: 1 addition & 1 deletion src/test/e2e/11_oci_pull_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (suite *PullInspectTestSuite) Test_1_Remote_Inspect() {
// Test inspect on a public package.
// NOTE: This also makes sure that Zarf does not attempt auth when inspecting a public package.
ref := fmt.Sprintf("oci://ghcr.io/zarf-dev/packages/dos-games:1.0.0-%s", e2e.Arch)
_, stdErr, err = e2e.Zarf(suite.T(), "package", "inspect", ref)
_, stdErr, err = e2e.Zarf(suite.T(), "package", "inspect", ref, "--skip-signature-validation")
suite.NoError(err, stdErr)
}

Expand Down
11 changes: 5 additions & 6 deletions src/test/e2e/31_checksum_and_signature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ func TestChecksumAndSignature(t *testing.T) {
require.NoError(t, err, stdOut, stdErr)
defer e2e.CleanFiles(pkgName)

/* Test operations during package inspect */
// Test that we can inspect the yaml of the package without the private key
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", pkgName)
require.NoError(t, err, stdOut, stdErr)

// Test that we don't get an error when we remember to provide the public key
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", pkgName, publicKeyFlag)
require.NoError(t, err, stdOut, stdErr)
require.Contains(t, stdErr, "Verified OK")

/* Test operations during package inspect */
// Test that we can inspect the yaml of the package without the private key
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", pkgName, "--skip-signature-validation")
require.NoError(t, err, stdOut, stdErr)

/* Test operations during package deploy */
// Test that we get an error when trying to deploy a package without providing the public key
Expand Down
2 changes: 1 addition & 1 deletion src/test/e2e/34_custom_init_package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestCustomInit(t *testing.T) {

/* Test operations during package inspect */
// Test that we can inspect the yaml of the package without the private key
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", pkgName)
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", pkgName, "--skip-signature-validation")
require.NoError(t, err, stdOut, stdErr)

// Test that we don't get an error when we remember to provide the public key
Expand Down
4 changes: 2 additions & 2 deletions src/test/nightly/ecr_publish_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ func TestECRPublishing(t *testing.T) {
defer e2e.CleanFiles(testPackageFileName)

// Ensure we get a warning when trying to inspect the package without providing the public key
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", testPackageFileName)
// and the insecure flag
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", testPackageFileName, "--skip-signature-validation")
require.NoError(t, err, stdOut, stdErr)
require.NotContains(t, stdErr, "Validating SBOM checksums")
require.Contains(t, stdErr, "The package was signed but no public key was provided, skipping signature validation")

// Validate that we get no warnings when inspecting the package while providing the public key
stdOut, stdErr, err = e2e.Zarf(t, "package", "inspect", testPackageFileName, keyFlag)
Expand Down

0 comments on commit 5d4fc4b

Please sign in to comment.