-
Notifications
You must be signed in to change notification settings - Fork 10
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
feat: adds --list-images arg to inspect #599
Changes from all commits
c0ea98d
f51cd4b
0cdab7d
c04cc55
4af54fe
6da643d
9899da5
5b3e457
3473982
6d88815
e374b0f
9533f3b
09439ee
bc005b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -107,6 +107,13 @@ Inspect the `uds-bundle.yaml` of a bundle | |||||
1. From an OCI registry: `uds inspect oci://ghcr.io/defenseunicorns/dev/<name>:<tag>` | ||||||
1. From your local filesystem: `uds inspect uds-bundle-<name>.tar.zst` | ||||||
|
||||||
#### Viewing Images in a Bundle | ||||||
It is possible derive images from a `uds-bundle.yaml`. This can be useful for situations where you need to know what images will be bundled before you actually create the bundle. This is accomplished with the `--list-images`. For example: | ||||||
|
||||||
`uds inspect ./uds-bundle.yaml --list-images` | ||||||
|
||||||
This command will return a list of images derived from the bundle's packages and taking into account optional and required package components. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
#### Viewing SBOMs | ||||||
There are 2 additional flags for the `uds inspect` command you can use to extract and view SBOMs: | ||||||
- Output the SBOMs as a tar file: `uds inspect ... --sbom` | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,13 +5,48 @@ | |
package bundle | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"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/zoci" | ||
zarfTypes "github.com/defenseunicorns/zarf/src/types" | ||
"github.com/fatih/color" | ||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/pterm/pterm" | ||
) | ||
|
||
// Inspect pulls/unpacks a bundle's metadata and shows it | ||
func (b *Bundle) Inspect() error { | ||
// handle --list-images flag | ||
if b.cfg.InspectOpts.ListImages { | ||
err := utils.CheckYAMLSourcePath(b.cfg.InspectOpts.Source) | ||
if err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit, but is there a particular reason to have the err check separate on this one but "inlined" (if that's the right word) for the next call. just wondering if we can make them all look the same for consistency. Also, do you think there's any value in extracting the list image functions into a separate function |
||
return err | ||
} | ||
|
||
if err := utils.ReadYAMLStrict(b.cfg.InspectOpts.Source, &b.bundle); err != nil { | ||
return err | ||
} | ||
|
||
// find images in the packages taking into account optional components | ||
imgs, err := b.getPackageImages() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
formattedImgs := pterm.Color(color.FgHiMagenta).Sprintf(strings.Join(imgs, "\n")) | ||
pterm.Printfln("\n%s\n", formattedImgs) | ||
return nil | ||
} | ||
|
||
// Check that provided oci source path is valid, and update it if it's missing the full path | ||
source, err := CheckOCISourcePath(b.cfg.InspectOpts.Source) | ||
|
@@ -52,7 +87,84 @@ func (b *Bundle) Inspect() error { | |
// show the bundle's metadata | ||
zarfUtils.ColorPrintYAML(b.bundle, nil, false) | ||
|
||
// TODO: showing package metadata? | ||
// TODO: could be cool to have an interactive mode that lets you select a package and show its metadata | ||
return nil | ||
} | ||
|
||
func (b *Bundle) getPackageImages() ([]string, error) { | ||
// use a map to track the images for easy de-duping | ||
imgMap := make(map[string]string) | ||
|
||
for _, pkg := range b.bundle.Packages { | ||
// get package source | ||
var source zarfSources.PackageSource | ||
if pkg.Repository != "" { | ||
// handle remote packages | ||
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 | ||
} | ||
|
||
source = &zarfSources.OCISource{ | ||
ZarfPackageOptions: &zarfTypes.ZarfPackageOptions{}, | ||
Remote: remote, | ||
} | ||
} else if pkg.Path != "" { | ||
// handle local packages | ||
err := os.Chdir(filepath.Dir(b.cfg.InspectOpts.Source)) // change to the bundle's directory | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
bundleArch := config.GetArch(b.bundle.Metadata.Architecture) | ||
tarballName := fmt.Sprintf("zarf-package-%s-%s-%s.tar.zst", pkg.Name, bundleArch, pkg.Ref) | ||
source = &zarfSources.TarballSource{ | ||
ZarfPackageOptions: &zarfTypes.ZarfPackageOptions{ | ||
PackageSource: filepath.Join(pkg.Path, tarballName), | ||
}, | ||
} | ||
} else { | ||
return nil, fmt.Errorf("package %s is missing a repository or path", pkg.Name) | ||
} | ||
|
||
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), | ||
) | ||
|
||
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 | ||
} | ||
} | ||
|
||
} | ||
|
||
// convert img map to list of strings | ||
var images []string | ||
for _, img := range imgMap { | ||
images = append(images, img) | ||
} | ||
|
||
return images, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,8 +91,10 @@ func ConfigureLogs(cmd *cobra.Command) error { | |
return err | ||
} | ||
|
||
// use Zarf pterm output | ||
message.Notef("Saving log file to %s", tmpLogLocation) | ||
// don't print the note for inspect cmds because they are used in automation | ||
if !strings.Contains(cmd.Use, "inspect") { | ||
message.Notef("Saving log file to %s", tmpLogLocation) | ||
} | ||
return nil | ||
} | ||
|
||
|
@@ -194,3 +196,18 @@ func ReadYAMLStrict(path string, destConfig any) error { | |
} | ||
return nil | ||
} | ||
|
||
// CheckYAMLSourcePath checks if the provided YAML source path is valid | ||
func CheckYAMLSourcePath(source string) error { | ||
// check if the source is a YAML file | ||
isYaml := strings.HasSuffix(source, ".yaml") || strings.HasSuffix(source, ".yml") | ||
if !isYaml { | ||
return fmt.Errorf("source must have .yaml or yml file extension") | ||
} | ||
// Check if the file exists | ||
if _, err := os.Stat(source); os.IsNotExist(err) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks good to me, but also wondering if we want to leverage the |
||
return fmt.Errorf("file %s does not exist", source) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,7 @@ type UDSBundle struct { | |
type Package struct { | ||
Name string `json:"name" jsonschema:"name=Name of the Zarf package"` | ||
Description string `json:"description,omitempty" jsonschema:"description=Description of the Zarf package"` | ||
Images []string `json:"images,omitempty" jsonschema:"description=List of images included in the Zarf package"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this getting used somewhere? having trouble understanding how it's being used / set. |
||
Repository string `json:"repository,omitempty" jsonschema:"description=The repository to import the package from"` | ||
Path string `json:"path,omitempty" jsonschema:"description=The local path to import the package from"` | ||
Ref string `json:"ref" jsonschema:"description=Ref (tag) of the Zarf package"` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.