-
Notifications
You must be signed in to change notification settings - Fork 23
chore: Add crane pipeline that checks image environment #234
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
Open
EyeCantCU
wants to merge
1
commit into
chainguard-dev:main
Choose a base branch
from
EyeCantCU:crane
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| PROJECT = crane-check | ||
| MELANGE_CONTEXTDIR ?= /tmp/melange-context/$(PROJECT) | ||
| MELANGE_INSTALL_PATH = $(MELANGE_CONTEXTDIR)/usr/bin | ||
|
|
||
| .PHONY: build tidy vet test clean melange-install | ||
|
|
||
| build: | ||
| go build -o crane-check | ||
|
|
||
| test: | ||
| go test -v ./... | ||
|
|
||
| tidy: | ||
| go mod tidy | ||
|
|
||
| vet: | ||
| go vet ./... | ||
|
|
||
| clean: | ||
| rm -f crane-check | ||
|
|
||
| melange-install: build | ||
| mkdir -p $(MELANGE_INSTALL_PATH) | ||
| install -Dm755 crane-check $(MELANGE_INSTALL_PATH)/crane-check |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| module chainguard.dev/tw/crane-check | ||
|
|
||
| go 1.25 | ||
|
|
||
| require ( | ||
| github.com/chainguard-dev/clog v1.7.0 | ||
| github.com/google/go-containerregistry v0.20.6 | ||
| github.com/spf13/cobra v1.10.1 | ||
| ) | ||
|
|
||
| require ( | ||
| github.com/containerd/stargz-snapshotter/estargz v0.18.0 // indirect | ||
| github.com/docker/cli v28.5.2+incompatible // indirect | ||
| github.com/docker/distribution v2.8.3+incompatible // indirect | ||
| github.com/docker/docker-credential-helpers v0.9.4 // indirect | ||
| github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||
| github.com/klauspost/compress v1.18.1 // indirect | ||
| github.com/mitchellh/go-homedir v1.1.0 // indirect | ||
| github.com/opencontainers/go-digest v1.0.0 // indirect | ||
| github.com/opencontainers/image-spec v1.1.1 // indirect | ||
| github.com/pkg/errors v0.9.1 // indirect | ||
| github.com/sirupsen/logrus v1.9.3 // indirect | ||
| github.com/spf13/pflag v1.0.10 // indirect | ||
| github.com/vbatts/tar-split v0.12.2 // indirect | ||
| golang.org/x/sync v0.17.0 // indirect | ||
| golang.org/x/sys v0.37.0 // indirect | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| github.com/chainguard-dev/clog v1.7.0 h1:guPznsK8vLHvzz1QJe2yU6MFeYaiSOFOQBYw4OXu+g8= | ||
| github.com/chainguard-dev/clog v1.7.0/go.mod h1:4+WFhRMsGH79etYXY3plYdp+tCz/KCkU8fAr0HoaPvs= | ||
| github.com/containerd/stargz-snapshotter/estargz v0.18.0 h1:Ny5yptQgEXSkDFKvlKJGTvf1YJ+4xD8V+hXqoRG0n74= | ||
| github.com/containerd/stargz-snapshotter/estargz v0.18.0/go.mod h1:7hfU1BO2KB3axZl0dRQCdnHrIWw7TRDdK6L44Rdeuo0= | ||
| github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= | ||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
| github.com/docker/cli v28.5.2+incompatible h1:XmG99IHcBmIAoC1PPg9eLBZPlTrNUAijsHLm8PjhBlg= | ||
| github.com/docker/cli v28.5.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= | ||
| github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= | ||
| github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= | ||
| github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= | ||
| github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= | ||
| github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= | ||
| github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= | ||
| github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= | ||
| github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= | ||
| github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | ||
| github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||
| github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= | ||
| github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= | ||
| github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= | ||
| github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||
| github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= | ||
| github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= | ||
| github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= | ||
| github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= | ||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
| github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
| github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= | ||
| github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||
| github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= | ||
| github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= | ||
| github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
| github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= | ||
| github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
| github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= | ||
| github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= | ||
| golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= | ||
| golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= | ||
| golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
| golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= | ||
| golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= | ||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
| gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= | ||
| gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "os" | ||
| "strings" | ||
|
|
||
| "github.com/chainguard-dev/clog" | ||
| "github.com/google/go-containerregistry/pkg/crane" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| type cfg struct { | ||
| Image string | ||
| Environment string | ||
| MatchMode string | ||
| } | ||
|
|
||
| type ImageConfig struct { | ||
| Config struct { | ||
| Env []string `json:"Env"` | ||
| } `json:"config"` | ||
| } | ||
|
|
||
| var validMatchModes = []string{"exact", "prefix", "relative", "contains"} | ||
|
|
||
| func main() { | ||
| cmd := Command() | ||
| if err := cmd.Execute(); err != nil { | ||
| clog.ErrorContextf(cmd.Context(), "failed to execute command: %v", err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
|
|
||
| func isValidMatchMode(mode string) bool { | ||
| for _, valid := range validMatchModes { | ||
| if mode == valid { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| func Command() *cobra.Command { | ||
| cfg := &cfg{} | ||
|
|
||
| cmd := &cobra.Command{ | ||
| Use: "crane-check", | ||
| Short: "Check image config using crane", | ||
| Long: `Tool that evaluates image configuration using crane. | ||
|
|
||
| The image to evaluate is simply passed via the '--image' argument (required) | ||
|
|
||
| Environment variables (required, --env) can be specified in two formats: | ||
| - Space-separated: ENV_VAR1=value1 ENV_VAR2=value2 | ||
| - Newline-separated (useful for multi-line input) | ||
|
|
||
| Match modes (--match): | ||
| - exact: Provided value and image value must match exactly (default) | ||
| - prefix: Provided value is a prefix of the image value | ||
| - relative: Either value is a prefix of the other | ||
| - contains: The provided value is a substring within the image value | ||
|
|
||
| Usage: | ||
| crane-check --image=cgr.dev/chainguard/go:latest --env="GOPATH=/go GOROOT=/usr/lib/go" | ||
| crane-check --image=alpine:latest --env="PATH=/usr/lib" --match=relative`, | ||
| // We don't want to print usage every single time there's an error | ||
| SilenceUsage: true, | ||
| // We don't want duplicate error output | ||
| SilenceErrors: true, | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| // Throw an error early if we provide an invalid match mode | ||
| if !isValidMatchMode(cfg.MatchMode) { | ||
| return fmt.Errorf("invalid match-mode: %s (must be one of: %s)", cfg.MatchMode, strings.Join(validMatchModes, ", ")) | ||
| } | ||
| return cfg.Run(cmd.Context()) | ||
| }, | ||
| } | ||
|
|
||
| cmd.Flags().StringVar(&cfg.Image, "image", "", "Image to be assessed (e.g., cgr.dev/chainguard/crane:latest) [required]") | ||
| cmd.Flags().StringVar(&cfg.Environment, "env", "", "Environment variables to check (space or newline-separated, format: ENV_VAR=VALUE) [required]") | ||
| cmd.Flags().StringVar(&cfg.MatchMode, "match", "exact", "Match mode: 'exact', 'relative', or 'contains'") | ||
|
|
||
| cmd.MarkFlagRequired("image") | ||
| cmd.MarkFlagRequired("env") | ||
|
|
||
| return cmd | ||
| } | ||
|
|
||
| func (c *cfg) Run(ctx context.Context) error { | ||
| log := clog.FromContext(ctx).With("image", c.Image) | ||
|
|
||
| // Parse passed environment variables | ||
| envVars := parseEnvironmentVariables(c.Environment) | ||
| if envVars == nil { | ||
| return fmt.Errorf("no environment variables provided") | ||
| } | ||
| log.InfoContext(ctx, "retrieving image configuration") | ||
|
|
||
| // Get the image config with crane | ||
| imageConfig, err := getCraneConfig(ctx, c.Image) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to get image configuration: %w", err) | ||
| } | ||
|
|
||
| // Parse the config | ||
| var imgCfg ImageConfig | ||
| if err := json.Unmarshal([]byte(imageConfig), &imgCfg); err != nil { | ||
| return fmt.Errorf("failed to parse image configuration: %w", err) | ||
| } | ||
|
|
||
| // Ensure environment exists in config | ||
| if imgCfg.Config.Env == nil { | ||
| return fmt.Errorf("failed to parse environment from image config") | ||
| } | ||
| log.InfoContext(ctx, "successfully parsed environment from image config") | ||
|
|
||
| // Check each environment variable | ||
| for _, envVar := range envVars { | ||
| if err := matchEnvironmentVariable(ctx, envVar, imgCfg.Config.Env, c.MatchMode); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| log.InfoContext(ctx, "all environment variables match expected values") | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func parseEnvironmentVariables(envStr string) []string { | ||
| envStr = strings.TrimSpace(envStr) | ||
| if envStr == "" { | ||
| return nil | ||
| } | ||
|
|
||
| // Attempt to split input via newlines first | ||
| // I want newline separation to "just" work in our pipelines | ||
| lines := strings.Split(envStr, "\n") | ||
| var envVars []string | ||
|
|
||
| for _, line := range lines { | ||
| line = strings.TrimSpace(line) | ||
|
|
||
| // Ignore commented out lines and skip empty ones | ||
| if line == "" || strings.HasPrefix(line, "#") { | ||
| continue | ||
| } | ||
|
|
||
| // Account for space separated env vars | ||
| if strings.Contains(line, " ") && strings.Count(line, "=") > 1 { | ||
| envs := strings.Fields(line) | ||
| envVars = append(envVars, envs...) | ||
| } else { | ||
| envVars = append(envVars, line) | ||
| } | ||
| } | ||
|
|
||
| return envVars | ||
| } | ||
|
|
||
| func getCraneConfig(ctx context.Context, image string) (string, error) { | ||
| log := clog.FromContext(ctx) | ||
|
|
||
| // Get image config with crane | ||
| configJSON, err := crane.Config(image, crane.WithContext(ctx)) | ||
| if err != nil { | ||
| log.ErrorContextf(ctx, "crane config failed: %v", err) | ||
| return "", fmt.Errorf("crane config failed: %w", err) | ||
| } | ||
|
|
||
| return string(configJSON), nil | ||
| } | ||
|
|
||
| func matchEnvironmentVariable(ctx context.Context, envStr string, imageEnv []string, matchMode string) error { | ||
| log := clog.FromContext(ctx) | ||
|
|
||
| // Split env var and value | ||
| parts := strings.SplitN(envStr, "=", 2) | ||
| if len(parts) != 2 { | ||
| return fmt.Errorf("invalid environment variable format: %s (expected KEY=VALUE)", envStr) | ||
| } | ||
|
|
||
| envVar := parts[0] | ||
| envVal := parts[1] | ||
|
|
||
| // Make sure environment variable is in image config | ||
| var foundEnv string | ||
| found := false | ||
| for _, imageEnvVar := range imageEnv { | ||
| if strings.HasPrefix(imageEnvVar, envVar+"=") { | ||
| foundEnv = imageEnvVar | ||
| found = true | ||
| break | ||
| } | ||
| } | ||
|
|
||
| if !found { | ||
| return fmt.Errorf("failed to find %s in image config", envVar) | ||
| } | ||
| log.InfoContext(ctx, "found variable in image config", "var", envVar) | ||
|
|
||
| // Now split the env from the image config | ||
| imageParts := strings.SplitN(foundEnv, "=", 2) | ||
| imageVal := "" | ||
| if len(imageParts) == 2 { | ||
| imageVal = imageParts[1] | ||
| } | ||
|
|
||
| matched := false | ||
| switch matchMode { | ||
| case "exact": | ||
| matched = envVal == imageVal | ||
| case "prefix": | ||
| matched = strings.HasPrefix(imageVal, envVal) | ||
| case "relative": | ||
| matched = strings.HasPrefix(imageVal, envVal) || strings.HasPrefix(envVal, imageVal) | ||
| case "contains": | ||
| matched = strings.Contains(imageVal, envVal) | ||
| default: | ||
| // We should never ever reach this point, but if we do... | ||
| return fmt.Errorf("unsupported match mode: %s", matchMode) | ||
| } | ||
|
|
||
| if !matched { | ||
| return fmt.Errorf(`invalid value provided for %s: | ||
| Provided value: %s | ||
| Image value: %s | ||
| Match mode: %s`, envVar, envVal, imageVal, matchMode) | ||
| } | ||
| log.InfoContext(ctx, "environment variable successfully matched", "var", envVar, "value", envVal, "match", matchMode) | ||
|
|
||
| return nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| go 1.25 | ||
|
|
||
| use ( | ||
| ./crane-check | ||
| ./gosh | ||
| ./no-docs-check | ||
| ./package-type-check | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This will always be checking the
amd64image by default, which we might be okay with? We could plumb an arch flag down to this as well in case we want to check thearm64variant?