-
Notifications
You must be signed in to change notification settings - Fork 52
OCPCLOUD-3327: Rewrite manifests-gen to support upgrade safety #434
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
base: main
Are you sure you want to change the base?
Conversation
|
Pipeline controller notification For optional jobs, comment This repository is configured in: LGTM mode |
📝 WalkthroughWalkthroughThreads provider image metadata into startup and reconciler wiring, adds a providerimages package to fetch/cache provider manifests from container images, replaces legacy manifest pipeline with a Go-based manifests-generator using typed client.Object, and updates RBAC and deployment to allow reading cached provider manifests. Changes
Sequence Diagram(s)sequenceDiagram
participant Operator as Cluster CAPI Operator
participant K8s as Kubernetes API
participant Registry as Container Registry
participant Disk as Local Disk Cache
participant Reconciler as CAPI Installer Reconciler
Operator->>K8s: Read pull-secret (openshift-config)
K8s-->>Operator: Return pull-secret
Operator->>Registry: Fetch provider image (using keychain)
Registry-->>Operator: Return image layers
Operator->>Operator: Extract metadata.yaml & manifests.yaml
Operator->>Disk: Write manifests to providerImageDir/<sanitized-ref>/
Operator->>Operator: Compute ContentID (SHA256)
Operator->>Reconciler: Pass ProviderImages metadata & paths
Reconciler->>Disk: Read cached manifests
Reconciler->>K8s: Apply manifests
K8s-->>Reconciler: Apply results
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Cache: Disabled due to data retention organization setting Knowledge base: Disabled due to data retention organization setting 📒 Files selected for processing (3)
🧰 Additional context used🧬 Code graph analysis (2)pkg/providerimages/providerimages_test.go (1)
pkg/controllers/capiinstaller/capi_installer_controller.go (1)
🔇 Additional comments (15)
✏️ Tip: You can disable this entire section by setting Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.5.0)Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions Comment |
|
Skipping CI for Draft Pull Request. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
@mdbooth: This pull request references OCPCLOUD-3327 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/test e2e-aws-ovn-techpreview |
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 |
1ab7f33 to
5087e3b
Compare
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 |
|
/test e2e-aws-ovn-techpreview |
dab2c52 to
7c0b761
Compare
f3ba06c to
a8a93e3
Compare
|
/test e2e-aws-ovn-techpreview |
|
/test e2e-aws-ovn-techpreview |
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
2 similar comments
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
|
The e2e-aws-ovn-techpreview pass shows the modifications to the capiinstaller controller are backwards compatible with current transport configmaps. openshift/cluster-api-provider-aws#584 and openshift/cluster-api#259 update CAPA and core CAPI to publish manifests only in the provider images. They do not publish transport configmaps. The multi-pr test pass with these 2 PRs shows that pulling manifests from images is working. |
|
@mdbooth: Overrode contexts on behalf of mdbooth: ci/prow/okd-scos-images DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
The existing tests will demonstrate that the PR continues to work with existing transport configmaps. The following test will demonstrate that the PR works with updated providers which use image-based manifests. /testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
|
/pipeline required |
|
Scheduling tests matching the |
|
/retest-required |
1 similar comment
|
/retest-required |
|
/test e2e-azure-ovn-techpreview |
|
@mdbooth: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
The bump to x/tools v0.39.0 changes the behaviour of the gosec linter, which no longer complains about these valid conversions.
|
@mdbooth: This pull request references OCPCLOUD-3327 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
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.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
manifests-gen/go.mod (1)
3-6: Reconcile toolchain Go version inconsistency betweenmanifests-genandhack/toolssubmodules.The Go 1.24.0 requirement is justified—the codebase uses
slices.Collect()at line 223 ofcmd/cluster-capi-operator/main.go, which requires Go 1.24+. However,manifests-gen/go.modpinstoolchain go1.24.4whilehack/tools/go.modpinstoolchain go1.24.10. Consider updatingmanifests-gento match the newer toolchain version used elsewhere in the repo for consistency.The
replace github.com/openshift/cluster-capi-operator => ..directive is appropriate for this submodule and the project uses.ci-operator.yaml(OpenShift CI), which should have Go 1.24+ support. Note thatdocker/cli v29.0.3appears as an indirect dependency inmanifests-gen/go.mod, inherited fromgoogle/go-containerregistry.
🤖 Fix all issues with AI agents
In @cmd/cluster-capi-operator/main.go:
- Around line 218-230: Replace the current unconditional os.Exit on provider
image read failures by using a signal-aware startup context and a bounded
timeout: create a single ctx via ctrl.SetupSignalHandler() (use it for manager
startup), wrap providerimages.ReadProviderImages with context.WithTimeout to
enforce a deadline, call providerimages.ReadProviderImages(ctxWithTimeout, ...)
instead of context.Background(), and on error log the failure (including err)
but do not call os.Exit—leave providerImages nil so the legacy ConfigMap path
can proceed; apply the same pattern to the other provider image read sites (the
calls around the blocks that set providerImages and similar reads at 231-243 and
259-263).
In @go.mod:
- Line 12: The go.mod update pins github.com/docker/cli to v29.0.3 while
go-containerregistry is v0.20.7 (tested against docker v28.x); verify
compatibility by testing the combination in staging focusing on daemon API
usage, content trust and cgroup behavior, and if any breakage appears pin
github.com/docker/cli back to the v28.x series in go.mod (or add a go.mod
replace) to match go-containerregistry’s tested environment until you can
upgrade go-containerregistry or validate v29; run your container
registry-related integration tests and CI to confirm no runtime/API regressions.
In @pkg/controllers/capiinstaller/capi_installer_controller.go:
- Around line 181-183: When reconcileProviderImages(ctx, log) returns an error,
call r.setDegradedCondition(ctx, "ProviderImagesReconcileFailed", err) (or the
same setDegradedCondition signature used for ConfigMap errors) before returning
so the controller sets the degraded condition consistently; mirror the pattern
used for ConfigMap processing errors (the calls around ConfigMap handling) and
include the error in the degraded condition message, then return the wrapped
error as currently done.
🧹 Nitpick comments (6)
manifests-gen/util.go (1)
21-25: UsemustConvertcarefully in non-initialization code paths.The
mustConvertfunction panics on conversion errors, which is appropriate for programming errors (type mismatches) but should be avoided in runtime reconciliation loops where conversion failures could occur due to malformed resources. Ensure this function is only used in contexts where conversion is guaranteed to succeed (e.g., well-known internal types, initialization code).pkg/providerimages/pullsecret.go (2)
29-40: Consider treating “missing pull-secret key” differently than “empty pull-secret”.
len(pullSecret)==0returningauthn.DefaultKeychaincan silently hide a misconfigured Secret (e.g., missing.dockerconfigjson). If you want stricter behavior, have the caller distinguish between “Secret key absent” vs “present but empty”.
48-75: Make auth lookup more robust: avoid struct-equality emptiness checks; consider alternate registry keys.Two nits that can turn into hard-to-diagnose auth failures:
cfg == (types.AuthConfig{})after mutatingServerAddressis fragile; better to explicitly check the credential/token fields you actually use.- Some docker configs store registry keys with a scheme/prefix; a fallback lookup (e.g., try
key, thenhttps://variants) would improve compatibility.Proposed adjustment (field-based emptiness + fallback keys)
func (k *configFileKeychain) Resolve(resource authn.Resource) (authn.Authenticator, error) { // Try to get auth config for the registry key := resource.RegistryStr() if key == name.DefaultRegistry { key = authn.DefaultAuthKey } - cfg, err := k.cf.GetAuthConfig(key) - if err != nil { - return nil, fmt.Errorf("failed to get auth config for %s: %w", key, err) - } + cfg, err := k.cf.GetAuthConfig(key) + if err != nil { + return nil, fmt.Errorf("failed to get auth config for %s: %w", key, err) + } + // Fallback for configs that include a scheme in the auth key. + if cfg == (types.AuthConfig{}) && !strings.HasPrefix(key, "https://") && !strings.HasPrefix(key, "http://") { + if alt, altErr := k.cf.GetAuthConfig("https://" + key); altErr == nil { + cfg = alt + } + } - // Check if we got an empty config - empty := types.AuthConfig{} - cfg.ServerAddress = "" // Clear for comparison - - if cfg == empty { + if cfg.Username == "" && cfg.Password == "" && cfg.Auth == "" && cfg.IdentityToken == "" && cfg.RegistryToken == "" { return authn.Anonymous, nil } return authn.FromConfig(authn.AuthConfig{ Username: cfg.Username, Password: cfg.Password, Auth: cfg.Auth, IdentityToken: cfg.IdentityToken, RegistryToken: cfg.RegistryToken, }), nil }(You’d need to add
stringsto imports.)manifests-gen/main.go (2)
21-47:allowedPlatformTypesis fine, but it will drift—add a guardrail.Given this list is manually curated, consider adding a small unit test (or a comment pointing to the upstream enum source) so future platform additions don’t silently reject new values.
111-147: Validation flow is solid; improve error detail for invalid semver and output to stderr.Two small tweaks:
- Include the semantic parse error (
%w) so callers know why it failed.- Print validation/generation errors to stderr.
Proposed tweak
if err := validateFlags(opts); err != nil { - fmt.Println(err) + fmt.Fprintln(os.Stderr, err) os.Exit(1) } if err := generateManifests(opts); err != nil { - fmt.Println(err) + fmt.Fprintln(os.Stderr, err) os.Exit(1) } @@ if opts.version != "" { if _, err := version.ParseSemantic(opts.version); err != nil { - return fmt.Errorf("invalid version %s for provider %s", opts.version, opts.name) + return fmt.Errorf("invalid version %q for provider %q: %w", opts.version, opts.name, err) } } return nil }(),pkg/providerimages/providerimages_test.go (1)
63-93: Minor test determinism nit: tar file entry order depends on map iteration.Probably fine (logic likely looks up by path), but if you ever debug layer contents or add logic that depends on traversal order, consider sorting
pathkeys before writing headers.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
⛔ Files ignored due to path filters (266)
e2e/go.sumis excluded by!**/*.sumgo.sumis excluded by!**/*.sumgo.work.sumis excluded by!**/*.sumhack/tools/go.sumis excluded by!**/*.summanifests-gen/go.sumis excluded by!**/*.sumvendor/github.com/MakeNowJust/heredoc/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/MakeNowJust/heredoc/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/MakeNowJust/heredoc/heredoc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/AUTHORSis excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/CONTRIBUTORSis excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/PATENTSis excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/bitcurves/bitcurve.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/brainpool/brainpool.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/brainpool/rcurve.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/eax/eax.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/eax/eax_test_vectors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/eax/random_vectors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/internal/byteutil/byteutil.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/ocb/ocb.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/ocb/random_vectors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_a.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/ocb/rfc7253_test_vectors_suite_b.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/aes/keywrap/keywrap.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/armor/armor.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/armor/encode.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/canonical_text.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/ecdh/ecdh.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/ecdsa/ecdsa.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/eddsa/eddsa.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/aead.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/cipher.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/algorithm/hash.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve25519.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curve_info.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/curves.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed25519.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/ed448.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/generic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/ecc/x448.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/encoding.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/mpi.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/internal/encoding/oid.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/key_generation.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/keys.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/keys_test_data.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_config.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_crypter.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/aead_encrypted.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/compressed.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/encrypted_key.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/literal.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/notation.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/ocfb.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/one_pass_signature.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/opaque.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/packet.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/private_key_test_data.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key_test_data.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/reader.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetric_key_encrypted.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_aead.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/symmetrically_encrypted_mdc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userattribute.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/packet/userid.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/read.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/read_write_test_data.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/s2k/s2k.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/ProtonMail/go-crypto/openpgp/write.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/CODE_OF_CONDUCT.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/CONTRIBUTING.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/base_dirs.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/codecov.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/internal/pathutil/pathutil.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/internal/pathutil/pathutil_plan9.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/internal/pathutil/pathutil_unix.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/internal/pathutil/pathutil_windows.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/internal/userdirs/config_unix.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/internal/userdirs/userdirs.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/paths_darwin.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/paths_plan9.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/paths_unix.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/paths_windows.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/adrg/xdg/xdg.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/curve.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.his excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/curve_generic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/curve_noasm.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/key.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x25519/table.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/curve.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.his excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/curve_generic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/curve_noasm.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/key.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/dh/x448/table.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/constants.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/curve.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/isogeny.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/point.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/scalar.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/twist.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/twistPoint.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/twistTables.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/ecc/goldilocks/twist_basemult.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/conv/conv.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/hashes.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/keccakf.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/rc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/sha3.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/sha3_s390x.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/shake.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/xor.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/xor_generic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/internal/sha3/xor_unaligned.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp25519/fp.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.his excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp25519/fp_generic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp25519/fp_noasm.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fp.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.his excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.sis excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fp_generic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fp_noasm.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/fp448/fuzzer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/integer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/mlsbset/mlsbset.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/mlsbset/power.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/primes.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/math/wnaf.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/ed25519.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/modular.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/mult.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/point.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/pubkey.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/pubkey112.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/signapi.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed25519/tables.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed448/ed448.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/ed448/signapi.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/cloudflare/circl/sign/sign.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/stargz-snapshotter/estargz/build.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/containerd/stargz-snapshotter/estargz/types.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/.gitattributesis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/.gitignoreis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/.golangci.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/CODE-OF-CONDUCT.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/CONTRIBUTING.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/GOVERNANCE.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/MAINTAINERSis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/Makefileis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/SECURITY.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/distribution-logo.svgis excluded by!**/*.svg,!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/helpers.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/normalize.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/reference.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/regexp.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/distribution/reference/sort.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/AUTHORSis excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/NOTICEis excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/config.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/configfile/file.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/configfile/file_unix.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/configfile/file_windows.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/credentials.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/default_store.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/default_store_darwin.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/default_store_linux.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/default_store_unsupported.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/default_store_windows.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/file_store.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/credentials/native_store.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/memorystore/store.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/cli/cli/config/types/authconfig.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/distribution/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/client/client.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/client/command.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/credentials/credentials.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/credentials/error.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/credentials/helper.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/docker/docker-credential-helpers/credentials/version.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/and/and_closer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/compression/compression.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/estargz/estargz.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/gzip/zip.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/redact/redact.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/retry/retry.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/retry/wait/kubernetes_apimachinery_wait.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/verify/verify.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/internal/zstd/zstd.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/anon.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/auth.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/authn.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/basic.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/bearer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/keychain.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/authn/multikeychain.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/compression/compression.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/logs/logs.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/check.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/digest.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/errors.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/options.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/ref.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/registry.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/repository.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/name/tag.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/config.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/empty/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/empty/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/empty/image.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/empty/index.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/hash.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/image.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/index.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/layer.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/manifest.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/match/match.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/partial/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/partial/compressed.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/partial/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/partial/image.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/google/go-containerregistry/pkg/v1/partial/index.gois excluded by!**/vendor/**,!vendor/**
📒 Files selected for processing (23)
cmd/cluster-capi-operator/main.goe2e/go.modgo.modhack/tools/go.modmanifests-gen/customizations.gomanifests-gen/generate.gomanifests-gen/go.modmanifests-gen/kustomization.yamlmanifests-gen/main.gomanifests-gen/providercustomizations.gomanifests-gen/providers.gomanifests-gen/util.gomanifests/0000_30_cluster-api_03_rbac_roles.yamlmanifests/0000_30_cluster-api_04_rbac_bindings.yamlmanifests/0000_30_cluster-api_11_deployment.yamlpkg/controllers/capiinstaller/capi_installer_controller.gopkg/controllers/capiinstaller/capi_installer_controller_test.gopkg/controllers/infracluster/azure.gopkg/conversion/capi2mapi/aws.gopkg/providerimages/providerimages.gopkg/providerimages/providerimages_test.gopkg/providerimages/pullsecret.gopkg/util/readconfig.go
💤 Files with no reviewable changes (3)
- manifests-gen/providers.go
- pkg/util/readconfig.go
- manifests-gen/providercustomizations.go
✅ Files skipped from review due to trivial changes (1)
- pkg/conversion/capi2mapi/aws.go
🚧 Files skipped from review as they are similar to previous changes (2)
- pkg/controllers/capiinstaller/capi_installer_controller_test.go
- hack/tools/go.mod
🧰 Additional context used
🧬 Code graph analysis (4)
pkg/providerimages/providerimages_test.go (1)
pkg/providerimages/providerimages.go (1)
ProviderImageManifests(56-63)
cmd/cluster-capi-operator/main.go (1)
pkg/providerimages/providerimages.go (2)
ReadProviderImages(117-131)ProviderImageManifests(56-63)
manifests-gen/generate.go (1)
pkg/providerimages/providerimages.go (1)
ProviderMetadata(66-72)
pkg/controllers/capiinstaller/capi_installer_controller.go (1)
pkg/providerimages/providerimages.go (1)
ProviderImageManifests(56-63)
🪛 Checkov (3.2.334)
manifests/0000_30_cluster-api_11_deployment.yaml
[medium] 2-96: Containers should not run with allowPrivilegeEscalation
(CKV_K8S_20)
[medium] 2-96: Minimize the admission of root containers
(CKV_K8S_23)
🔇 Additional comments (32)
pkg/controllers/infracluster/azure.go (1)
279-279: LGTM—Safe to remove the nolint directive.The conversion from
int64toint32is safe becausestrconv.ParseInton line 216 usesbitSize: 32, which guarantees the parsed value fits within the int32 range. Additionally, valid port numbers (0–65535) are well within int32 bounds.manifests/0000_30_cluster-api_11_deployment.yaml (1)
38-39: LGTM! Provider image infrastructure properly configured.The environment variable, volume mount, and emptyDir volume are correctly configured to support the provider image caching mechanism. Using an emptyDir volume is appropriate for storing ephemeral provider manifest data extracted from container images.
Also applies to: 58-59, 95-96
manifests-gen/util.go (2)
13-19: LGTM! GVK preservation after conversion is correct.The explicit copy of GroupVersionKind from source to target after
scheme.Convertis necessary and correct, as the conversion process doesn't automatically preserve GVK metadata.
13-19: No action required. Theschemevariable is properly initialized in the package scope atmanifests-gen/main.go:48as a package-level variable viaruntime.NewScheme(), and is fully configured with necessary types in theinit()function. Sinceutil.gois in the same package, theconvertfunction can safely access it.go.mod (2)
19-19: No action needed. The versiongithub.com/google/go-containerregistry v0.20.7is the latest release (Nov 24, 2025) and has no publicly assigned security vulnerabilities.
22-22: Dependencies verified—all versions are valid with no known security advisories.The dependency updates for container image operations are confirmed:
github.com/klauspost/compressv1.18.1golang.org/x/syncv0.18.0golang.org/x/toolsv0.39.0- Supporting indirect dependencies for OCI/container operations (stargz-snapshotter, docker-credential-helpers, opencontainers packages, tar-split)
All versions have been cross-checked and no CVEs or active security advisories were found for any of these versions as of January 2026.
e2e/go.mod (1)
80-80: Indirect dependency versions verified and safe.The three updated indirect dependencies have been checked:
github.com/klauspost/compressv1.18.1: No known CVEs; safe for usegolang.org/x/oauth2v0.33.0: Well above the patched version for CVE-2025-22868; securegolang.org/x/toolsv0.39.0: No known vulnerabilities in this versionAll versions are valid releases and free from published security vulnerabilities.
manifests/0000_30_cluster-api_03_rbac_roles.yaml (1)
36-55: Least-privilege RBAC for pull-secret access looks right.Granting only
getonsecretswithresourceNames: [pull-secret]inopenshift-configis appropriately scoped for a directGETof the pull secret. Consider double-checking thepullSecretName/pullSecretNamespaceconstants inpkg/providerimagesmatchopenshift-config/pull-secret.manifests/0000_30_cluster-api_04_rbac_bindings.yaml (1)
38-56: RoleBinding correctly grants the SA access inopenshift-config.Binding the
openshift-cluster-api/cluster-capi-operatorServiceAccount to theopenshift-configRole is the right pattern for narrowly scoped access topull-secret.pkg/providerimages/providerimages_test.go (1)
165-599: Nice table coverage; please verify Go version support fort.Context().The matrix hits the important edges (missing metadata/manifests, invalid YAML/ref, error aggregation, cancellation, and layer precedence). One thing to confirm is repository/CI Go version compatibility with
testing.T.Context()(and any other newer APIs used in this package).cmd/cluster-capi-operator/main.go (1)
275-318: PlumbingproviderImagesthrough reconcilers/controller init looks consistent.The updated signatures and the final handoff to
capiinstaller.CapiInstallerController{ProviderImages: providerImages}are coherent and keep the dataflow explicit.Also applies to: 320-373
pkg/controllers/capiinstaller/capi_installer_controller.go (5)
188-235: LGTM: Clean provider image reconciliation with proper sorting.The reconciliation logic correctly filters by platform, sorts by type priority (core → infrastructure → other) with stable name-based tie-breaking, and propagates errors with context.
237-262: LGTM: Proper resource cleanup with errors.Join.The deferred
errors.Joinpattern ensures both processing errors and reader close errors are properly propagated.
358-362: LGTM: Empty manifest filtering prevents downstream errors.Skipping empty manifests avoids unnecessary parsing errors and simplifies downstream processing.
517-532: LGTM: Supports both text and compressed ConfigMap formats.The reader handles both legacy text format and zstd-compressed binary format with proper error handling.
534-541: ManifestsPath is properly validated upstream and is safe to open.The path is constructed safely in
pkg/providerimages/providerimages.go(line 223) asfilepath.Join(outputDir, manifestsFile), wheremanifestsFileis a hardcoded constant"manifests.yaml"andoutputDiris built from a sanitized image reference. ThesanitizeImageReffunction removes forward slashes, colons, and @ characters, effectively preventing directory traversal attacks. No additional validation is needed inproviderManifestReader.manifests-gen/generate.go (4)
31-57: LGTM: Well-structured orchestration with clear error propagation.The function cleanly sequences resource generation, processing, and writing with contextual error messages at each step.
59-93: LGTM: Proper kustomize integration with nil-safe resource handling.The nil check at line 74 prevents panics from malformed kustomize output, and the JSON → Unstructured conversion path is correct.
98-98: Verify file permissions are appropriate for the deployment environment.Both
manifests.yamlandmetadata.yamlare created with0600permissions (owner read/write only). If these files need to be read by other processes, users, or during container image builds, more permissive permissions like0644may be required.Also applies to: 133-133
95-128: LGTM: Robust YAML writing with proper separators and cleanup.The incremental write pattern with document separators (
---) and deferred flush/close usingerrors.Joinensures both correctness and proper resource cleanup.pkg/providerimages/providerimages.go (7)
117-131: LGTM: Clean separation of concerns with keychain initialization.Fetching the pull secret, parsing Docker config, and delegating to the internal implementation with an abstracted fetcher is well-structured.
139-189: LGTM: Effective concurrent processing with controlled parallelism.The errgroup with a limit of 5 concurrent fetches balances throughput and resource usage. Aggregating errors via channels while returning
nilfrom goroutines (line 156) is the correct pattern for collecting all errors without short-circuiting.
145-145: Verify concurrency limit is appropriate for production load.The hardcoded limit of 5 concurrent image fetches may need tuning based on:
- Network bandwidth and registry rate limits
- Memory consumption per image fetch
- Startup time requirements
Consider making this configurable if production metrics indicate the limit is too conservative or too aggressive.
246-306: LGTM: Secure tar extraction with proper layer overwrite semantics.Iterating layers top-to-bottom (line 259) correctly implements OCI layer semantics. The error handling distinguishes between missing directories (skip) and missing required files (error), which is appropriate.
308-347: LGTM: Path traversal protection via normalization.Using
path.Cleanwith a leading "/" (line 329) correctly normalizes tar entry paths and prevents../traversal attacks.
363-388: LGTM: Atomic write with simultaneous hash computation.The
io.MultiWriterpattern (line 374) efficiently computes the SHA256 hash while writing, and the string replacer (lines 377-378) correctly substitutes placeholder image names.
349-358: LGTM: Simple but effective image reference sanitization.Replacing
/,:, and@with_produces filesystem-safe directory names. Since these characters are required in image references, collisions are unlikely in practice.manifests-gen/customizations.go (5)
34-99: LGTM: Clean group/kind dispatch with client.Object migration.The refactored dispatch logic using
getGroupandgetKindis cleaner than per-kind branching. Filtering cert-manager resources (lines 86-90) and handling core API resources correctly maintains backward compatibility.
173-205: LGTM: Strong image registry validation prevents supply chain risks.Enforcing that all images originate from
registry.ci.openshift.org(lines 194-201) is a solid security control. The clear error messages help developers quickly identify misconfigured images.
242-308: LGTM: Well-structured admission policy for InfraCluster protection.The policy correctly:
- Matches DELETE operations on the target CRD (lines 259-265)
- Validates against the Infrastructure parameter (line 271)
- Scopes binding to the CAPI namespace (lines 290-295)
- Strips redundant status field (line 305)
310-318: LGTM: Clean status field removal via unstructured conversion.Converting to unstructured, deleting the status field, and converting back is the correct approach for stripping fields from typed objects.
58-58: Panics on conversion failure are acceptable for a build-time tool.The
mustConvertcalls will panic if type conversion fails. Since this is a manifest generation tool run at build time with controlled inputs, failing fast on unexpected types is appropriate and helps catch issues early.Also applies to: 123-123, 133-133, 147-147, 159-159, 175-175
| providerImageDir := os.Getenv(providerImageDirEnvVar) | ||
| if providerImageDir == "" { | ||
| providerImageDir = defaultProviderImageDirPath | ||
| } | ||
|
|
||
| containerImageRefs := slices.Collect(maps.Values(containerImages)) | ||
|
|
||
| providerImages, err := providerimages.ReadProviderImages(context.Background(), mgr.GetAPIReader(), mgr.GetLogger(), containerImageRefs, providerImageDir) | ||
| if err != nil { | ||
| klog.Error(err, "unable to get provider image metadata") | ||
| os.Exit(1) | ||
| } | ||
|
|
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.
Avoid hard-failing operator startup on provider image fetch; add cancellation + timeout.
Right now, any failure to read the pull-secret / reach registries / write cache dir will os.Exit(1). During the transition period (ConfigMap transport still supported), that’s a significant availability risk—clusters could lose reconciliation entirely due to a transient registry outage.
Suggested approach:
- Use a single signal-aware context for startup + manager (
ctx := ctrl.SetupSignalHandler()once). - Bound provider image reads with a timeout.
- On failure, log and continue with
providerImages == nil(so legacy ConfigMap path can still function).
Proposed change
@@
func main() {
+ ctx := ctrl.SetupSignalHandler()
+
scheme := runtime.NewScheme()
initScheme(scheme)
@@
- providerImages, err := providerimages.ReadProviderImages(context.Background(), mgr.GetAPIReader(), mgr.GetLogger(), containerImageRefs, providerImageDir)
+ startupCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
+ defer cancel()
+
+ providerImages, err := providerimages.ReadProviderImages(startupCtx, mgr.GetAPIReader(), mgr.GetLogger(), containerImageRefs, providerImageDir)
if err != nil {
- klog.Error(err, "unable to get provider image metadata")
- os.Exit(1)
+ // Keep backwards-compat during transition: fall back to transport ConfigMaps.
+ klog.Error(err, "unable to get provider image metadata; continuing without provider images")
+ providerImages = nil
}
- infra, err := util.GetInfra(context.Background(), mgr.GetAPIReader())
+ infra, err := util.GetInfra(ctx, mgr.GetAPIReader())
@@
- platform, err := util.GetPlatform(context.Background(), infra)
+ platform, err := util.GetPlatform(ctx, infra)
@@
- if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
+ if err := mgr.Start(ctx); err != nil {
klog.Error(err, "problem running manager")
os.Exit(1)
}
}Also applies to: 231-243, 259-263
🤖 Prompt for AI Agents
In @cmd/cluster-capi-operator/main.go around lines 218 - 230, Replace the
current unconditional os.Exit on provider image read failures by using a
signal-aware startup context and a bounded timeout: create a single ctx via
ctrl.SetupSignalHandler() (use it for manager startup), wrap
providerimages.ReadProviderImages with context.WithTimeout to enforce a
deadline, call providerimages.ReadProviderImages(ctxWithTimeout, ...) instead of
context.Background(), and on error log the failure (including err) but do not
call os.Exit—leave providerImages nil so the legacy ConfigMap path can proceed;
apply the same pattern to the other provider image read sites (the calls around
the blocks that set providerImages and similar reads at 231-243 and 259-263).
| ) | ||
|
|
||
| require ( | ||
| github.com/docker/cli v29.0.3+incompatible |
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.
🧩 Analysis chain
🏁 Script executed:
cd / && git ls-files go.modRepository: openshift/cluster-capi-operator
Length of output: 145
🏁 Script executed:
cat -n go.mod | head -20Repository: openshift/cluster-capi-operator
Length of output: 1015
🌐 Web query:
docker/cli v29.0.3 release stability and compatibility
💡 Result:
Short summary — stability and compatibility for docker/cli v29.0.3
-
What v29.0.3 is: a patch-level release in the v29 series that fixes CLI output/format bugs (e.g., restores BuildTime format, fixes imageFormat handling) and includes corresponding daemon/networking fixes. [1][2]
-
Compatibility / breaking changes to be aware of:
- Docker Engine v29 (the same major line) raises the minimum daemon API to v1.44 (clients older than v25 may be incompatible). Upgrade client and daemon together or ensure API negotiation is supported. [1]
- Several v29 breaking changes affect ecosystems (Go module changes, Docker Content Trust removed from the CLI and moved to a plugin, cgroup v1 deprecated). Review the v29 release notes before upgrading. [1]
- Third‑party tooling may need updates (some tools like Portainer, cAdvisor, Watchtower reported compatibility issues initially). Test your tooling/agents against v29 in staging. [3][4]
-
Stability recommendation:
- v29.0.3 itself is a patch release addressing known bugs — it is reasonable for production use if you confirm your toolchain supports the raised API and other v29 changes.
- If you rely on third‑party management/monitoring GUIs or older clients, hold or test first; otherwise upgrade both CLI and Engine (or pin API compatibility) and monitor for issues. [1][3]
Sources
- Docker Engine / release notes (v29.0.3 details and v29 breaking changes). [1]
- docker/cli repository (project and changelogs). [2]
- Compatibility reports / vendor notices (examples: Harness CI guidance, Portainer community reports). [3][4]
If you want, I can: (a) list the exact changed CLI fields/PR IDs in v29.0.3, (b) check a specific third‑party tool for v29 compatibility, or (c) give a short upgrade checklist.
🌐 Web query:
go-containerregistry v0.20.7 docker/cli compatibility requirements
💡 Result:
Short answer: go-containerregistry v0.20.7 expects the Docker engine/Go SDK compatibility introduced in the v0.20.x line — it bumped its docker/docker dependency to the v28.x series (i.e., github.com/docker/docker v28.0.0+incompatible) and the release deprecates the library’s DockerVersion field. If you rely on docker/cli SDKs, expect compatibility with the docker/docker (v28.x) / docker/cli calver used around that bump; newer docker v29+ breaking changes have been discussed as a potential future incompatibility. [1][2][3]
Sources
- go-containerregistry v0.20.7 release notes (changes include deprecating DockerVersion and dependency updates). [1]
- go-containerregistry v0.20.4 / v0.20.x changelog noting bump of github.com/docker/docker to v28.0.0+incompatible. [2]
- Issue discussing future docker v29.0.0 breaking changes impacting ggcr. [3]
References
[1] https://github.com/google/go-containerregistry/releases/tag/v0.20.7
[2] https://github.com/google/go-containerregistry/releases/tag/v0.20.4
[3] google/go-containerregistry#2160
Verify docker/cli v29.0.3 compatibility with go-containerregistry v0.20.7.
The docker/cli v29.0.3 release is stable but introduces breaking changes (minimum daemon API raised to v1.44, Docker Content Trust moved to plugin, cgroup v1 deprecated). However, go-containerregistry v0.20.7 was tested and built against docker v28.x, not v29.x. Test this version combination in staging to ensure the v29 breaking changes do not conflict with your containerregistry usage, or consider pinning docker/cli to the v28 series to match go-containerregistry's tested dependencies.
🤖 Prompt for AI Agents
In @go.mod at line 12, The go.mod update pins github.com/docker/cli to v29.0.3
while go-containerregistry is v0.20.7 (tested against docker v28.x); verify
compatibility by testing the combination in staging focusing on daemon API
usage, content trust and cgroup behavior, and if any breakage appears pin
github.com/docker/cli back to the v28.x series in go.mod (or add a go.mod
replace) to match go-containerregistry’s tested environment until you can
upgrade go-containerregistry or validate v29; run your container
registry-related integration tests and CI to confirm no runtime/API regressions.
| apiVersion: kustomize.config.k8s.io/v1alpha1 | ||
| kind: Component | ||
|
|
||
| namespace: openshift-cluster-api | ||
|
|
||
| patches: | ||
|
|
||
| # Retain Secrets internally for reference tracking, but don't emit them | ||
| - target: | ||
| kind: Secret | ||
| patch: | | ||
| apiVersion: v1 | ||
| kind: Secret | ||
| metadata: | ||
| name: __ignored__ | ||
| annotations: | ||
| config.kubernetes.io/local-config: "true" |
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.
🧩 Analysis chain
🌐 Web query:
Kustomize patch target all resources of kind without name
💡 Result:
Yes — use the newer "patches" (or patchesStrategicMerge) with a target that specifies kind (and group/version if needed) but no name; Kustomize will apply that patch to all matching resources of that kind. Example:
kustomization.yaml
- patches:
- path: patch.yaml
target:
kind: ConfigMapoptionally: group: "" # for core API, or group: apps, etc.
optionally: version: v1
- path: patch.yaml
patch.yaml (strategic merge or json patch content as appropriate)
References: Kubernetes Kustomize docs (patches target fields) and Kustomize discussion explaining patches can match multiple resources. [1][2]
Sources:
[1] Kubernetes docs — Declarative Management of Kubernetes Objects Using Kustomize (patches target fields).
[2] Stack Overflow — explanation that "patches" can match more than one resource (unlike patchesJson6902).
🌐 Web query:
Kustomize config.kubernetes.io/local-config annotation behavior
💡 Result:
Summary — meaning and behavior
-
Purpose: config.kubernetes.io/local-config marks a resource as "local-only" (a configuration for local tools, not intended to be sent to the Kubernetes API server). Examples: kustomization.yaml or other helper/config resources. [1]
-
Semantics: tools SHOULD treat resources with this annotation as not-for-apply/send-to-server. Kustomize and related tooling use it so local config is skipped when producing/applying live resources. [1][2]
-
Value: set as a string (commonly "true"). Any value other than the literal "false" is treated as local; avoid unquoted YAML booleans. [2]
-
Practical effect: resources annotated with config.kubernetes.io/local-config are not applied to the cluster by tools that honor the annotation (they are kept only for local processing). [2]
Sources
[1] sigs.k8s.io/kustomize (Configuration IO API docs).
[2] kpt documentation for local-config annotation.
🌐 Web query:
Kustomize strategic merge patch metadata.name placeholder __ignored__
💡 Result:
Short answer: metadata.name in a strategic-merge patch is used to identify the target resource and cannot be treated as a free placeholder. Putting a fake name like "ignored" will prevent Kustomize from matching the patch. Use one of these instead:
- Give the real metadata.name in the patch so it matches the resource. [1][2]
- Use the patch "target" (group/version/kind plus name or labelSelector) so you don't need metadata.name inside the patch body. Example: use patches with a target: labelSelector to match by label. [1][3]
- Use a JSON6902 patch when you need to patch a resource without depending on metadata.name matching. [1][2]
References:
- Kustomize/Kubernetes docs (patch matching rules, JSON6902 vs strategic merge). [1]
- Kustomize examples / warning that metadata.name must match. [2]
- StackOverflow/notes on using target/labelSelector and deprecation notes for older fields. [3]
- Community notes that you cannot change or ignore name when using strategic merge. [4]
Remove metadata.name placeholder from Secret patch — it will prevent the annotation from being applied.
The patch uses metadata.name: __ignored__ in a strategic-merge patch, but this placeholder will prevent Kustomize from matching any actual Secret resources. In strategic-merge patches, metadata.name must match the actual resource name to apply the patch.
To add the config.kubernetes.io/local-config: "true" annotation to all Secrets, use one of these approaches:
- Specify individual Secret names in the target for each Secret that needs the annotation
- Use a labelSelector in the target to match Secrets by label instead of relying on metadata.name matching
- Use JSON6902 patches instead of strategic-merge, which don't depend on metadata.name matching for multi-resource application
The annotation purpose (marking Secrets as local-only, preventing emission) is correct, but the patch mechanism needs to be fixed.
| if err := r.reconcileProviderImages(ctx, log); err != nil { | ||
| return ctrl.Result{}, fmt.Errorf("error reconciling CAPI provider images: %w", err) | ||
| } |
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.
Missing degraded condition on provider image reconciliation error.
When reconcileProviderImages returns an error, the controller should call setDegradedCondition before returning, consistent with how ConfigMap processing errors are handled at lines 143-145 and 159-161.
🔧 Proposed fix
if err := r.reconcileProviderImages(ctx, log); err != nil {
+ if err := r.setDegradedCondition(ctx, log); err != nil {
+ return ctrl.Result{}, fmt.Errorf("failed to set conditions for CAPI Installer controller: %w", err)
+ }
+
return ctrl.Result{}, fmt.Errorf("error reconciling CAPI provider images: %w", err)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if err := r.reconcileProviderImages(ctx, log); err != nil { | |
| return ctrl.Result{}, fmt.Errorf("error reconciling CAPI provider images: %w", err) | |
| } | |
| if err := r.reconcileProviderImages(ctx, log); err != nil { | |
| if err := r.setDegradedCondition(ctx, log); err != nil { | |
| return ctrl.Result{}, fmt.Errorf("failed to set conditions for CAPI Installer controller: %w", err) | |
| } | |
| return ctrl.Result{}, fmt.Errorf("error reconciling CAPI provider images: %w", err) | |
| } |
🤖 Prompt for AI Agents
In @pkg/controllers/capiinstaller/capi_installer_controller.go around lines 181
- 183, When reconcileProviderImages(ctx, log) returns an error, call
r.setDegradedCondition(ctx, "ProviderImagesReconcileFailed", err) (or the same
setDegradedCondition signature used for ConfigMap errors) before returning so
the controller sets the degraded condition consistently; mirror the pattern used
for ConfigMap processing errors (the calls around ConfigMap handling) and
include the error in the degraded condition message, then return the wrapped
error as currently done.
Adds a flag, profile-name allowing specification of a profile (and therefore subdirectory) to output manifests and metadata to.
TODO: merge into rewrite commit
When parsing images, the logic now checks for multiple 'profiles' (sub directories) under /capi-operator-manifests. If multiple are found, we return them all.
We've been referring to a slice of []providerImagesManifests as providerImages, which is misleading. With us now having profiles, this makes more sense being named 'providerProfiles', each slice is a representation of metadata and manifests for a named profile.
manifests-gen outputs to profile directory
|
@mdbooth: This pull request references OCPCLOUD-3327 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
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.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@manifests-gen/main.go`:
- Line 72: The flag help text for profileName (the flag.String call assigning
profileName) has a typo with an extra single quote and misplaced period; update
the help string to correct punctuation and matching quotes—e.g., change the text
to something like "Name of the profile, e.g. 'featuregate-foo' (default:
'default')." ensuring quotes and the final period are placed correctly.
In `@pkg/providerimages/providerimages.go`:
- Around line 206-228: The outputDir created for each image (via sanitizedRef
from sanitizeImageRef) is reused and never cleared before calling
extractCapiManifestsToDir, causing stale files and incorrect manifests; before
extraction, remove or empty outputDir (e.g., os.RemoveAll(outputDir) then
recreate) to ensure a clean extraction surface, and if images may delete files
across layers implement OCI whiteout handling in extractCapiManifestsToDir so
deletions in higher layers are honored; keep discoverProfiles usage unchanged
after a fresh extraction.
- Around line 95-118: Update the ReadProviderImages docblock to match the actual
function signature and directory layout: state that containerImages is a
[]string of image references (not a map), describe that providerImageDir is a
cache root where each image is written to a subdirectory named for the image
reference, and clarify the manifest layout is
/capi-operator-manifests/<profile>/<metadata.yaml and
/capi-operator-manifests/<profile>/<manifests.yaml> (i.e., profiles are
required), also mention that missing profile files cause an error and that
manifestImageName replacements are applied when writing to the cache.
♻️ Duplicate comments (1)
cmd/cluster-capi-operator/main.go (1)
218-230: Don’t hard-fail operator startup on provider image read errors during the transition period.Right now any pull-secret/registry/cache-dir issue exits the operator even though transport ConfigMaps are still supported. This is a significant availability risk; use a signal-aware context + timeout and fall back to
providerProfiles == nilon error.Proposed change
func main() { + ctx := ctrl.SetupSignalHandler() + scheme := runtime.NewScheme() initScheme(scheme) @@ - providerProfiles, err := providerimages.ReadProviderImages(context.Background(), mgr.GetAPIReader(), mgr.GetLogger(), containerImageRefs, providerImageDir) + startupCtx, cancel := context.WithTimeout(ctx, 2*time.Minute) + defer cancel() + providerProfiles, err := providerimages.ReadProviderImages(startupCtx, mgr.GetAPIReader(), mgr.GetLogger(), containerImageRefs, providerImageDir) if err != nil { - klog.Error(err, "unable to get provider image metadata") - os.Exit(1) + klog.Error(err, "unable to get provider image metadata; continuing without provider images") + providerProfiles = nil } @@ - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + if err := mgr.Start(ctx); err != nil { klog.Error(err, "problem running manager") os.Exit(1) } }Also applies to: 259-262
🧹 Nitpick comments (5)
manifests-gen/main.go (1)
125-133: Consider preserving the original parse error for better diagnostics.When
ParseSemanticfails, the original error is discarded. Including it would help users understand why their version string is invalid (e.g., missing "v" prefix, invalid format).Suggested fix
func() error { // If set, provider version must be valid if opts.version != "" { if _, err := version.ParseSemantic(opts.version); err != nil { - return fmt.Errorf("invalid version %s for provider %s", opts.version, opts.name) + return fmt.Errorf("invalid version %s for provider %s: %w", opts.version, opts.name, err) } } return nil }(),pkg/providerimages/providerimages.go (3)
119-129: Add an explicit check for missing.dockerconfigjsonkey.Right now
pullSecret := secret.Data[pullSecretKey]will passniltoparseDockerConfigif the key is missing, which makes the failure mode less actionable.Proposed change
pullSecret := secret.Data[pullSecretKey] + if len(pullSecret) == 0 { + return nil, fmt.Errorf("pull secret %s/%s missing key %q", pullSecretNamespace, pullSecretName, pullSecretKey) + } keychain, err := parseDockerConfig(pullSecret)
146-193: errgroup is currently not used to propagate errors/cancellation; simplify or make it effective.
g.Goalways returnsnilandg.Wait()is ignored, so errgroup isn’t doing much beyond the concurrency limit. Consider either:
- return
errfrom the goroutine and rely ong.Wait()(and still collect per-image errors), or- drop errgroup and use a simple worker pool with context handling.
474-499: Write manifests atomically to avoid partial/corrupt cache entries.If the process is interrupted mid-write, downstream readers may see truncated
manifests.yaml. Writing to a temp file +os.Renamewould make this safer.pkg/providerimages/providerimages_test.go (1)
166-678: Test suite coverage looks solid for profiles + layering + replacement + cancellation.The cases hit the important edges for this feature (multi-profile, layered overrides, and error aggregation).
One optional improvement: makecreateTarLayerwrite files in sorted key order to avoid incidental nondeterminism if assertions tighten later.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (5)
cmd/cluster-capi-operator/main.gomanifests-gen/generate.gomanifests-gen/main.gopkg/providerimages/providerimages.gopkg/providerimages/providerimages_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- manifests-gen/generate.go
🧰 Additional context used
🧬 Code graph analysis (1)
cmd/cluster-capi-operator/main.go (1)
pkg/providerimages/providerimages.go (2)
ReadProviderImages(118-132)ProviderImageManifests(56-64)
🔇 Additional comments (2)
cmd/cluster-capi-operator/main.go (1)
20-23: The review comment is incorrect. The code is correct as written.In Go 1.22+,
maps.Values()returns an iterator, not a slice. Therefore,slices.Collect(maps.Values(containerImages))is the correct and necessary way to materialize that iterator into a[]stringslice for use withReadProviderImages(). Both the"maps"and"slices"imports are required and appropriate for Go 1.24.0.The proposed change to remove
slicesand usemaps.Values(containerImages)directly would be incorrect—it would assign an iterator tocontainerImageRefs, causing a type mismatch.Likely an incorrect or invalid review comment.
pkg/providerimages/providerimages.go (1)
148-159: The range variable capture issue described does not apply to this codebase. The project targets Go 1.24.0 (as specified in go.mod), which is well above Go 1.22 where automatic per-iteration loop variable scoping was introduced as a language feature. In Go 1.22+, theimageRefvariable in the range loop is automatically scoped to each iteration, preventing the goroutine closure from observing stale values across iterations. The proposed shadowing fix is unnecessary.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| func main() { | ||
| var ( | ||
| manifestsPath = flag.String("manifests-path", "", "Path to the desired directory where to output the generated manifests. Required.") | ||
| profileName = flag.String("profile-name", "default", "Name of the profile, e.g 'featuregate-foo' (default: 'default'.'") |
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.
Typo in flag help text.
There's an extra single quote and period in the help text.
Suggested fix
- profileName = flag.String("profile-name", "default", "Name of the profile, e.g 'featuregate-foo' (default: 'default'.'")
+ profileName = flag.String("profile-name", "default", "Name of the profile, e.g. 'featuregate-foo' (default: 'default')")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| profileName = flag.String("profile-name", "default", "Name of the profile, e.g 'featuregate-foo' (default: 'default'.'") | |
| profileName = flag.String("profile-name", "default", "Name of the profile, e.g. 'featuregate-foo' (default: 'default')") |
🤖 Prompt for AI Agents
In `@manifests-gen/main.go` at line 72, The flag help text for profileName (the
flag.String call assigning profileName) has a typo with an extra single quote
and misplaced period; update the help string to correct punctuation and matching
quotes—e.g., change the text to something like "Name of the profile, e.g.
'featuregate-foo' (default: 'default')." ensuring quotes and the final period
are placed correctly.
| // Create output directory for this provider image | ||
| sanitizedRef := sanitizeImageRef(imageRef) | ||
| outputDir := filepath.Join(providerImageDir, sanitizedRef) | ||
|
|
||
| if err := os.MkdirAll(outputDir, 0750); err != nil { | ||
| return nil, fmt.Errorf("failed to create output directory: %w", err) | ||
| } | ||
|
|
||
| // Extract files from the image to disk | ||
| if err := extractCapiManifestsToDir(img, outputDir); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Discover profiles from extracted files | ||
| profiles, err := discoverProfiles(outputDir) | ||
| if err != nil { | ||
| if errors.Is(err, errNoCapiManifests) { | ||
| // Image doesn't contain /capi-operator-manifests, skip it | ||
| return nil, nil | ||
| } | ||
|
|
||
| return nil, err | ||
| } |
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.
Cache coherency risk: stale files + missing layer deletion (whiteout) semantics can produce wrong manifests across upgrades.
outputDiris reused and never cleared.- Extraction skips existing files and doesn’t interpret OCI whiteouts, so a file deleted in a higher layer can still be written from a lower layer.
At minimum, clear outputDir before extraction; if you expect deletions across layers, you’ll also need whiteout handling.
Minimal hardening (clear output dir)
outputDir := filepath.Join(providerImageDir, sanitizedRef)
+ // Ensure cache dir reflects only the current image contents.
+ if err := os.RemoveAll(outputDir); err != nil {
+ return nil, fmt.Errorf("failed to clear output directory: %w", err)
+ }
if err := os.MkdirAll(outputDir, 0750); err != nil {
return nil, fmt.Errorf("failed to create output directory: %w", err)
}Also applies to: 356-441
🤖 Prompt for AI Agents
In `@pkg/providerimages/providerimages.go` around lines 206 - 228, The outputDir
created for each image (via sanitizedRef from sanitizeImageRef) is reused and
never cleared before calling extractCapiManifestsToDir, causing stale files and
incorrect manifests; before extraction, remove or empty outputDir (e.g.,
os.RemoveAll(outputDir) then recreate) to ensure a clean extraction surface, and
if images may delete files across layers implement OCI whiteout handling in
extractCapiManifestsToDir so deletions in higher layers are honored; keep
discoverProfiles usage unchanged after a fresh extraction.
|
@mdbooth: This pull request references OCPCLOUD-3327 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-ovn-techpreview openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
|
/testwith openshift/cluster-capi-operator/main/regression-clusterinfra-aws-ipi-techpreview-capi openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
|
/testwith openshift/cluster-capi-operator/main/e2e-aws-capi-techpreview openshift/cluster-api-provider-aws#584 openshift/cluster-api#259 |
Rewrite manifests-gen to support embedding CAPI installer manifests in the provider image instead of in a transport configmap.
Update the CAPI installer controller to support the new embedded manifests in addition to the existing transport configmaps. This allows us to have a smooth transition period while we update all providers.
Summary by CodeRabbit
New Features
Infrastructure
Manifests / Tooling
Tests
✏️ Tip: You can customize this high-level summary in your review settings.