diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 669f46394..606171411 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,7 +26,6 @@ repos: hooks: - id: golangci-lint-full args: [--timeout=5m] - linters: - repo: local hooks: - id: check-docs-and-schema diff --git a/src/cmd/uds.go b/src/cmd/uds.go index 914d34a73..f734800b8 100644 --- a/src/cmd/uds.go +++ b/src/cmd/uds.go @@ -198,7 +198,7 @@ func init() { inspectCmd.Flags().BoolVarP(&bundleCfg.InspectOpts.IncludeSBOM, "sbom", "s", false, lang.CmdPackageInspectFlagSBOM) inspectCmd.Flags().BoolVarP(&bundleCfg.InspectOpts.ExtractSBOM, "extract", "e", false, lang.CmdPackageInspectFlagExtractSBOM) inspectCmd.Flags().StringVarP(&bundleCfg.InspectOpts.PublicKeyPath, "key", "k", v.GetString(V_BNDL_INSPECT_KEY), lang.CmdBundleInspectFlagKey) - inspectCmd.Flags().BoolVarP(&bundleCfg.InspectOpts.ExtractImages, "list-images", "i", false, "Recursively go through all of the sbom's and get the images") + inspectCmd.Flags().BoolVarP(&bundleCfg.InspectOpts.ListImages, "list-images", "i", false, lang.CmdBundleInspectFlagFindImages) // remove cmd flags rootCmd.AddCommand(removeCmd) diff --git a/src/config/lang/lang.go b/src/config/lang/lang.go index ccd3bacc1..ff5ae8ef8 100644 --- a/src/config/lang/lang.go +++ b/src/config/lang/lang.go @@ -43,6 +43,7 @@ const ( CmdBundleInspectFlagKey = "Path to a public key file that will be used to validate a signed bundle" CmdPackageInspectFlagSBOM = "Create a tarball of SBOMs contained in the bundle" CmdPackageInspectFlagExtractSBOM = "Create a folder of SBOMs contained in the bundle" + CmdBundleInspectFlagFindImages = "Find all images in the bundle" // bundle remove CmdBundleRemoveShort = "Remove a bundle that has been deployed already" diff --git a/src/pkg/bundle/inspect.go b/src/pkg/bundle/inspect.go index bce340f24..e7a1c51e8 100644 --- a/src/pkg/bundle/inspect.go +++ b/src/pkg/bundle/inspect.go @@ -5,88 +5,43 @@ package bundle import ( - "bytes" - "os" - "os/exec" + "fmt" "path/filepath" "strings" + "github.com/defenseunicorns/pkg/oci" "github.com/defenseunicorns/uds-cli/src/config" "github.com/defenseunicorns/uds-cli/src/pkg/utils" + "github.com/defenseunicorns/zarf/src/pkg/layout" + "github.com/defenseunicorns/zarf/src/pkg/packager/filters" + zarfSources "github.com/defenseunicorns/zarf/src/pkg/packager/sources" zarfUtils "github.com/defenseunicorns/zarf/src/pkg/utils" - "github.com/defenseunicorns/zarf/src/pkg/message" - "github.com/defenseunicorns/zarf/src/pkg/utils" - "gopkg.in/yaml.v2" + "github.com/defenseunicorns/zarf/src/pkg/zoci" + zarfTypes "github.com/defenseunicorns/zarf/src/types" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) -func (b *Bundle) extractImagesFromPackages() { - for i, pkg := range b.bundle.Packages { - if pkg.Repository != "" && pkg.Ref != "" { - message.Debugf("Package: %s, Repository: %s, Ref: %s", pkg.Name, pkg.Repository, pkg.Ref) - type Component struct { - Images []string `yaml:"images"` - } - - type Output struct { - Components []Component `yaml:"components"` - } - - cmd := exec.Command("uds", "zarf", "package", "inspect", "oci://"+pkg.Repository+":"+pkg.Ref, "--no-color", "--no-log-file") - - var output bytes.Buffer - cmd.Stdout = &output - cmd.Stderr = &output - - err := cmd.Run() // Use Run instead of CombinedOutput - if err != nil { - message.Fatalf("Error executing command: %s, output: %s", err.Error(), output.String()) - continue - } - outputStr := output.String() - - // Find the index of "kind:" - idx := strings.Index(outputStr, "kind:") - if idx == -1 { - message.Fatalf("Invalid output: %s", outputStr) - continue - } - - // Trim the output - trimmedOutput := outputStr[idx:] - - message.Debugf("Trimmed Output: %s", trimmedOutput) - - var result interface{} - err = yaml.Unmarshal([]byte(trimmedOutput), &result) - if err != nil { - message.Fatalf("Error unmarshaling YAML: %s", err.Error()) - continue - } - - var allImages []string - findImages(result, &allImages) - - // Add the images to the package - if len(allImages) > 0 { - message.Debugf("Images: %v", allImages) - b.bundle.Packages[i].Images = allImages - } else { - message.Debugf("No images found in package %v", pkg.Name) - } - } - } -} - // Inspect pulls/unpacks a bundle's metadata and shows it func (b *Bundle) Inspect() error { // Check if the source is a YAML file - if filepath.Ext(b.cfg.InspectOpts.Source) == ".yaml" { + if b.cfg.InspectOpts.ListImages && filepath.Ext(b.cfg.InspectOpts.Source) == ".yaml" { // todo: or .yml source, err := CheckYAMLSourcePath(b.cfg.InspectOpts.Source) if err != nil { return err } b.cfg.InspectOpts.Source = source - return b.InspectYAML(b.cfg.InspectOpts.Source) + + // read the bundle's metadata into memory + if err := utils.ReadYAMLStrict(b.cfg.InspectOpts.Source, &b.bundle); err != nil { + return err + } + + imgs, err := b.extractImagesFromPackages() + if err != nil { + return err + } + fmt.Println(strings.Join(imgs, "\n")) + return nil } // Check that provided oci source path is valid, and update it if it's missing the full path @@ -124,13 +79,6 @@ func (b *Bundle) Inspect() error { if err := utils.ReadYAMLStrict(loaded[config.BundleYAML], &b.bundle); err != nil { return err } - // If ExtractImages flag is set, extract images from each package - if b.cfg.InspectOpts.ExtractImages { - message.Debugf("Extracting images from packages") - b.extractImagesFromPackages() - } else { - message.Debugf("Skipping image extraction") - } // show the bundle's metadata zarfUtils.ColorPrintYAML(b.bundle, nil, false) @@ -140,52 +88,62 @@ func (b *Bundle) Inspect() error { return nil } -// InspectYAML inspects a bundle from a YAML file -func (b *Bundle) InspectYAML(yamlPath string) error { - message.Debugf("Reading the yaml file: %s", yamlPath) - // Read the YAML file - data, err := os.ReadFile(yamlPath) - if err != nil { - return err - } +func (b *Bundle) extractImagesFromPackages() ([]string, error) { + imgMap := make(map[string]string) + for _, pkg := range b.bundle.Packages { + if pkg.Repository != "" && pkg.Ref != "" { - // Unmarshal the YAML data into the Bundle struct - err = yaml.Unmarshal(data, &b.bundle) - if err != nil { - return err - } + url := fmt.Sprintf("oci://%s:%s", pkg.Repository, pkg.Ref) + platform := ocispec.Platform{ + Architecture: config.GetArch(), + OS: oci.MultiOS, + } + remote, err := zoci.NewRemote(url, platform) + if err != nil { + return nil, err + } - // If ExtractImages flag is set, extract images from each package - if b.cfg.InspectOpts.ExtractImages { - b.extractImagesFromPackages() - } + source := zarfSources.OCISource{ + ZarfPackageOptions: &zarfTypes.ZarfPackageOptions{ + PublicKeyPath: "", + }, + Remote: remote, + } - // show the bundle's metadata - utils.ColorPrintYAML(b.bundle, nil, false) - return nil -} + tmpDir, err := zarfUtils.MakeTempDir(config.CommonOptions.TempDirectory) + if err != nil { + return nil, err + } + pkgPaths := layout.New(tmpDir) + zarfPkg, _, err := source.LoadPackageMetadata(pkgPaths, false, true) + if err != nil { + return nil, err + } + + // create filter for optional components + inspectFilter := filters.Combine( + filters.ForDeploy(strings.Join(pkg.OptionalComponents, ","), false), + ) -func findImages(node interface{}, images *[]string) { - switch node := node.(type) { - case map[interface{}]interface{}: - for k, v := range node { - if k == "images" { - // Check if v is a slice of interfaces - if imgSlice, ok := v.([]interface{}); ok { - // Convert each element to a string and append it to images - for _, img := range imgSlice { - if imgStr, ok := img.(string); ok { - *images = append(*images, imgStr) - } - } + filteredComponents, err := inspectFilter.Apply(zarfPkg) + if err != nil { + return nil, err + } + + // grab images from each filtered component + for _, component := range filteredComponents { + for _, img := range component.Images { + imgMap[img] = img } - } else { - findImages(v, images) } } - case []interface{}: - for _, v := range node { - findImages(v, images) - } } + + // convert img map to list of strings + var images []string + for _, img := range imgMap { + images = append(images, img) + } + + return images, nil } diff --git a/src/test/bundles/14-optional-components/uds-bundle.yaml b/src/test/bundles/14-optional-components/uds-bundle.yaml index 390733623..6142a3cc2 100644 --- a/src/test/bundles/14-optional-components/uds-bundle.yaml +++ b/src/test/bundles/14-optional-components/uds-bundle.yaml @@ -11,15 +11,15 @@ packages: ref: v0.34.0 # deploys prometheus as a required component and upload-image as an optional component (with noOptionalComponents key) - - name: prometheus - repository: localhost:888/prometheus - ref: 0.0.1 - optionalComponents: - - upload-image - - # deploys podinfo as an optional component and apache as a required component - - name: podinfo-nginx - path: ../../packages/podinfo-nginx - ref: 0.0.1 - optionalComponents: - - podinfo +# - name: prometheus +# repository: localhost:888/prometheus +# ref: 0.0.1 +# optionalComponents: +# - upload-image +# +# # deploys podinfo as an optional component and apache as a required component +# - name: podinfo-nginx +# path: ../../packages/podinfo-nginx +# ref: 0.0.1 +# optionalComponents: +# - podinfo diff --git a/src/test/e2e/commands_test.go b/src/test/e2e/commands_test.go index 250b0388a..cda25a574 100644 --- a/src/test/e2e/commands_test.go +++ b/src/test/e2e/commands_test.go @@ -16,9 +16,6 @@ import ( "testing" "github.com/defenseunicorns/pkg/helpers/v2" - "gopkg.in/yaml.v2" - - "github.com/defenseunicorns/pkg/helpers" "github.com/defenseunicorns/uds-cli/src/config" "github.com/defenseunicorns/zarf/src/pkg/message" ocispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -114,16 +111,6 @@ func inspectLocalAndSBOMExtract(t *testing.T, tarballPath string) { require.NoError(t, err) } -func inspectBundleYAML(t *testing.T, yamlPath string) { - cmd := strings.Split(fmt.Sprintf("inspect %s", yamlPath), " ") - _, _, err := e2e.UDS(cmd...) - data, err := os.ReadFile(yamlPath) - require.NoError(t, err) - var bundleData map[string]interface{} - err = yaml.Unmarshal(data, &bundleData) - require.NoError(t, err) -} - func deploy(t *testing.T, tarballPath string) (stdout string, stderr string) { cmd := strings.Split(fmt.Sprintf("deploy %s --retries 1 --confirm", tarballPath), " ") stdout, stderr, err := e2e.UDS(cmd...) diff --git a/src/types/options.go b/src/types/options.go index d420632d2..34d138e22 100644 --- a/src/types/options.go +++ b/src/types/options.go @@ -43,7 +43,7 @@ type BundleInspectOptions struct { Source string IncludeSBOM bool ExtractSBOM bool - ExtractImages bool + ListImages bool } // BundlePublishOptions is the options for the bundle.Publish() function