Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: packager inspect command #2990

Merged
merged 25 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading