Skip to content

CNTRLPLANE-1331: Get self-managed Azure working again - #6703

Merged
openshift-merge-bot[bot] merged 8 commits into
openshift:mainfrom
bryan-cox:self-managed-azure
Sep 23, 2025
Merged

CNTRLPLANE-1331: Get self-managed Azure working again#6703
openshift-merge-bot[bot] merged 8 commits into
openshift:mainfrom
bryan-cox:self-managed-azure

Conversation

@bryan-cox

@bryan-cox bryan-cox commented Aug 27, 2025

Copy link
Copy Markdown
Member

What this PR does / why we need it:
This PR contains the necessary changes to get self-managed Azure Hosted Clusters working again when running on an Azure OpenShift management cluster.

Which issue(s) this PR fixes:
Fixes CNTRLPLANE-1331

Checklist

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Aug 27, 2025
@openshift-ci-robot

openshift-ci-robot commented Aug 27, 2025

Copy link
Copy Markdown

@bryan-cox: This pull request references CNTRLPLANE-1331 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.20.0" version, but no target version was set.

Details

In response to this:

This PR is still very much in draft, in active development, and no where near a final form to be reviewed.

What this PR does / why we need it:

Which issue(s) this PR fixes (optional, use fixes #<issue_number>(, fixes #<issue_number>, ...) format, where issue_number might be a GitHub issue, or a Jira story:
Fixes #

Checklist

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

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.

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 27, 2025
@openshift-ci

openshift-ci Bot commented Aug 27, 2025

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai

coderabbitai Bot commented Aug 27, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a new Network workload identity across APIs and CRDs, updates codegen, and vendors. Introduces CLI/infra support for workload identities files and role assignment. Adapts control plane (cloud-controller-manager) and storage (CSI) to federated identity with token-minter. Implements Azure credential reconciliation utilities and integrates them into operators. Updates docs and examples; adds tests and fixtures.

Changes

Cohort / File(s) Summary
API: Azure workload identities field
api/hypershift/v1beta1/azure.go, api/hypershift/v1beta1/zz_generated.deepcopy.go, client/applyconfiguration/hypershift/v1beta1/azureworkloadidentities.go, vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/azure.go, vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go
Adds Network to AzureWorkloadIdentities; updates deepcopy; adds applyconfiguration field and WithNetwork setter; vendors mirrored changes.
CRDs: HostedCluster (all variants)
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/*, cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-*.crd.yaml
Extends schema with workloadIdentities.cloudProvider.network (UUID-validated clientID) and marks it required; some add top-level spec.network where applicable.
CRDs: HostedControlPlane (all variants)
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/*, cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-*.crd.yaml
Adds workloadIdentities.cloudProvider.network (UUID-validated clientID) and includes in required lists; introduces related entries in identity providers; adds new SA manifests references.
CLI and Infra wiring
cmd/cluster/azure/create.go, cmd/infra/azure/create.go, cmd/cluster/azure/create_test.go
Introduces --workload-identities-file; validates mutual exclusivity with existing flags; wires to infra; infra reads identities and assigns roles (including Network) when enabled; tests updated.
CCM (Azure) federated identity enablement
control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/component.go, .../azure/config.go, .../azure/deployment.go, control-plane-operator/controllers/hostedcontrolplane/v2/assets/azure-cloud-controller-manager/serviceaccount.yaml
Adds ServiceAccount with workload identity annotations and token-minter integration; extends AzureConfig for federated identity and networking fields; sets SA on deployment for self-managed Azure; adjusts predicates.
Storage (Azure CSI) identity handling
control-plane-operator/controllers/hostedcontrolplane/v2/storage/azure.go, .../v2/storage/component.go, control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/**/zz_fixture_*azure_*_csi_config_secret.yaml
Updates CSI secret config to support MSI for ARO and federated workload identity for self-managed Azure; enables config manifests for Azure platform; adds/updates test fixtures.
HCCO Azure credentials setup
control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure.go, .../azure/azure_test.go, control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/creds.go, .../resources.go
Adds functions to set up operand credential secrets (ingress, registry, disk, file) for managed/self-managed Azure; introduces Azure secret manifest helpers; integrates into reconciliation; tests added.
Operator platform Azure integration
hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go, .../azure/azure_test.go, hypershift-operator/controllers/hostedcluster/internal/platform/platform.go, hypershift-operator/controllers/manifests/azure.go
Enables workload identities (env, token-minter, secrets reconciliation); adds utilitiesImage field and updates constructor signature; adds secret manifest helpers; tests for credentials reconciliation.
Support utilities (Azure)
support/azureutil/azureutil.go, support/azureutil/azureutil_test.go, support/controlplane-component/token-minter-container.go
Adds IsSelfManagedAzure, AzureCredentialConfig, ReconcileAzureCredentials; broadens token-minter to self-managed Azure; comprehensive tests for credential reconciliation.
Tests and fixtures: CCM
control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/**/zz_fixture_*
Updates/introduces CCM fixtures (ServiceAccount, Deployment with token-minter, ConfigMap/Secret content changes, annotations, volumes) across variants.
Docs and examples
docs/content/reference/api.md, examples/workload-identities-example.json
Documents new network identity; adds example JSON including network.clientID.
Controller test tweak
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go
Configures Azure workload identities in test cluster spec (workload identity type with CloudProvider clientID).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.96% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "CNTRLPLANE-1331: Get self-managed Azure working again" succinctly captures the PR's primary goal and includes the Jira reference; it directly relates to the main changes in the diff (restoring self‑managed Azure behavior via workload identities, cloud-controller-manager/serviceaccount and token-minting changes, CLI wiring, and CRD updates) and is concise and clear for history scanning.
Description Check ✅ Passed The PR description states the intent ("get self‑managed Azure Hosted Clusters working again"), references the Jira issue CNTRLPLANE-1331, and is directly related to the changeset; though brief, it meets the lenient requirement of being on-topic and providing the rationale for the change.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci Bot added do-not-merge/needs-area area/api Indicates the PR includes changes for the API area/cli Indicates the PR includes changes for CLI approved Indicates a PR has been approved by an approver from all required OWNERS files. area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/documentation Indicates the PR includes changes for documentation and removed do-not-merge/needs-area labels Aug 27, 2025
@openshift-ci-robot

openshift-ci-robot commented Aug 27, 2025

Copy link
Copy Markdown

@bryan-cox: This pull request references CNTRLPLANE-1331 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.20.0" version, but no target version was set.

Details

In response to this:

This PR is still very much in draft, in active development, and no where near a final form to be reviewed.

What this PR does / why we need it:

Which issue(s) this PR fixes:
Fixes CNTRLPLANE-1331

Checklist

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

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.

@openshift-ci openshift-ci Bot added the area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release label Aug 27, 2025
@bryan-cox
bryan-cox force-pushed the self-managed-azure branch 9 times, most recently from b197a66 to 857fa6d Compare September 3, 2025 17:33
@bryan-cox

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 3, 2025

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@openshift-ci-robot

openshift-ci-robot commented Sep 3, 2025

Copy link
Copy Markdown

@bryan-cox: This pull request references CNTRLPLANE-1331 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.21.0" version, but no target version was set.

Details

In response to this:

This PR is still very much in draft, in active development, and no where near a final form to be reviewed.

What this PR does / why we need it:

Which issue(s) this PR fixes:
Fixes CNTRLPLANE-1331

Checklist

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • New Features

  • Azure: Added federated Workload Identities support, including a new required “network” identity for cluster-network-operator across HostedCluster/HostedControlPlane schemas.

  • CLI: New --workload-identities-file flag; infra flow can read identities and assign roles.

  • Control plane: Updated Azure cloud controller manager (renamed deployment, added ServiceAccount), token minting, and cloud config.

  • Storage/Credentials: Enhanced CSI and credential reconciliation for self-managed Azure.

  • Documentation

  • Updated API reference for the new “network” identity and added an example workload-identities JSON.

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.

@openshift-ci-robot

openshift-ci-robot commented Sep 3, 2025

Copy link
Copy Markdown

@bryan-cox: This pull request references CNTRLPLANE-1331 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.21.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:
This PR contains the necessary changes to get self-managed Azure Hosted Clusters working again when running on an Azure OpenShift management cluster.

Which issue(s) this PR fixes:
Fixes CNTRLPLANE-1331

Checklist

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • New Features

  • Azure: Added federated Workload Identities support, including a new required “network” identity for cluster-network-operator across HostedCluster/HostedControlPlane schemas.

  • CLI: New --workload-identities-file flag; infra flow can read identities and assign roles.

  • Control plane: Updated Azure cloud controller manager (renamed deployment, added ServiceAccount), token minting, and cloud config.

  • Storage/Credentials: Enhanced CSI and credential reconciliation for self-managed Azure.

  • Documentation

  • Updated API reference for the new “network” identity and added an example workload-identities JSON.

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.

@openshift-ci-robot

openshift-ci-robot commented Sep 3, 2025

Copy link
Copy Markdown

@bryan-cox: This pull request references CNTRLPLANE-1331 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.21.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:
This PR contains the necessary changes to get self-managed Azure Hosted Clusters working again when running on an Azure OpenShift management cluster.

Which issue(s) this PR fixes:
Fixes CNTRLPLANE-1331

Checklist

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • New Features

  • Azure: Added federated Workload Identities support, including a new required “network” identity for cluster-network-operator across HostedCluster/HostedControlPlane schemas.

  • CLI: New --workload-identities-file flag; infra flow can read identities and assign roles.

  • Control plane: Updated Azure cloud controller manager (renamed deployment, added ServiceAccount), token minting, and cloud config.

  • Storage/Credentials: Enhanced CSI and credential reconciliation for self-managed Azure.

  • Documentation

  • Updated API reference for the new “network” identity and added an example workload-identities JSON.

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.

1 similar comment
@openshift-ci-robot

openshift-ci-robot commented Sep 3, 2025

Copy link
Copy Markdown

@bryan-cox: This pull request references CNTRLPLANE-1331 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.21.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:
This PR contains the necessary changes to get self-managed Azure Hosted Clusters working again when running on an Azure OpenShift management cluster.

Which issue(s) this PR fixes:
Fixes CNTRLPLANE-1331

Checklist

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • New Features

  • Azure: Added federated Workload Identities support, including a new required “network” identity for cluster-network-operator across HostedCluster/HostedControlPlane schemas.

  • CLI: New --workload-identities-file flag; infra flow can read identities and assign roles.

  • Control plane: Updated Azure cloud controller manager (renamed deployment, added ServiceAccount), token minting, and cloud config.

  • Storage/Credentials: Enhanced CSI and credential reconciliation for self-managed Azure.

  • Documentation

  • Updated API reference for the new “network” identity and added an example workload-identities JSON.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 35

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (10)
vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/azure.go (2)

394-394: Kubebuilder marker typo disables validation generation

The marker uses "kubeubilder" instead of "kubebuilder", so this XValidation won’t be applied in the generated CRD.

-// +kubeubilder:validation:XValidation:rule="self.split('/')[2].matches('^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$')",message="the subscriptionId in the encryptionSetID must be a valid UUID. It should be 5 groups of hyphen separated hexadecimal characters in the form 8-4-4-4-12"
+// +kubebuilder:validation:XValidation:rule="self.split('/')[2].matches('^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$')",message="the subscriptionId in the encryptionSetID must be a valid UUID. It should be 5 groups of hyphen separated hexadecimal characters in the form 8-4-4-4-12"

629-633: Controller name mismatch in docs (“file” identity)

The “file” identity is described as associated with the azure-disk-controller; it should be the Azure File CSI controller.

-// file is a pre-existing managed identity associated with the azure-disk-controller.
+// file is a pre-existing managed identity associated with the azure-file-controller.
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml (1)

4116-4123: Adding "network" to required breaks backward compatibility for existing HostedClusters using WorkloadIdentities

Marking network as required will cause updates to fail admission for existing objects that don’t yet set it. If this is intentional, please document the migration path and ensure all test fixtures and examples include it. Otherwise, consider making it optional initially and enforcing via controllers, then flipping to required in a later minor with a deprecation window.

Proposed schema change (make it optional for now):

             required:
             - cloudProvider
             - disk
             - file
             - imageRegistry
             - ingress
-            - network
             - nodePoolManagement

Optionally add a soft validation to nudge users without hard-failing existing clusters (can be removed when you later make it required):

           type: object
+          x-kubernetes-validations:
+          - message: "Azure Workload Identities: 'network' clientID will be required in a future release"
+            rule: has(self.network)
cmd/infra/azure/create.go (1)

70-75: Wire the new option into the CLI

WorkloadIdentitiesFile is added to options but there’s no flag, so the feature is unreachable.

Add a flag in NewCreateCommand():

@@
   cmd.Flags().StringVar(&opts.SubnetID, "subnet-id", opts.SubnetID, "The subnet ID where the VMs will be placed.")
   cmd.Flags().StringVar(&opts.RHCOSImage, "rhcos-image", opts.RHCOSImage, `RHCOS image to be used for the NodePool. Could be obtained using podman run --rm -it --entrypoint cat $RELEASE_IMAGE release-manifests/0000_50_installer_coreos-bootimages.yaml | yq .data.stream -r | yq '.architectures.x86_64["rhel-coreos-extensions"]["azure-disk"].url'`)
   cmd.Flags().StringToStringVarP(&opts.ResourceGroupTags, "resource-group-tags", "t", opts.ResourceGroupTags, "Additional tags to apply to the resource group created (e.g. 'key1=value1,key2=value2')")
+  cmd.Flags().StringVar(&opts.WorkloadIdentitiesFile, "workload-identities-file", opts.WorkloadIdentitiesFile, "Path to a JSON/YAML file defining Azure workload identities; with --assign-service-principal-roles, roles are assigned for each identity.")
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml (1)

4615-4622: Remove network from required fields under .spec.platform.azure.azureAuthenticationConfig.workloadIdentities
Revert to:

   required:
    - cloudProvider
    - disk
    - file
    - imageRegistry
    - ingress
-   - network
    - nodePoolManagement

(cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml Lines 4619–4622)
If enforcing in 4.20, add admission defaulting or migration and document manual upgrade steps.

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml (1)

4107-4113: Remove network from workloadIdentities.required to prevent breaking change

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml
@@ lines 4106-4113
   required:
     - cloudProvider
     - disk
     - file
     - imageRegistry
     - ingress
-    - network
     - nodePoolManagement
  • Add an XValidation under spec.platform.azure.azureAuthenticationConfig to require workloadIdentities.network only for new clusters, grandfathering existing ones:
    - message: workloadIdentities.network is required for new clusters using WorkloadIdentities; existing objects without it remain valid
      rule: |
        self.azureAuthenticationConfigType != 'WorkloadIdentities' ||
        has(self.workloadIdentities) && (
          has(self.workloadIdentities.network) ||
          (has(oldSelf.workloadIdentities) && !has(oldSelf.workloadIdentities.network))
        )
  • Verify all CRDs, controllers, and documentation/examples referencing workloadIdentities.network align with this non-breaking change.
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml (1)

3999-4005: Revert ‘network’ from the CRD’s required list to avoid breaking upgrades
Adding network as a required field in this CRD will cause existing self-managed Azure HCPs (created before this change) to fail on future edits.

• In the YAML at api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml (lines 3999–4005), remove – network from required:.
• Enforce network at creation time via CLI or a validating webhook, and add controller logic that sets a clear Condition if it’s missing.
• Alternatively, gate the requirement behind a feature flag or CEL expression tied to a new opt-in field to preserve upgrade paths.
• Ensure you ship a release note/migration guide and update example manifests with the new network client ID.

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml (1)

4102-4151: Critical: CRD defines workloadIdentities.network but Go API/codegen do not

  • Evidence: CRD manifests include workloadIdentities.network (api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml:3989); the Go type AzureWorkloadIdentities is in api/hypershift/v1beta1/azure.go (type AzureWorkloadIdentities ~line 493) and the generated deepcopy (api/hypershift/v1beta1/zz_generated.deepcopy.go:750–755) does not copy a Network field; a vendored deepcopy does include Network (vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go:758).
  • Action: add a Network WorkloadIdentity field to AzureWorkloadIdentities (api/hypershift/v1beta1/azure.go) with json:"network", regenerate deepcopy/CRDs (controller-gen/codegen), and update controllers/operators and docs/examples to consume and document workloadIdentities.network.
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml (1)

4098-4105: Revert making workloadIdentities.network required — breaking API change

Adding "- network" to the CRD required list will reject existing HostedCluster objects at admission. Keep it optional and enforce via controller/feature-flag or provide a backwards-compatible conversion/defaulting path.

Locations:

  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml (lines 4098–4105) — CRD currently includes "- network".
  • api/hypershift/v1beta1/azure.go (≈lines 525–527, 622–623) — AzureWorkloadIdentities / ManagedIdentity are marked +required.
  • api/hypershift/v1beta1/openstack.go (line 78) — OpenStack.Network is defined as optional (network,omitempty) — inconsistent with the CRD change.

Proposed minimal, safe change:

               required:
               - cloudProvider
               - disk
               - file
               - imageRegistry
               - ingress
-              - network
               - nodePoolManagement
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml (1)

4118-4124: Make workloadIdentities.network conditionally required

Remove network from the workloadIdentities.required list in api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml (lines 4118–4124), and alongside the existing x-kubernetes-validations under azureAuthenticationConfig, add:

- message: workloadIdentities.network.clientID is required for WorkloadIdentities
  rule: self.azureAuthenticationConfigType == 'WorkloadIdentities'
        ? has(self.workloadIdentities.network) && has(self.workloadIdentities.network.clientID)
        : true
🧹 Nitpick comments (43)
control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/creds.go (1)

34-36: Reduce duplication: extract a tiny helper + constants for Azure secret names.

Centralizing names avoids typos and makes future renames trivial. Also consider adding brief doc comments to exported funcs to satisfy linters.

-// Azure credential secrets for hosted cluster operators
+// Azure credential secrets for hosted cluster operators.
+// Shared constants keep names/Namespaces in one place.
+const (
+  nsIngressOperator      = "openshift-ingress-operator"
+  nsImageRegistry        = "openshift-image-registry"
+  nsCSIDrivers           = "openshift-cluster-csi-drivers"
+
+  secCloudCreds          = "cloud-credentials"
+  secInstallerCloudCreds = "installer-cloud-credentials"
+  secAzureDiskCreds      = "azure-disk-credentials"
+  secAzureFileCreds      = "azure-file-credentials"
+)
+
+// azureCredsSecret returns a Secret with only ObjectMeta populated.
+func azureCredsSecret(ns, name string) *corev1.Secret {
+  return &corev1.Secret{
+    ObjectMeta: metav1.ObjectMeta{
+      Namespace: ns,
+      Name:      name,
+    },
+  }
+}
vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/azure.go (2)

319-323: Typos: “Ephmeral” → “Ephemeral” in user-facing schema docs

These appear in CRD descriptions.

-// When set to Ephmeral, the OS disk will not be persisted to Azure storage and implies restrictions to the VM size and caching type.
+// When set to Ephemeral, the OS disk will not be persisted to Azure storage and implies restrictions to the VM size and caching type.
-// Ephmeral disks are primarily used for stateless applications, provide lower latency than Persistent disks and also incur no storage costs.
+// Ephemeral disks are primarily used for stateless applications, provide lower latency than Persistent disks and also incur no storage costs.

105-105: Misleading validation message for AzureMarketplace

Message references “RequiredMember” instead of the actual discriminator value.

-// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'AzureMarketplace' ?  has(self.azureMarketplace) : !has(self.azureMarketplace)",message="azureMarketplace is required when type is RequiredMember, and forbidden otherwise"
+// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'AzureMarketplace' ?  has(self.azureMarketplace) : !has(self.azureMarketplace)",message="azureMarketplace is required when type is AzureMarketplace, and forbidden otherwise"
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-TechPreviewNoUpgrade.crd.yaml (1)

4728-4735: Making network required may block edits to existing clusters; confirm upgrade impact.

Adding network to the required list is a backward-incompatible schema change for existing self-managed Azure HCPs that lack this field—they will fail future spec updates. If that’s acceptable for TechPreview, OK. Otherwise, consider staging this as optional for one release and enforce via controllers, or add migration docs.

If you choose to soften the rollout, drop network from required here:

   required:
   - cloudProvider
   - disk
   - file
   - imageRegistry
   - ingress
-  - network
   - nodePoolManagement

Then enforce in code/admission (or via CEL) only for new clusters and document the migration path.

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml (1)

3965-3985: Add format: uuid to clientID for better tooling hints.

Schema already enforces UUID via pattern/length; adding format improves UX without changing validation.

           clientID:
             description: clientID is client ID of a federated
               managed identity used in workload identity authentication
+            format: uuid
             maxLength: 36
             minLength: 36
             pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
             type: string
hypershift-operator/controllers/manifests/azure.go (2)

12-18: Set Secret.Type to Opaque.

These constructors only set metadata; default Type is empty. Explicitly setting Opaque avoids downstream assumptions.

Apply:

 return &corev1.Secret{
   ObjectMeta: metav1.ObjectMeta{
     Namespace: controlPlaneNamespace,
     Name:      "cloud-network-config-controller-creds",
   },
+  Type: corev1.SecretTypeOpaque,
 }

Repeat the same addition for:

  • "azure-cloud-config"
  • "azure-ingress-credentials"
  • "azure-image-registry-credentials"
  • "azure-disk-csi-config"
  • "azure-file-csi-config"
...same Type line for each function's returned Secret...

Also applies to: 22-28, 32-38, 42-48, 52-58, 62-68


20-28: Rename for clarity or align name/content.

Function name says “Credentials” but the Secret is “azure-cloud-config”. Consider renaming to AzureCloudProviderConfigSecret (or change Secret name) for consistency.

control-plane-operator/controllers/hostedcontrolplane/v2/capi_provider/component.go (1)

3-10: Use metav1.NamespaceSystem instead of string literal.

Minor: prefer the constant for kube-system.

Apply:

 import (
   hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
   component "github.com/openshift/hypershift/support/controlplane-component"
   "github.com/openshift/hypershift/support/util"
 
   appsv1 "k8s.io/api/apps/v1"
   rbacv1 "k8s.io/api/rbac/v1"
+  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
@@
-      ServiceAccountNameSpace: "kube-system",
+      ServiceAccountNameSpace: metav1.NamespaceSystem,

Also applies to: 63-63

control-plane-operator/controllers/hostedcontrolplane/v2/assets/cloud-controller-manager-azure/serviceaccount.yaml (1)

1-4: Fix YAML lint: trim trailing space and add EOF newline

Removes trailing space after the ServiceAccount name and adds a newline at EOF.

 apiVersion: v1
 kind: ServiceAccount
 metadata:
-  name: azure-cloud-controller-manager 
+  name: azure-cloud-controller-manager
+ 
support/controlplane-component/token-minter-container.go (2)

90-95: Use token-specific mount path to reduce confusion

Align the token-minter mount path with the token type to avoid surprises when debugging or extending.

- tokenFileMountPath := "/var/run/secrets/openshift/serviceaccount"
-
- var audience string
- switch tokenType {
- case CloudToken:
-   audience = "openshift"
- case KubeAPIServerToken:
-   audience = hcp.Spec.IssuerURL
- }
+ var (
+   tokenFileMountPath string
+   audience           string
+ )
+ switch tokenType {
+ case CloudToken:
+   audience = "openshift"
+   tokenFileMountPath = cloudTokenFileMountPath
+ case KubeAPIServerToken:
+   audience = hcp.Spec.IssuerURL
+   tokenFileMountPath = kubeAPITokenFileMountPath
+ }

58-61: Use context-aware ARO detection or ensure env var injection
azureutil.IsAroHCP() returns os.Getenv("MANAGED_SERVICE")==hyperv1.AroHCP, relying solely on global state rather than the cpContext.HCP being processed. Ensure the MANAGED_SERVICE env var is set correctly on the control-plane-component container for each HCP, or refactor to read the managed-service type directly from cpContext.HCP.Spec.Platform.Azure to avoid relying on global state.

control-plane-operator/controllers/hostedcontrolplane/v2/assets/cloud-controller-manager-azure/deployment.yaml (1)

47-48: ServiceAccount + token automount: confirm WI injection; consider reducing token exposure

Ensure the referenced ServiceAccount exists and is annotated for Azure Workload Identity so the mutating webhook injects AZURE_* env and the federated token. If the webhook does not require the default legacy SA token, consider:

  • Setting automountServiceAccountToken: false to minimize token exposure.
  • Relying on the projected federated token added by the WI webhook.

Please confirm webhook requirements before changing.

control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/component.go (1)

85-87: Minor: verify azureutil.IsAroHCP() doesn’t need context

If future detection needs HCP context, consider accepting cpContext or switching to a predicate that returns (bool, error) for parity with others.

cmd/infra/azure/create.go (2)

284-295: Redundant nested condition and JSON-only parsing

  • The inner if o.WorkloadIdentitiesFile != "" is redundant.
  • Prefer yaml.Unmarshal to accept both YAML and JSON (YAML is a superset), matching other inputs.

Apply:

-  if o.WorkloadIdentitiesFile != "" {
-    if o.WorkloadIdentitiesFile != "" {
-      workloadIdentitiesRaw, err := os.ReadFile(o.WorkloadIdentitiesFile)
-      if err != nil {
-        return nil, fmt.Errorf("failed to read --workload-identities-file %s: %w", o.WorkloadIdentitiesFile, err)
-      }
-      result.WorkloadIdentities = &hyperv1.AzureWorkloadIdentities{}
-      if err := json.Unmarshal(workloadIdentitiesRaw, result.WorkloadIdentities); err != nil {
-        return nil, fmt.Errorf("failed to unmarshal --workload-identities-file: %w", err)
-      }
-    }
-  }
+  if o.WorkloadIdentitiesFile != "" {
+    workloadIdentitiesRaw, err := os.ReadFile(o.WorkloadIdentitiesFile)
+    if err != nil {
+      return nil, fmt.Errorf("failed to read --workload-identities-file %s: %w", o.WorkloadIdentitiesFile, err)
+    }
+    result.WorkloadIdentities = &hyperv1.AzureWorkloadIdentities{}
+    if err := yaml.Unmarshal(workloadIdentitiesRaw, result.WorkloadIdentities); err != nil {
+      return nil, fmt.Errorf("failed to unmarshal --workload-identities-file: %w", err)
+    }
+  }

313-329: Idempotent role assignment path is good; consider small hardening

Loop+assign flow is fine. Consider bubbling context into Graph lookups and setting an HTTP client timeout to prevent hangs. I can send a follow-up patch if desired.

control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go (2)

1653-1661: Verify Azure secret key schema and centralize base keys.

  • Please confirm the consumers (CSI, ingress/operator, CNCC, CCM) expect these exact keys (notably azure_region vs azure_location, and whether azure_client_id must be present). If helpers inject azure_client_id per workload identity, note that here in a comment.
  • Consider moving construction of the common base map into azureresources to keep the contract in one place and reduce drift if keys evolve.

Would you like a follow-up patch that lifts this base map into azureresources?


2764-2771: ARO path should clean up previously-created Azure CSIDriver CRs.

When switching code paths or upgrading existing clusters, leaving ClusterCSIDriver objects around can cause unintended reconciliation. Proactively delete them in the ARO branch.

Proposed change:

 case hyperv1.AzurePlatform:
-    if !azureutil.IsAroHCP() {
+    if !azureutil.IsAroHCP() {
         driverNames = []operatorv1.CSIDriverName{
             operatorv1.AzureDiskCSIDriver,
             operatorv1.AzureFileCSIDriver,
         }
-    }
+    } else {
+        // Ensure any previously-created CSIDriver CRs are removed on ARO.
+        for _, name := range []operatorv1.CSIDriverName{
+            operatorv1.AzureDiskCSIDriver,
+            operatorv1.AzureFileCSIDriver,
+        } {
+            if _, err := util.DeleteIfNeeded(ctx, r.client, manifests.ClusterCSIDriver(name)); err != nil {
+                errs = append(errs, fmt.Errorf("failed to delete ClusterCSIDriver %s: %w", name, err))
+            }
+        }
+    }
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml (1)

4023-4043: Azure workload identity "network": schema looks consistent with peers. Consider uniqueness guard.

  • The new WorkloadIdentity object mirrors existing entries (UUID format, min/max length, CEL check). LGTM.
  • Optional: add a top-level CEL validation at azure.workloadIdentities to ensure all clientIDs (cloudProvider, disk, file, imageRegistry, ingress, network, nodePoolManagement) are distinct to avoid accidental reuse.
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml (1)

4064-4084: Addition of workloadIdentities.network looks consistent; minor schema nits

  • Schema matches sibling blocks (uuid pattern, length, required: clientID). Looks good.
  • Optional: add format: uuid for better client/tooling hints, or drop redundant min/max length since the regex enforces them.

Apply one of these diffs:

Option A (add format):

   clientID:
     description: clientID is client ID of a federated
       managed identity used in workload identity authentication
-    maxLength: 36
+    maxLength: 36
     minLength: 36
     pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
+    format: uuid
     type: string

Option B (remove redundant length constraints):

   clientID:
     description: clientID is client ID of a federated
       managed identity used in workload identity authentication
-    maxLength: 36
-    minLength: 36
     pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
     type: string
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-TechPreviewNoUpgrade.crd.yaml (1)

4836-4842: Required list updated to include network: ordering consistent; confirm admission won’t brick existing clusters.

If any clusters with WorkloadIdentities exist without network, updates to their HostedCluster will fail validation. If that’s acceptable under TechPreviewNoUpgrade, document it; otherwise consider a transitional validation (e.g., allow missing on update when oldSelf lacked it) or provide an upgrade controller to populate it.

If you want a softer transition, relax required and add a CEL rule like:

-                            required:
-                            - cloudProvider
-                            - disk
-                            - file
-                            - imageRegistry
-                            - ingress
-                            - network
-                            - nodePoolManagement
+                            x-kubernetes-validations:
+                            - message: network must be set for new clusters using WorkloadIdentities
+                              rule: 'has(self.network) || has(oldSelf) && has(oldSelf.network)'
+                            required:
+                            - cloudProvider
+                            - disk
+                            - file
+                            - imageRegistry
+                            - ingress
+                            - nodePoolManagement

(This keeps new creates strict while allowing updates on legacy objects; once migrated, you can restore hard-required in a later release.)

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml (1)

4439-4440: Requiring network is a breaking change—verify backward compatibility and docs

Making network mandatory under WorkloadIdentities can reject existing HostedClusters that lack it. Ensure examples/e2e are updated, admission/migration is handled, and release notes call this out.

I can draft a short migration note and update the example manifests to include the new network clientID on request.

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml (2)

3969-3986: Tiny wording and OpenAPI hint: tweak description and add uuid format.

Improve UX for generators/validators by adding format: uuid, and fix article in description.

-                                  clientID:
-                                    description: clientID is client ID of a federated
+                                  clientID:
+                                    description: clientID is the client ID of a federated
                                       managed identity used in workload identity authentication
+                                    format: uuid

4011-4017: Adding ‘network’ to required is a breaking API change—confirm migration plan.

Existing self-managed Azure clusters using WorkloadIdentities but lacking ‘network’ will fail validation on upgrade. If intentional, document clearly and ensure controllers/docs/examples cover it; otherwise consider a one-release deprecation window (optional field + controller validation) before making it required.

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml (1)

4131-4151: Clarify validation message to match “federated managed identity” wording.

The description explicitly says this is a federated managed identity. The validation message still says “managed identity.” Align it for precision.

-                                    - message: the client ID of a managed identity
+                                    - message: the client ID of a federated managed identity
                                         must be a valid UUID. It should be 5 groups
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml (1)

4464-4484: Azure workloadIdentities.network field looks correct; consider adding format: uuid for extra validation clarity

The UUID regex + min/max length are good. Adding OpenAPI format can improve tooling UX.

Apply via kubebuilder tag on the source type (then re-generate CRDs); expected generated change:

-                                  clientID:
+                                  clientID:
                                     description: clientID is client ID of a federated
                                       managed identity used in workload identity authentication
                                     maxLength: 36
                                     minLength: 36
                                     pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
+                                    format: uuid
                                     type: string
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml (1)

3959-3964: Nit: tighten description grammar for clientID.

Minor wording polish for consistency with other fields.

-                                    description: clientID is client ID of a federated
-                                      managed identity used in workload identity authentication
+                                    description: clientID is the client ID of a federated
+                                      managed identity used in workload identity authentication.
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml (1)

4102-4123: Network workload identity shape looks correct; consider adding format: uuid.

The new workloadIdentities.network.clientID mirrors the other identities (length/pattern/CEL). Optional nit: add format: uuid to improve tooling UX (it won’t change server-side validation).

Apply:

           clientID:
             description: clientID is client ID of a federated managed identity used in workload identity authentication
             maxLength: 36
             minLength: 36
             pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
+            format: uuid
             type: string
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/DynamicResourceAllocation.yaml (1)

4076-4096: Add Azure workload identity for Network operator — looks consistent; consider tiny schema/docs polish.

  • Schema mirrors existing WI blocks (uuid regex, lengths, validation); good.
  • Nit: the validation message says “managed identity” while the description uses “federated managed identity”. Consider aligning wording.
  • Optional: add OpenAPI format: uuid alongside the regex for better tooling support.

Proposed diff (wording + format):

   properties:
     clientID:
       description: clientID is client ID of a federated
         managed identity used in workload identity authentication
       maxLength: 36
       minLength: 36
       pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
+      format: uuid
       type: string
       x-kubernetes-validations:
-      - message: the client ID of a managed identity
+      - message: the client ID of a federated managed identity
           must be a valid UUID. It should be 5 groups
           of hyphen separated hexadecimal characters
           in the form 8-4-4-4-12.
         rule: self.matches('^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml (1)

4283-4303: Azure WI: network identity addition looks correct; consider adding format: uuid.

Schema and CEL mirror existing fields. Adding format: uuid improves tooling/UX and remains backward-compatible.

   properties:
     clientID:
       description: clientID is client ID of a federated
         managed identity used in workload identity authentication
+      format: uuid
       maxLength: 36
       minLength: 36
       pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
       type: string
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml (1)

4055-4075: New Azure workload identity "network": schema looks consistent

Matches existing pattern for workload identity fields (UUID-validated clientID, required: clientID). No functional concerns.

Optional: add format: uuid alongside the regex to aid tooling, mirroring other places in this CRD that declare UUIDs.

           clientID:
             description: clientID is client ID of a federated
               managed identity used in workload identity authentication
             maxLength: 36
             minLength: 36
             pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
+            format: uuid
             type: string
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml (1)

3947-3967: Network workload identity field looks correct; UUID validation consistent with peers

The new workloadIdentities.network object with clientID and required validates as a 36-char UUID and matches the pattern used for other Azure client IDs in this CRD. Good addition and descriptions align with cluster-network-operator usage.

Please verify companion changes exist:

  • Go type: AzureWorkloadIdentities has Network field (json:"network,omitempty") in api/hypershift/v1beta1/azure.go.
  • DeepCopy and applyconfig updated.
  • HostedCluster and any other CRDs exposing workloadIdentities also include network.

If desired, you could optionally add format: uuid alongside the regex for better tooling support.

   clientID:
     description: clientID is client ID of a federated managed identity used in workload identity authentication
     maxLength: 36
     minLength: 36
+    format: uuid
     pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
     type: string
cmd/cluster/azure/create.go (2)

70-73: Flag help should reflect both identity modes

Users may supply workload-identities; the role-assignment help still says “managed identities”.

Example:

-flags.BoolVar(&opts.AssignServicePrincipalRoles, "assign-service-principal-roles", opts.AssignServicePrincipalRoles, "Assign the service principal roles to the managed identities.")
+flags.BoolVar(&opts.AssignServicePrincipalRoles, "assign-service-principal-roles", opts.AssignServicePrincipalRoles, "Assign necessary roles to the identities (managed or workload).")

271-279: Prefer typed constants over string literals for AzureAuthenticationConfigType

Use API constants if available to prevent drift.

Example:

- AzureAuthenticationConfigType: "WorkloadIdentities",
+ AzureAuthenticationConfigType: hyperv1.AzureAuthTypeWorkloadIdentities, // verify exact identifier

Repeat for "ManagedIdentities".

control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure.go (1)

15-72: Reduce duplication between managed/self-managed credential setup

Both functions differ only in the source of client IDs. Consider a small strategy function or parameterized struct to pick IDs, and a single reconcile loop over targets (ingress/disk/registry/file).

I can draft this refactor if desired.

Also applies to: 74-125

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml (1)

4207-4227: Addition of workloadIdentities.network looks consistent, but consider using format: uuid.

Schema matches sibling identities. Optionally add format: uuid to aid clients.

           clientID:
             description: clientID is client ID of a federated
               managed identity used in workload identity authentication
+            format: uuid
             maxLength: 36
             minLength: 36
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml (1)

4438-4454: Optional: add format: uuid for tooling discoverability

OpenAPI consumers sometimes surface format hints in UIs/linting. Adding format: uuid (while keeping the regex) can help, with no behavior change.

Apply this minimal diff:

           clientID:
             description: clientID is client ID of a federated
               managed identity used in workload identity authentication
             maxLength: 36
             minLength: 36
+            format: uuid
             pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
             type: string
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml (1)

4099-4119: Consider adding format: uuid to clientID for better tooling and consistency

The regex + length checks are good. Adding format: uuid can help IDEs and schema-aware tooling without changing validation behavior.

Apply:

           clientID:
             description: clientID is client ID of a federated
               managed identity used in workload identity authentication
             maxLength: 36
             minLength: 36
+            format: uuid
             pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
             type: string
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml (1)

3999-4011: Optional: add format hint for UUID

Consider adding format: uuid to clientID to improve tooling/UX; keep the regex as-is.

           clientID:
-            description: clientID is client ID of a federated managed identity used in workload identity authentication
+            description: clientID is client ID of a federated managed identity used in workload identity authentication
             maxLength: 36
             minLength: 36
             pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
-            type: string
+            type: string
+            format: uuid
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-CustomNoUpgrade.crd.yaml (1)

4925-4932: Requiring workloadIdentities.network may break existing CRs; consider softer rollout

Marking network as required tightens validation for all self-managed Azure clusters using WorkloadIdentities. Existing HostedClusters that don’t set it will fail future edits. Recommend a staged approach (optional first with controller warning/condition, then enforced in a later release) to avoid upgrade friction.

If you choose to defer hard validation now, apply:

@@
                             required:
-                            - cloudProvider
-                            - disk
-                            - file
-                            - imageRegistry
-                            - ingress
-                            - network
-                            - nodePoolManagement
+                            - cloudProvider
+                            - disk
+                            - file
+                            - imageRegistry
+                            - ingress
+                            - nodePoolManagement

And surface a clear Degraded/Warning condition from the controllers when network is missing. Also add a migration note in docs/examples indicating from which release the field becomes mandatory.

hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go (1)

305-321: Embed cloud.conf or remove unused helper
No calls to addCloudConfigToSecret, so the CNCC secret never includes a cloud.conf key. Either invoke addCloudConfigToSecret(secretData, hcluster) in the reconciliation closure or delete addCloudConfigToSecret (and related constants) to avoid dead code.

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml (1)

4015-4015: Adding network to the required list may break existing CRs on upgrade — confirm rollout plan.

  • Making a new property required in a served storage version (v1beta1) can invalidate edits to older objects that don’t have it. Ensure:
    • Existing HostedClusters using WorkloadIdentities can be updated without adding network immediately, or
    • You’re comfortable gating this for 4.20 with clear migration docs, or
    • Consider making it optional for one release and enforcing via controller validation, then flipping to required later.

If you prefer to keep API compatibility for a cycle, drop “- network” from required and enforce via reconciliation/validation:

   required:
    - cloudProvider
    - disk
    - file
    - imageRegistry
    - ingress
-   - network
    - nodePoolManagement

I can also add an x-kubernetes-validations rule to soft-require network when azureAuthenticationConfigType == WorkloadIdentities if you want it enforced at CRD level.

control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/config.go (2)

113-117: Use filepath.Join for path safety

Safer and portable than string concatenation, avoids double/missing slashes.

Apply this diff:

-			azureConfig.AADMSIDataPlaneIdentityPath = config.ManagedAzureCertificatePath + hcp.Spec.Platform.Azure.AzureAuthenticationConfig.ManagedIdentities.ControlPlane.CloudProvider.CredentialsSecretName
+			azureConfig.AADMSIDataPlaneIdentityPath = filepath.Join(
+				config.ManagedAzureCertificatePath,
+				hcp.Spec.Platform.Azure.AzureAuthenticationConfig.ManagedIdentities.ControlPlane.CloudProvider.CredentialsSecretName,
+			)

Add import:

import "path/filepath"

128-154: Tighten JSON tags for optional/sensitive fields and verify tag names

  • Add omitempty to optional/sensitive fields to avoid emitting empty values in logs/manifests.
  • Please double-check the exact key names against cloud-provider-azure config; mismatched keys are silently ignored.

Apply this diff (subset focusing on sensitive/optional fields):

-	UseManagedIdentityExtension           bool   `json:"useManagedIdentityExtension"`
-	UseFederatedWorkloadIdentityExtension bool   `json:"useFederatedWorkloadIdentityExtension"`
+	UseManagedIdentityExtension           bool   `json:"useManagedIdentityExtension,omitempty"`
+	UseFederatedWorkloadIdentityExtension bool   `json:"useFederatedWorkloadIdentityExtension,omitempty"`
 	SubscriptionID                        string `json:"subscriptionId"`
 	AADClientID                           string `json:"aadClientId"`
 	// TODO HOSTEDCP-1542 - Bryan - drop client secret once we have WorkloadIdentity working
-	AADClientSecret                           string `json:"aadClientSecret"`
-	AADClientCertPath                         string `json:"aadClientCertPath"`
-	AADFederatedTokenFile                     string `json:"aadFederatedTokenFile"`
-	AADMSIDataPlaneIdentityPath               string `json:"aadMSIDataPlaneIdentityPath"`
+	AADClientSecret                           string `json:"aadClientSecret,omitempty"`
+	AADClientCertPath                         string `json:"aadClientCertPath,omitempty"`
+	AADFederatedTokenFile                     string `json:"aadFederatedTokenFile,omitempty"`
+	AADMSIDataPlaneIdentityPath               string `json:"aadMSIDataPlaneIdentityPath,omitempty"`
 	...
-	RouteTableName                            string `json:"routeTableName"`
+	RouteTableName                            string `json:"routeTableName,omitempty"`
-	CloudProviderBackoff                      bool   `json:"cloudProviderBackoff"`
-	CloudProviderBackoffDuration              int    `json:"cloudProviderBackoffDuration"`
-	UseInstanceMetadata                       bool   `json:"useInstanceMetadata"`
+	CloudProviderBackoff                      bool   `json:"cloudProviderBackoff,omitempty"`
+	CloudProviderBackoffDuration              int    `json:"cloudProviderBackoffDuration,omitempty"`
+	UseInstanceMetadata                       bool   `json:"useInstanceMetadata,omitempty"`
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b820ba3 and 857fa6d.

📒 Files selected for processing (49)
  • api/hypershift/v1beta1/azure.go (1 hunks)
  • api/hypershift/v1beta1/zz_generated.deepcopy.go (1 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/DynamicResourceAllocation.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml (2 hunks)
  • client/applyconfiguration/hypershift/v1beta1/azureworkloadidentities.go (2 hunks)
  • cmd/cluster/azure/create.go (6 hunks)
  • cmd/infra/azure/create.go (3 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-CustomNoUpgrade.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-TechPreviewNoUpgrade.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-CustomNoUpgrade.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-TechPreviewNoUpgrade.crd.yaml (2 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/cloud-controller-manager-azure/deployment.yaml (2 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/cloud-controller-manager-azure/serviceaccount.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/capi_provider/component.go (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/component.go (3 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/config.go (3 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/storage/azure.go (4 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/storage/component.go (0 hunks)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure.go (1 hunks)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/creds.go (1 hunks)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go (3 hunks)
  • docs/content/reference/api.md (1 hunks)
  • examples/workload-identities-example.json (1 hunks)
  • hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go (7 hunks)
  • hypershift-operator/controllers/hostedcluster/internal/platform/platform.go (1 hunks)
  • hypershift-operator/controllers/manifests/azure.go (1 hunks)
  • support/controlplane-component/token-minter-container.go (3 hunks)
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/azure.go (1 hunks)
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go (1 hunks)
💤 Files with no reviewable changes (1)
  • control-plane-operator/controllers/hostedcontrolplane/v2/storage/component.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-29T18:49:17.570Z
Learnt from: CR
PR: openshift/hypershift#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T18:49:17.570Z
Learning: Applies to hypershift-operator/controllers/**/*.go : Place operator controller implementations under hypershift-operator/controllers/

Applied to files:

  • control-plane-operator/controllers/hostedcontrolplane/v2/storage/azure.go
  • hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go
🪛 YAMLlint (1.37.1)
control-plane-operator/controllers/hostedcontrolplane/v2/assets/cloud-controller-manager-azure/serviceaccount.yaml

[error] 4-4: no new line character at the end of file

(new-line-at-end-of-file)


[error] 4-4: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Red Hat Konflux / control-plane-operator-main-on-pull-request
  • GitHub Check: Red Hat Konflux / hypershift-operator-main-on-pull-request

Comment thread api/hypershift/v1beta1/azure.go
Comment thread hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go Outdated
Comment thread hypershift-operator/controllers/manifests/azure.go
Comment thread hypershift-operator/controllers/manifests/azure.go
- Address compilation and import issues in test code
- Update test fixtures/mocks and fix failing assertions
- Stabilize timing-dependent tests to reduce flakiness
- `make unit` now runs successfully

Signed-off-by: Bryan Cox <brcox@redhat.com>
Assisted-by: GPT-5 (via Cursor)
…ion for Azure

Remove ARO-only guard so registry management state admission policy and binding
are deleted during HostedCluster teardown.

Signed-off-by: Bryan Cox <brcox@redhat.com>
Assisted-by: GPT-5 (via Cursor)
Deduplicate secret creation logic used by control-plane-operator and hypershift-operator by centralizing it in a single helper.
This reduces code duplication, ensures consistent labels/owner refs/annotations, and simplifies future maintenance.

Signed-off-by: Bryan Cox <brcox@redhat.com>
Assisted-by: Claude
@devguyio

devguyio commented Sep 22, 2025

Copy link
Copy Markdown
Contributor

LGTM
leaving it to @muraee and @sjenning . Thanks for the great work Bryan !

@devguyio devguyio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm
/hold

looks like I need to remove the "request change" review status, so stamping it and leaving it for other reviewers to unhold

@openshift-ci openshift-ci Bot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. lgtm Indicates that a PR is ready to be merged. labels Sep 22, 2025
@openshift-ci

openshift-ci Bot commented Sep 22, 2025

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bryan-cox, devguyio

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@bryan-cox
bryan-cox marked this pull request as ready for review September 22, 2025 19:39
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 22, 2025
@bryan-cox

Copy link
Copy Markdown
Member Author

/test verify-deps

@openshift-ci
openshift-ci Bot requested review from enxebre and jparrill September 22, 2025 19:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
cmd/infra/azure/create.go (2)

489-503: Pager bug: only first DNS zones page is checked

If the base domain isn’t on the first page, it won’t be found. Iterate all pages.

-  pager := zonesClient.NewListPager(nil)
-  if pager.More() {
-    pagerResults, err := pager.NextPage(ctx)
-    if err != nil {
-      return "", fmt.Errorf("failed to retrieve list of DNS zones: %w", err)
-    }
-
-    for _, result := range pagerResults.Value {
-      if *result.Name == baseDomain {
-        return *result.ID, nil
-      }
-    }
-  }
-  return "", fmt.Errorf("could not find any DNS zones in subscription")
+  pager := zonesClient.NewListPager(nil)
+  for pager.More() {
+    page, err := pager.NextPage(ctx)
+    if err != nil {
+      return "", fmt.Errorf("failed to retrieve list of DNS zones: %w", err)
+    }
+    for _, z := range page.Value {
+      if z.Name != nil && strings.EqualFold(*z.Name, baseDomain) {
+        return *z.ID, nil
+      }
+    }
+  }
+  return "", fmt.Errorf("could not find DNS zone %q in subscription", baseDomain)

70-75: Expose CLI flag --workload-identities-file in cmd/infra/azure/create.go

opts.WorkloadIdentitiesFile is defined and read in cmd/infra/azure/create.go but not registered as a CLI flag; cmd/cluster/azure/create.go already registers this flag (flags.StringVar at cmd/cluster/azure/create.go:68–71). Add the same registration to the infra create command.

Apply:

@@
   cmd.Flags().StringToStringVarP(&opts.ResourceGroupTags, "resource-group-tags", "t", opts.ResourceGroupTags, "Additional tags to apply to the resource group created (e.g. 'key1=value1,key2=value2')")
+  cmd.Flags().StringVar(&opts.WorkloadIdentitiesFile, "workload-identities-file", opts.WorkloadIdentitiesFile, "Path to workload identities file (YAML or JSON) for federated SP role assignments")
hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure_test.go (1)

29-34: Fix state leakage between subtests: re-create AzureClusterIdentity per case

initialAzureClusterIdentity is shared and mutated across subtests, making results order‑dependent. Create a fresh object inside each t.Run.

- initialAzureClusterIdentity := &capiazure.AzureClusterIdentity{
-   ObjectMeta: metav1.ObjectMeta{
-     Name:      "test-cluster-identity",
-     Namespace: controlPlaneNamespace,
-   },
- }
+ // (moved into each subtest)

@@
   for _, tc := range testCases {
     t.Run(tc.name, func(t *testing.T) {
       g := NewWithT(t)
 
+      // Fresh identity object per subtest
+      initialAzureClusterIdentity := &capiazure.AzureClusterIdentity{
+        ObjectMeta: metav1.ObjectMeta{
+          Name:      "test-cluster-identity",
+          Namespace: controlPlaneNamespace,
+        },
+      }
+
       if tc.isManagedService {
         t.Setenv("MANAGED_SERVICE", hyperv1.AroHCP)
       }

Also applies to: 121-133

control-plane-operator/controllers/hostedcontrolplane/v2/storage/azure.go (1)

46-52: Prevent nil-map write on Secret.Data.

Initialize Secret.Data to avoid panics on first reconcile if manifest didn’t pre‑seed Data.

Apply:

-serializedConfig, err := json.MarshalIndent(azureConfig, "", "  ")
+serializedConfig, err := json.MarshalIndent(azureConfig, "", "  ")
 if err != nil {
     return fmt.Errorf("failed to serialize cloudconfig: %w", err)
 }
 
-secret.Data[azure.ConfigKey] = serializedConfig
+if secret.Data == nil {
+    secret.Data = make(map[string][]byte, 1)
+}
+secret.Data[azure.ConfigKey] = serializedConfig
🧹 Nitpick comments (36)
control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/zz_fixture_TestControlPlaneComponents_azure_file_csi_config_secret.yaml (3)

4-4: Add type: Opaque to reduce defaulting-related brittleness.

Kubernetes defaults the Secret type; making it explicit keeps fixtures resilient to compare logic that doesn’t normalize defaults.

 kind: Secret
+type: Opaque

16-16: resourceVersion in fixtures can be fragile.

If tests perform deep equality without stripping server-managed fields, resourceVersion: "1" may cause churn. Confirm test harness normalization; otherwise consider dropping it.


1-17: Gitleaks false positive on Secret testdata.

This is expected testdata, not real creds, but scanners will flag it. Add/extend an allowlist (path-based rule for testdata secrets) to prevent noisy CI.

control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_deployment.yaml (1)

96-99: Mount cloud-token as readOnly in CCM.

CCM only reads the minted token; make the volume mount read-only to tighten pod security.

-        - mountPath: /var/run/secrets/openshift/serviceaccount
-          name: cloud-token
+        - mountPath: /var/run/secrets/openshift/serviceaccount
+          name: cloud-token
+          readOnly: true
control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml (3)

9-16: Drop server‑populated fields from fixture (uid, resourceVersion).

These are apiserver‑managed and can cause brittle golden updates; most fixtures omit them.

Apply:

   ownerReferences:
   - apiVersion: hypershift.openshift.io/v1beta1
     blockOwnerDeletion: true
     controller: true
     kind: HostedControlPlane
     name: hcp
-    uid: ""
-  resourceVersion: "1"

4-4: Set Secret type explicitly.

Be explicit for readability and future consumers; defaults to Opaque, but let’s state it.

 kind: Secret
+type: Opaque

2-4: Gitleaks false positive: mark testdata in allowlist.

This is non‑sensitive fixture data. Silence CI noise by allowlisting this path in .gitleaks config.

# .gitleaks.toml
[allowlist]
paths = [
  "control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml"
]
control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml (3)

4-16: Secret type not set; be explicit.

Add type: Opaque to reduce ambiguity and align with other fixtures.

Apply:

   resourceVersion: "1"
+type: Opaque

2-3: Gitleaks false positive: add allowlist for testdata Secret.

This is a non‑sensitive test fixture, but it trips the kubernetes-secret-yaml rule. Add a repo allowlist for this path to avoid noise.

Example .gitleaks.toml snippet:

[allowlist]
  description = "Allow k8s Secret fixtures under testdata"
  paths = [
    '''control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret\.yaml$'''
  ]

1-3: Optional: add breadcrumbs for maintainers.

A tiny header comment helps future readers decode/verify the payload without hunting tooling.

Apply:

+## TEST FIXTURE: contains no real credentials.
+## Decode with: yq -r '.data["cloud.conf"]' <file> | base64 -d | jq .
 apiVersion: v1
 data:
control-plane-operator/controllers/hostedcontrolplane/v2/assets/azure-cloud-controller-manager/serviceaccount.yaml (1)

4-4: Trim trailing whitespace in name

There’s a trailing space after the name. Clean it up to avoid churn in diffs.

Apply this diff:

-  name: azure-cloud-controller-manager 
+  name: azure-cloud-controller-manager
cmd/infra/azure/create.go (3)

90-91: Avoid nulls in output when not provided

Add omitempty so the YAML/JSON output doesn’t contain “workloadIdentities: null”.

-    WorkloadIdentities  *hyperv1.AzureWorkloadIdentities        `json:"workloadIdentities"`
+    WorkloadIdentities  *hyperv1.AzureWorkloadIdentities        `json:"workloadIdentities,omitempty"`

284-293: Accept both YAML and JSON for the identities file (consistent with other flags)

Other inputs use yaml.Unmarshal (which also accepts JSON). Align for UX consistency.

-    result.WorkloadIdentities = &hyperv1.AzureWorkloadIdentities{}
-    if err := json.Unmarshal(workloadIdentitiesRaw, result.WorkloadIdentities); err != nil {
+    result.WorkloadIdentities = &hyperv1.AzureWorkloadIdentities{}
+    if err := yaml.Unmarshal(workloadIdentitiesRaw, result.WorkloadIdentities); err != nil {
       return nil, fmt.Errorf("failed to unmarshal --workload-identities-file: %w", err)
     }

998-1014: Harden Graph call: check HTTP status and set Accept header

Prevents decoding non-200 responses as success and improves error clarity.

-  req, err := http.NewRequest("GET", url, nil)
+  req, err := http.NewRequest("GET", url, nil)
   if err != nil {
     return "", fmt.Errorf("failed to create request: %w", err)
   }
@@
-  req.Header.Set("Content-Type", "application/json")
+  req.Header.Set("Content-Type", "application/json")
+  req.Header.Set("Accept", "application/json")
@@
-  // Parse response
+  // Fail fast on non-200
+  if resp.StatusCode != http.StatusOK {
+    body, _ := io.ReadAll(resp.Body)
+    return "", fmt.Errorf("graph servicePrincipals query failed: status=%d body=%s", resp.StatusCode, strings.TrimSpace(string(body)))
+  }
+  // Parse response
control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure_test.go (6)

64-68: Avoid shared mutable baseSecretData across subtests

Passing the same map risks hidden coupling if SetupOperandCredentials mutates it. Create a fresh map per subtest.

-	// Base data that should be preserved in all created secrets
-	baseSecretData := map[string][]byte{
-		"some": []byte("data"),
-	}
+	// Base data is created per subtest to avoid shared mutable state
 		for _, tc := range tests {
 			t.Run(tc.name, func(t *testing.T) {
 				g := NewWithT(t)
 				c := fake.NewClientBuilder().WithScheme(api.Scheme).Build()
 
+				baseSecretData := map[string][]byte{
+					"some": []byte("data"),
+				}
+
 				hcp := makeHCP(tc.managedAzure)
 				if tc.disableIngress {
 					hcp.Spec.Capabilities = &hyperv1.Capabilities{Disabled: []hyperv1.OptionalCapability{hyperv1.IngressCapability}}
 				}

Also applies to: 111-122


135-143: Assert NotFound on missing ingress secret

Be precise on the expected failure mode.

 import (
 	"strings"
 	"testing"
 
 	. "github.com/onsi/gomega"
 
 	hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
 	"github.com/openshift/hypershift/support/api"
 	"github.com/openshift/hypershift/support/upsert"
 
 	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 		if tc.expectIngress {
 			g.Expect(err).ToNot(HaveOccurred())
 		} else {
-			g.Expect(err).To(HaveOccurred())
+			g.Expect(apierrors.IsNotFound(err)).To(BeTrue())
 		}

Also applies to: 13-18


205-209: Also assert NotFound here for clarity

-err := c2.Get(t.Context(), ingressKey, &ingressSecret)
-g.Expect(err).To(HaveOccurred())
+err := c2.Get(t.Context(), ingressKey, &ingressSecret)
+g.Expect(apierrors.IsNotFound(err)).To(BeTrue())

210-215: Verify file and registry secrets when ingress is disabled

Currently only disk is checked.

 // Verify other secrets were still created
 diskKey := client.ObjectKey{Namespace: "openshift-cluster-csi-drivers", Name: "azure-disk-credentials"}
 var diskSecret corev1.Secret
 err = c2.Get(t.Context(), diskKey, &diskSecret)
 g.Expect(err).ToNot(HaveOccurred())
+
+fileKey := client.ObjectKey{Namespace: "openshift-cluster-csi-drivers", Name: "azure-file-credentials"}
+var fileSecret corev1.Secret
+err = c2.Get(t.Context(), fileKey, &fileSecret)
+g.Expect(err).ToNot(HaveOccurred())
+
+registryKey := client.ObjectKey{Namespace: "openshift-image-registry", Name: "installer-cloud-credentials"}
+var registrySecret corev1.Secret
+err = c2.Get(t.Context(), registryKey, &registrySecret)
+g.Expect(err).ToNot(HaveOccurred())

111-146: Optional: Run subtests in parallel

Safe after isolating baseSecretData; speeds CI a bit.

 t.Run(tc.name, func(t *testing.T) {
+	t.Parallel()
 	g := NewWithT(t)

164-169: Optional: Add ObjectMeta to hcp for consistency

If owner refs or names are used later, having Name/Namespace avoids surprises.

 hcp := &hyperv1.HostedControlPlane{
+	ObjectMeta: metav1.ObjectMeta{
+		Name:      "hcp",
+		Namespace: "ns",
+	},
 	Spec: hyperv1.HostedControlPlaneSpec{
 		Capabilities: &hyperv1.Capabilities{},
 	},
 }
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-CustomNoUpgrade.crd.yaml (1)

4816-4823: Requiring network is a spec-breaking change for existing WorkloadIdentities HCPs—confirm upgrade path.

Existing HostedControlPlanes using WorkloadIdentities but lacking network will fail future spec updates under the new CRD. If that’s expected, add explicit upgrade/migration notes and ensure CLI/defaulting cover it; otherwise consider gating (feature gate or temporary optional + validation) until all toolchains provide the value.

If acceptable as‑is, please:

  • Confirm no managed clusters will reconcile spec without providing this field.
  • Ensure the installer/CLI rejects configs missing network with a clear error.
  • Add release notes/migration doc for day‑2 edits on legacy objects.
control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/deployment.go (1)

35-37: Prefer a shared constant for the SA name.

Avoid magic strings; define const azureCCMSA = "azure-cloud-controller-manager" near other constants and reference it here to prevent drift.

-        deployment.Spec.Template.Spec.ServiceAccountName = "azure-cloud-controller-manager"
+        deployment.Spec.Template.Spec.ServiceAccountName = azureCCMSA
control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go (3)

293-310: Scope of Azure cleanup: comment/code mismatch.

The comment mentions ARO-only cleanup, but the condition now applies to all Azure. Either narrow the condition to ARO or update the comment/log to reflect the broader behavior.

-        // Delete admission policies during cluster deletion to allow HCCO cleanup operations for ARO HCP
-        if hcp.Spec.Platform.Type == hyperv1.AzurePlatform {
+        // Delete admission policies during cluster deletion to allow HCCO cleanup operations on Azure
+        if hcp.Spec.Platform.Type == hyperv1.AzurePlatform {
           ...
         }

Or, if this was intended to be ARO-only:

-        if hcp.Spec.Platform.Type == hyperv1.AzurePlatform {
+        if azureutil.IsAroHCP() {

1653-1666: Guard against missing Azure fields before building secrets.

If any of Location, ResourceGroupName, SubscriptionID, or TenantID are empty, you’ll silently write partial creds. Validate early and return a clear error.

 case hyperv1.AzurePlatform:
-        // Create a base secret data map with the common Azure credentials
+        // Validate required Azure fields before building secrets
+        if hcp.Spec.Platform.Azure == nil ||
+           hcp.Spec.Platform.Azure.Location == "" ||
+           hcp.Spec.Platform.Azure.ResourceGroupName == "" ||
+           hcp.Spec.Platform.Azure.SubscriptionID == "" ||
+           hcp.Spec.Platform.Azure.TenantID == "" {
+            return []error{fmt.Errorf("incomplete Azure platform configuration: location, resourceGroup, subscriptionID, and tenantID are required")}
+        }
+        // Create a base secret data map with the common Azure credentials
         secretData := map[string][]byte{
             "azure_federated_token_file": []byte("/var/run/secrets/openshift/serviceaccount/token"),
             "azure_region":               []byte(hcp.Spec.Platform.Azure.Location),
             "azure_resource_prefix":      []byte(hcp.Name + "-" + hcp.Spec.InfraID),
             "azure_resourcegroup":        []byte(hcp.Spec.Platform.Azure.ResourceGroupName),
             "azure_subscription_id":      []byte(hcp.Spec.Platform.Azure.SubscriptionID),
             "azure_tenant_id":            []byte(hcp.Spec.Platform.Azure.TenantID),
         }
 
         // Set up the operand credentials for either managed or self-managed Azure environments
         errs = azureresources.SetupOperandCredentials(ctx, r.client, r.CreateOrUpdateProvider, hcp, secretData, azureutil.IsAroHCP())
         if len(errs) > 0 {
             return errs
         }

1653-1666: Consistency: avoid early return in one platform branch.

Other branches accumulate errs and return at the end; this one returns early. Not harmful, but consider consistent flow for readability.

-        errs = azureresources.SetupOperandCredentials(... )
-        if len(errs) > 0 {
-            return errs
-        }
+        errs = append(errs, azureresources.SetupOperandCredentials(... )...)
support/azureutil/azureutil_test.go (1)

344-538: Avoid t.Context() to keep compatibility with older Go toolchains.

Use context.Background() unless you require test cancellation semantics.

-            errs := ReconcileAzureCredentials(
-                t.Context(),
+            errs := ReconcileAzureCredentials(
+                context.Background(),
                 fakeClient,
                 mockCreateOrUpdate,
                 baseSecretData,
                 tt.configs,
                 tt.capabilities,
             )
hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure_test.go (2)

312-357: Strengthen assertions for disabled capabilities case

Also validate the remaining secrets’ azure_client_id values (disk, file, cncc) and shared base data, mirroring the first test’s checks.


373-415: Optional: add idempotency check

Call ReconcileCredentials twice and assert no additional secrets are “created” on the second run (adjust the mock to return OperationResultNone on repeats).

api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml (1)

4064-4084: Add OpenAPI uuid format for clientID; keep regex

clientID already enforces UUID via regex; add format: uuid for better tooling/validation without changing behavior.

Apply this diff:

           clientID:
             description: clientID is client ID of a federated
               managed identity used in workload identity authentication
             maxLength: 36
             minLength: 36
+            format: uuid
             pattern: ^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$
             type: string
control-plane-operator/controllers/hostedcontrolplane/v2/storage/azure.go (1)

39-44: Clarify requirement and fail early when VNetID is missing.

Keep unconditional resolution, but give a precise error if VnetID is empty and update the comment to reflect the broader requirement (both ARO and self‑managed).

Apply:

-// aro hcp csi nfs protocol provision volumes needs the vnetName/vnetResourceGroup config
+// Azure CSI requires vnetName/vnetResourceGroup (ARO and self-managed).
+if azureSpec.VnetID == "" {
+    return fmt.Errorf("azureSpec.vnetID must be set for Azure CSI")
+}
 azureConfig.VnetName, azureConfig.VnetResourceGroup, getVnetNameAndResourceGroupErr = azureutil.GetVnetNameAndResourceGroupFromVnetID(azureSpec.VnetID)
support/azureutil/azureutil.go (1)

361-416: Tighten ReconcileAzureCredentials: var naming, nil-map safety, and key constant.

  • Rename loop var (config → cfg) to avoid shadowing an imported config package.
  • Ensure secret.Data is non‑nil: clone baseSecretData when non‑nil, otherwise use make(map[string][]byte) before assigning/adding keys.
  • Prefer a shared constant for "azure_client_id" only if you can update other occurrences (found literals in support/azureutil/azureutil.go, support/azureutil/azureutil_test.go, hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go, control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure_test.go and related tests).
control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure.go (1)

15-15: Use a clearer sentinel value for the managed-ingress placeholder

fakeClientID may confuse operators/debuggers. Prefer an explicit sentinel like unused.

Apply this diff:

-const placeholderClientID = "fakeClientID"
+const placeholderClientID = "unused"
cmd/cluster/azure/create.go (2)

131-146: Validate: strong mutual‑exclusion/dependency; consider empty‑config guard.

Current rules look good. Do we ever expect neither managed+data‑plane nor workload identities? If not, add a guard to fail early when all three are empty.

Example:

@@
   if o.ManagedIdentitiesFile != "" && o.DataPlaneIdentitiesFile == "" {
     return nil, fmt.Errorf("--managed-identities-file requires --data-plane-identities-file")
   }
+
+  if o.ManagedIdentitiesFile == "" && o.DataPlaneIdentitiesFile == "" && o.WorkloadIdentitiesFile == "" {
+    return nil, fmt.Errorf("one of --workload-identities-file or the pair (--managed-identities-file, --data-plane-identities-file) must be provided")
+  }

606-611: Avoid wrapping nil in error.

err is nil here; wrapping yields “...: ”.

Apply:

- if rhcosImage == "" {
-   return "", fmt.Errorf("RHCOS VHD image is empty: %w", err)
- }
+ if rhcosImage == "" {
+   return "", fmt.Errorf("RHCOS VHD image is empty")
+ }
hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go (1)

105-145: Mark kubeconfig mount read‑only.

Harden the manager container by making the kubeconfig mount RO.

- {
-   Name:      "svc-kubeconfig",
-   MountPath: "/etc/kubernetes",
- },
+ {
+   Name:      "svc-kubeconfig",
+   MountPath: "/etc/kubernetes",
+   ReadOnly:  true,
+ },
control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_deployment.yaml (1)

92-99: Tighten mounts to read‑only.

Mark kubeconfig mounts RO; token mount should already be RO by design but can remain RW for token updates.

- - mountPath: /etc/kubernetes/kubeconfig
+ - mountPath: /etc/kubernetes/kubeconfig
    name: kubeconfig
+   readOnly: true
@@
- - mountPath: /etc/kubernetes
+ - mountPath: /etc/kubernetes
    name: kubeconfig
+   readOnly: true

Also applies to: 120-125, 137-145

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between bb2c66f and 5d4cc72.

⛔ Files ignored due to path filters (2)
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/azure.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (74)
  • api/hypershift/v1beta1/azure.go (1 hunks)
  • api/hypershift/v1beta1/zz_generated.deepcopy.go (1 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/DynamicResourceAllocation.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml (2 hunks)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml (2 hunks)
  • client/applyconfiguration/hypershift/v1beta1/azureworkloadidentities.go (2 hunks)
  • cmd/cluster/azure/create.go (5 hunks)
  • cmd/cluster/azure/create_test.go (6 hunks)
  • cmd/infra/azure/create.go (3 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-CustomNoUpgrade.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-TechPreviewNoUpgrade.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-CustomNoUpgrade.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml (2 hunks)
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-TechPreviewNoUpgrade.crd.yaml (2 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_config_configmap.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_controlplanecomponent.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_deployment.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_config_configmap.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_controlplanecomponent.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_deployment.yaml (3 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_config_configmap.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_controlplanecomponent.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_deployment.yaml (3 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_file_csi_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_file_csi_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/zz_fixture_TestControlPlaneComponents_azure_file_csi_config_secret.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/ignition-server-proxy/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/assets/azure-cloud-controller-manager/serviceaccount.yaml (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/component.go (3 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/config.go (3 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/cloud_controller_manager/azure/deployment.go (1 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/storage/azure.go (4 hunks)
  • control-plane-operator/controllers/hostedcontrolplane/v2/storage/component.go (2 hunks)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure.go (1 hunks)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure_test.go (1 hunks)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/creds.go (1 hunks)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go (5 hunks)
  • docs/content/reference/api.md (1 hunks)
  • examples/workload-identities-example.json (1 hunks)
  • hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go (5 hunks)
  • hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure_test.go (2 hunks)
  • hypershift-operator/controllers/hostedcluster/internal/platform/platform.go (1 hunks)
  • hypershift-operator/controllers/manifests/azure.go (1 hunks)
  • support/azureutil/azureutil.go (4 hunks)
  • support/azureutil/azureutil_test.go (2 hunks)
  • support/controlplane-component/token-minter-container.go (3 hunks)
✅ Files skipped from review due to trivial changes (2)
  • control-plane-operator/controllers/hostedcontrolplane/testdata/ignition-server-proxy/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_file_csi_config_secret.yaml
🚧 Files skipped from review as they are similar to previous changes (34)
  • control-plane-operator/controllers/hostedcontrolplane/v2/storage/component.go
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_controlplanecomponent.yaml
  • cmd/cluster/azure/create_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests/creds.go
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_controlplanecomponent.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_deployment.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_controlplanecomponent.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_config_secret.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Default.crd.yaml
  • docs/content/reference/api.md
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_config_configmap.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml
  • api/hypershift/v1beta1/azure.go
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/DynamicResourceAllocation.yaml
  • support/controlplane-component/token-minter-container.go
  • api/hypershift/v1beta1/zz_generated.deepcopy.go
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/zz_fixture_TestControlPlaneComponents_azure_cloud_controller_manager_serviceaccount.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_config_configmap.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml
  • hypershift-operator/controllers/manifests/azure.go
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_cloud_config_secret.yaml
  • control-plane-operator/controllers/hostedcontrolplane/testdata/azure-cloud-controller-manager/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_cloud_config_secret.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Default.crd.yaml
  • examples/workload-identities-example.json
  • client/applyconfiguration/hypershift/v1beta1/azureworkloadidentities.go
  • control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/IBMCloud/zz_fixture_TestControlPlaneComponents_azure_file_csi_config_secret.yaml
🧰 Additional context used
🪛 Gitleaks (8.28.0)
control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/TechPreviewNoUpgrade/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml

[high] 2-4: Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments

(kubernetes-secret-yaml)

control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/zz_fixture_TestControlPlaneComponents_azure_disk_csi_config_secret.yaml

[high] 2-4: Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments

(kubernetes-secret-yaml)

control-plane-operator/controllers/hostedcontrolplane/testdata/cluster-storage-operator/zz_fixture_TestControlPlaneComponents_azure_file_csi_config_secret.yaml

[high] 2-4: Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments

(kubernetes-secret-yaml)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Red Hat Konflux / hypershift-release-mce-210-on-pull-request
  • GitHub Check: Red Hat Konflux / hypershift-cli-mce-210-on-pull-request
  • GitHub Check: Red Hat Konflux / hypershift-operator-main-on-pull-request
  • GitHub Check: Red Hat Konflux / control-plane-operator-main-on-pull-request

Comment on lines +4103 to 4104
- network
- nodePoolManagement

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

❓ Verification inconclusive

Making workloadIdentities.network required is a breaking API change. Stage or provide defaulting.

Existing self‑managed Azure clusters using WorkloadIdentities will fail validation on upgrade if they lack the new field. Consider:

  • Make network optional for one release and default to cloudProvider clientID, then enforce later; or
  • Gate enforcement behind a feature flag/version gate.

Minimal schema change to avoid immediate breakage:

   required:
   - cloudProvider
   - disk
   - file
   - imageRegistry
   - ingress
-  - network
   - nodePoolManagement

Do not make workloadIdentities.network required — this is a breaking API change; stage or provide defaulting.

File: api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml (lines 4103–4104)

Requiring workloadIdentities.network will break upgrades for existing self‑managed Azure clusters using WorkloadIdentities that lack this field.

  • Make network optional for one release and default it (e.g., to cloudProvider clientID), then make it required in a later release.
  • Or gate enforcement behind a feature/version flag.

Minimal schema change to avoid immediate breakage:

   required:
   - cloudProvider
   - disk
   - file
   - imageRegistry
   - ingress
-  - network
   - nodePoolManagement
📝 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.

Suggested change
- network
- nodePoolManagement
- nodePoolManagement
🤖 Prompt for AI Agents
In
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml
around lines 4103-4104, the schema currently makes workloadIdentities.network
required which is a breaking change; remove network from the required array so
the field is optional for at least one release, and add a migration/defaulting
strategy: either (a) implement server-side defaulting in the
conversion/defaulting webhook to set network (e.g., to the cloudProvider
clientID) for existing objects when omitted, or (b) gate strict validation
behind a feature/version flag so enforcement can be introduced in a later
release. Ensure the CRD change only drops the required constraint now and that
defaulting or gating logic is added in tandem to avoid upgrade breakage.

- file
- imageRegistry
- ingress
- network

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Requiring network in the CRD is a breaking change; consider a transition.

Making network mandatory will reject updates for existing self-managed Azure clusters lacking this field. Either:

  • defer strictness: don’t add it to required yet and enforce in controllers/CLI, or
  • provide a migration/defaulting path (webhook/defaults) and clearly document upgrade steps.

Proposed non-breaking schema tweak (drop from required for now):

-                            - network

If you intend to keep it required now, confirm:

  • All existing fixtures/tests/manifests are updated.
  • No controller writes to spec on upgrade that would force validation.
  • Release notes call out the breaking requirement.

You can scan repo for objects missing the new field:


🏁 Script executed:

#!/bin/bash
# HostedCluster/HostedControlPlane YAMLs missing workloadIdentities.network
rg -nP --type=yaml -C1 'workloadIdentities:(\n|.)*(cloudProvider|disk|file|imageRegistry|ingress|nodePoolManagement):(\n|.)*clientID:' \
  | rg -nP -v 'network:(\n|.)*clientID:'

Length of output: 0


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Search repo YAMLs: list files that contain `workloadIdentities` and which of those do NOT contain `network` under any workloadIdentities occurrence.
python3 - <<'PY'
import os, sys, subprocess
try:
    import yaml
except Exception:
    subprocess.check_call([sys.executable, "-m", "pip", "install", "--user", "pyyaml"])
    import yaml

files_with = []
missing = []

for root, dirs, files in os.walk('.'):
    for name in files:
        if name.endswith(('.yml', '.yaml')):
            path = os.path.join(root, name)
            try:
                with open(path, 'r', encoding='utf-8') as fh:
                    docs = list(yaml.safe_load_all(fh))
            except Exception:
                # skip files that can't be parsed as YAML
                continue

            found_any = False
            has_network_any = False

            def walk(obj):
                nonlocal found_any, has_network_any
                if isinstance(obj, dict):
                    for k, v in obj.items():
                        if k == 'workloadIdentities':
                            found_any = True
                            if isinstance(v, dict) and 'network' in v:
                                has_network_any = True
                        walk(v)
                elif isinstance(obj, list):
                    for it in obj:
                        walk(it)

            for doc in docs:
                walk(doc)

            if found_any:
                files_with.append(path)
                if not has_network_any:
                    missing.append(path)

# Output results
print(f"FILES_WITH_WORKLOAD_IDENTITIES:{len(files_with)}")
for p in files_with:
    print(p)
print("---")
print(f"FILES_MISSING_NETWORK:{len(missing)}")
for p in missing:
    print(p)
PY

Length of output: 80


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search repo YAMLs: list files that contain `workloadIdentities` and which of those do NOT contain `network`
python3 - <<'PY'
import os, sys, subprocess
try:
    import yaml
except Exception:
    subprocess.check_call([sys.executable, "-m", "pip", "install", "--user", "pyyaml"])
    import yaml

files_with = []
missing = []

for root, dirs, files in os.walk('.'):
    for name in files:
        if name.endswith(('.yml', '.yaml')):
            path = os.path.join(root, name)
            try:
                with open(path, 'r', encoding='utf-8') as fh:
                    docs = list(yaml.safe_load_all(fh))
            except Exception:
                continue

            found_any = False
            has_network_any = False

            stack = docs[:]
            while stack:
                node = stack.pop()
                if isinstance(node, dict):
                    for k, v in node.items():
                        if k == 'workloadIdentities':
                            found_any = True
                            if isinstance(v, dict) and 'network' in v:
                                has_network_any = True
                            elif isinstance(v, list):
                                for it in v:
                                    if isinstance(it, dict) and 'network' in it:
                                        has_network_any = True
                        if isinstance(v, (dict, list)):
                            stack.append(v)
                elif isinstance(node, list):
                    for it in node:
                        stack.append(it)

            if found_any:
                files_with.append(path)
                if not has_network_any:
                    missing.append(path)

print("FILES_WITH_WORKLOAD_IDENTITIES:", len(files_with))
for p in files_with:
    print(p)
print("---")
print("FILES_MISSING_NETWORK:", len(missing))
for p in missing:
    print(p)
PY

# Find any YAML manifests that explicitly include "- network"
echo
echo "FILES_CONTAINING_-_network (yaml files):"
rg -n --hidden --glob '!**/node_modules/**' --type=yaml '\- network\b' || true

# Show the referenced CRD file snippet if it exists
echo
echo "CHECK specific CRD file snippet (if present):"
FILE="api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml"
if [ -f "$FILE" ]; then
  echo "FOUND: $FILE"
  nl -ba -w3 -s' ' "$FILE" | sed -n '4060,4080p'
else
  echo "NOT FOUND: $FILE"
fi

Length of output: 7159


Revert network from required — this is a breaking change (26 generated manifests missing workloadIdentities.network).

Verification: 26 generated CRD/manifests (e.g. api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/... and cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/) do not include workloadIdentities.network; the CRD that added it as required is api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml (around line 4071).

Non-breaking schema tweak (recommended):

-                            - network

If you intend to keep it required now, confirm:

  • all fixtures/tests/manifests have been updated,
  • no controller will write to spec on upgrade (which would trigger validation failures),
  • release notes explicitly call out this breaking requirement.
📝 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.

Suggested change
- network

Comment on lines +96 to 126
- mountPath: /var/run/secrets/openshift/serviceaccount
name: cloud-token
- mountPath: /tmp
name: tmp-dir
- args:
- --token-audience=openshift
- --service-account-namespace=kube-system
- --service-account-name=azure-cloud-provider
- --token-file=/var/run/secrets/openshift/serviceaccount/token
- --kubeconfig=/etc/kubernetes/kubeconfig
command:
- /usr/bin/control-plane-operator
- token-minter
image: token-minter
imagePullPolicy: IfNotPresent
name: cloud-token-minter
resources:
requests:
cpu: 10m
memory: 30Mi
securityContext:
readOnlyRootFilesystem: true
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
- mountPath: /var/run/secrets/openshift/serviceaccount
name: cloud-token
- mountPath: /etc/kubernetes
name: kubeconfig
- mountPath: /tmp
name: tmp-dir
priorityClassName: hypershift-control-plane

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Token audience mismatch in fixture.

Align with Azure WI audience or tests will encode the wrong contract.

- - --token-audience=openshift
+ - --token-audience=api://AzureADTokenExchange
📝 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.

Suggested change
- mountPath: /var/run/secrets/openshift/serviceaccount
name: cloud-token
- mountPath: /tmp
name: tmp-dir
- args:
- --token-audience=openshift
- --service-account-namespace=kube-system
- --service-account-name=azure-cloud-provider
- --token-file=/var/run/secrets/openshift/serviceaccount/token
- --kubeconfig=/etc/kubernetes/kubeconfig
command:
- /usr/bin/control-plane-operator
- token-minter
image: token-minter
imagePullPolicy: IfNotPresent
name: cloud-token-minter
resources:
requests:
cpu: 10m
memory: 30Mi
securityContext:
readOnlyRootFilesystem: true
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
- mountPath: /var/run/secrets/openshift/serviceaccount
name: cloud-token
- mountPath: /etc/kubernetes
name: kubeconfig
- mountPath: /tmp
name: tmp-dir
priorityClassName: hypershift-control-plane
- mountPath: /var/run/secrets/openshift/serviceaccount
name: cloud-token
- mountPath: /tmp
name: tmp-dir
- args:
- --token-audience=api://AzureADTokenExchange
- --service-account-namespace=kube-system
- --service-account-name=azure-cloud-provider
- --token-file=/var/run/secrets/openshift/serviceaccount/token
- --kubeconfig=/etc/kubernetes/kubeconfig
command:
- /usr/bin/control-plane-operator
- token-minter
image: token-minter
imagePullPolicy: IfNotPresent
name: cloud-token-minter
resources:
requests:
cpu: 10m
memory: 30Mi
securityContext:
readOnlyRootFilesystem: true
terminationMessagePolicy: FallbackToLogsOnError
volumeMounts:
- mountPath: /var/run/secrets/openshift/serviceaccount
name: cloud-token
- mountPath: /etc/kubernetes
name: kubeconfig
- mountPath: /tmp
name: tmp-dir
priorityClassName: hypershift-control-plane

Comment on lines +3 to +13
import (
"context"

hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
"github.com/openshift/hypershift/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests"
"github.com/openshift/hypershift/support/azureutil"
"github.com/openshift/hypershift/support/capabilities"
"github.com/openshift/hypershift/support/upsert"

"sigs.k8s.io/controller-runtime/pkg/client"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Import fmt to support new error paths

Required for the proposed nil-safety checks.

Apply this diff:

 import (
   "context"
+  "fmt"
 
   hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
   "github.com/openshift/hypershift/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests"
   "github.com/openshift/hypershift/support/azureutil"
   "github.com/openshift/hypershift/support/capabilities"
   "github.com/openshift/hypershift/support/upsert"
 
   "sigs.k8s.io/controller-runtime/pkg/client"
 )
📝 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.

Suggested change
import (
"context"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
"github.com/openshift/hypershift/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests"
"github.com/openshift/hypershift/support/azureutil"
"github.com/openshift/hypershift/support/capabilities"
"github.com/openshift/hypershift/support/upsert"
"sigs.k8s.io/controller-runtime/pkg/client"
)
import (
"context"
"fmt"
hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
"github.com/openshift/hypershift/control-plane-operator/hostedclusterconfigoperator/controllers/resources/manifests"
"github.com/openshift/hypershift/support/azureutil"
"github.com/openshift/hypershift/support/capabilities"
"github.com/openshift/hypershift/support/upsert"
"sigs.k8s.io/controller-runtime/pkg/client"
)
🤖 Prompt for AI Agents
In
control-plane-operator/hostedclusterconfigoperator/controllers/resources/azure/azure.go
around lines 3 to 13, the new nil-safety/error code paths use fmt but the
package is not imported; add "fmt" to the import block so the file builds and
the error formatting calls resolve (ensure import ordering matches project
conventions).

Comment on lines +205 to +267
// For self-managed Azure with workload identity, instruct Azure SDK to use the minted SA token
if azureutil.IsSelfManagedAzure(hcluster.Spec.Platform.Type) &&
hcluster.Spec.Platform.Azure.AzureAuthenticationConfig.WorkloadIdentities != nil {
deploymentSpec.Template.Spec.Containers[0].Env = append(
deploymentSpec.Template.Spec.Containers[0].Env,
corev1.EnvVar{
Name: "AZURE_FEDERATED_TOKEN_FILE",
Value: "/var/run/secrets/openshift/serviceaccount/token",
},
corev1.EnvVar{
Name: "AZURE_CLIENT_ID",
Value: string(hcluster.Spec.Platform.Azure.AzureAuthenticationConfig.WorkloadIdentities.NodePoolManagement.ClientID),
},
corev1.EnvVar{
Name: "AZURE_TENANT_ID",
Value: hcluster.Spec.Platform.Azure.TenantID,
},
)

// Inject cloud token-minter sidecar and mount token volume for self-managed Azure workload identity
tokenVolume := corev1.Volume{
Name: "cloud-token",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory},
},
}
deploymentSpec.Template.Spec.Volumes = append(deploymentSpec.Template.Spec.Volumes, tokenVolume)

deploymentSpec.Template.Spec.Containers[0].VolumeMounts = append(deploymentSpec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
Name: tokenVolume.Name,
MountPath: "/var/run/secrets/openshift/serviceaccount",
})

deploymentSpec.Template.Spec.Containers = append(deploymentSpec.Template.Spec.Containers, corev1.Container{
Name: "cloud-token-minter",
Image: a.utilitiesImage,
Command: []string{"/usr/bin/control-plane-operator", "token-minter"},
Args: []string{
"--token-audience=openshift",
"--service-account-namespace=kube-system",
"--service-account-name=capi-provider",
"--token-file=/var/run/secrets/openshift/serviceaccount/token",
"--kubeconfig=/etc/kubernetes/kubeconfig",
},
ImagePullPolicy: corev1.PullIfNotPresent,
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("10m"),
corev1.ResourceMemory: resource.MustParse("30Mi"),
},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: tokenVolume.Name,
MountPath: "/var/run/secrets/openshift/serviceaccount",
},
{
Name: "svc-kubeconfig",
MountPath: "/etc/kubernetes",
},
},
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

❓ Verification inconclusive

Token audience likely wrong for Azure Workload Identity.

Azure WI expects SA tokens with audience api://AzureADTokenExchange. Using “openshift” will fail token exchange.

- "--token-audience=openshift",
+ "--token-audience=api://AzureADTokenExchange",

Also mark kubeconfig mounts RO in both containers:

  {
    Name:      "svc-kubeconfig",
    MountPath: "/etc/kubernetes",
+   ReadOnly:  true,
  },

(Optional) set AZURE_AUTHORITY_HOST for sovereign clouds to improve portability.


Set Azure Workload Identity token audience and make kubeconfig mounts read-only.

Azure Workload Identity requires the federated SA token audience be api://AzureADTokenExchange (USGov/China variants available). (learn.microsoft.com)

Replace:

- "--token-audience=openshift",
+ "--token-audience=api://AzureADTokenExchange",

Also mark the kubeconfig mounts ReadOnly in both containers:

  {
    Name:      "svc-kubeconfig",
    MountPath: "/etc/kubernetes",
+   ReadOnly:  true,
  },

(read-only mounts are the recommended pattern for configuration artefacts). (v1-33.docs.kubernetes.io)

Optional: set AZURE_AUTHORITY_HOST when targeting sovereign clouds to ensure the Azure SDK uses the correct authority. (azure.github.io)

📝 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.

Suggested change
// For self-managed Azure with workload identity, instruct Azure SDK to use the minted SA token
if azureutil.IsSelfManagedAzure(hcluster.Spec.Platform.Type) &&
hcluster.Spec.Platform.Azure.AzureAuthenticationConfig.WorkloadIdentities != nil {
deploymentSpec.Template.Spec.Containers[0].Env = append(
deploymentSpec.Template.Spec.Containers[0].Env,
corev1.EnvVar{
Name: "AZURE_FEDERATED_TOKEN_FILE",
Value: "/var/run/secrets/openshift/serviceaccount/token",
},
corev1.EnvVar{
Name: "AZURE_CLIENT_ID",
Value: string(hcluster.Spec.Platform.Azure.AzureAuthenticationConfig.WorkloadIdentities.NodePoolManagement.ClientID),
},
corev1.EnvVar{
Name: "AZURE_TENANT_ID",
Value: hcluster.Spec.Platform.Azure.TenantID,
},
)
// Inject cloud token-minter sidecar and mount token volume for self-managed Azure workload identity
tokenVolume := corev1.Volume{
Name: "cloud-token",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory},
},
}
deploymentSpec.Template.Spec.Volumes = append(deploymentSpec.Template.Spec.Volumes, tokenVolume)
deploymentSpec.Template.Spec.Containers[0].VolumeMounts = append(deploymentSpec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
Name: tokenVolume.Name,
MountPath: "/var/run/secrets/openshift/serviceaccount",
})
deploymentSpec.Template.Spec.Containers = append(deploymentSpec.Template.Spec.Containers, corev1.Container{
Name: "cloud-token-minter",
Image: a.utilitiesImage,
Command: []string{"/usr/bin/control-plane-operator", "token-minter"},
Args: []string{
"--token-audience=openshift",
"--service-account-namespace=kube-system",
"--service-account-name=capi-provider",
"--token-file=/var/run/secrets/openshift/serviceaccount/token",
"--kubeconfig=/etc/kubernetes/kubeconfig",
},
ImagePullPolicy: corev1.PullIfNotPresent,
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("10m"),
corev1.ResourceMemory: resource.MustParse("30Mi"),
},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: tokenVolume.Name,
MountPath: "/var/run/secrets/openshift/serviceaccount",
},
{
Name: "svc-kubeconfig",
MountPath: "/etc/kubernetes",
},
},
})
}
// For self-managed Azure with workload identity, instruct Azure SDK to use the minted SA token
if azureutil.IsSelfManagedAzure(hcluster.Spec.Platform.Type) &&
hcluster.Spec.Platform.Azure.AzureAuthenticationConfig.WorkloadIdentities != nil {
deploymentSpec.Template.Spec.Containers[0].Env = append(
deploymentSpec.Template.Spec.Containers[0].Env,
corev1.EnvVar{
Name: "AZURE_FEDERATED_TOKEN_FILE",
Value: "/var/run/secrets/openshift/serviceaccount/token",
},
corev1.EnvVar{
Name: "AZURE_CLIENT_ID",
Value: string(hcluster.Spec.Platform.Azure.AzureAuthenticationConfig.WorkloadIdentities.NodePoolManagement.ClientID),
},
corev1.EnvVar{
Name: "AZURE_TENANT_ID",
Value: hcluster.Spec.Platform.Azure.TenantID,
},
)
// Inject cloud token-minter sidecar and mount token volume for self-managed Azure workload identity
tokenVolume := corev1.Volume{
Name: "cloud-token",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{Medium: corev1.StorageMediumMemory},
},
}
deploymentSpec.Template.Spec.Volumes = append(deploymentSpec.Template.Spec.Volumes, tokenVolume)
deploymentSpec.Template.Spec.Containers[0].VolumeMounts = append(deploymentSpec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{
Name: tokenVolume.Name,
MountPath: "/var/run/secrets/openshift/serviceaccount",
})
deploymentSpec.Template.Spec.Containers = append(deploymentSpec.Template.Spec.Containers, corev1.Container{
Name: "cloud-token-minter",
Image: a.utilitiesImage,
Command: []string{"/usr/bin/control-plane-operator", "token-minter"},
Args: []string{
"--token-audience=api://AzureADTokenExchange",
"--service-account-namespace=kube-system",
"--service-account-name=capi-provider",
"--token-file=/var/run/secrets/openshift/serviceaccount/token",
"--kubeconfig=/etc/kubernetes/kubeconfig",
},
ImagePullPolicy: corev1.PullIfNotPresent,
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("10m"),
corev1.ResourceMemory: resource.MustParse("30Mi"),
},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: tokenVolume.Name,
MountPath: "/var/run/secrets/openshift/serviceaccount",
},
{
Name: "svc-kubeconfig",
MountPath: "/etc/kubernetes",
ReadOnly: true,
},
},
})
}
🤖 Prompt for AI Agents
In
hypershift-operator/controllers/hostedcluster/internal/platform/azure/azure.go
around lines 205 to 267, change the token-minter --token-audience to
"api://AzureADTokenExchange" (use the sovereign-cloud variant when targeting
USGov/China as appropriate) and mark the kubeconfig mounts ReadOnly for both
containers by setting the corresponding VolumeMount ReadOnly field to true;
additionally consider adding an AZURE_AUTHORITY_HOST env var when deploying to a
sovereign cloud so the Azure SDK uses the correct authority.

@bryan-cox

Copy link
Copy Markdown
Member Author

/verified by @xiuwang and @bryan-cox

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Sep 23, 2025
@openshift-ci-robot

Copy link
Copy Markdown

@bryan-cox: This PR has been marked as verified by @xiuwang and @bryan-cox.

Details

In response to this:

/verified by @xiuwang and @bryan-cox

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.

@sjenning

Copy link
Copy Markdown
Contributor

lgtm
/hold cancel

@openshift-ci openshift-ci Bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Sep 23, 2025
@sjenning

Copy link
Copy Markdown
Contributor

/override ci/prow/e2e-kubevirt-aws-ovn-reduced

openshift/release#68540 regressed the kubevirt presub, but the test stage passed

@openshift-ci

openshift-ci Bot commented Sep 23, 2025

Copy link
Copy Markdown
Contributor

@sjenning: Overrode contexts on behalf of sjenning: ci/prow/e2e-kubevirt-aws-ovn-reduced

Details

In response to this:

/override ci/prow/e2e-kubevirt-aws-ovn-reduced

openshift/release#68540 regressed the kubevirt presub, but the test stage passed

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.

@bryan-cox

Copy link
Copy Markdown
Member Author

/test verify

@bryan-cox

Copy link
Copy Markdown
Member Author

/test e2e-aws

@sjenning

Copy link
Copy Markdown
Contributor

/override ci/prow/e2e-aws

aws-ebs-csi-driver-controller pod restart flake

@openshift-ci

openshift-ci Bot commented Sep 23, 2025

Copy link
Copy Markdown
Contributor

@sjenning: Overrode contexts on behalf of sjenning: ci/prow/e2e-aws

Details

In response to this:

/override ci/prow/e2e-aws

aws-ebs-csi-driver-controller pod restart flake

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.

@openshift-merge-bot
openshift-merge-bot Bot merged commit 50d0c75 into openshift:main Sep 23, 2025
24 checks passed
@openshift-ci

openshift-ci Bot commented Sep 23, 2025

Copy link
Copy Markdown
Contributor

@bryan-cox: all tests passed!

Full PR test history. Your PR dashboard.

Details

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. I understand the commands that are listed here.

@bryan-cox
bryan-cox deleted the self-managed-azure branch September 24, 2025 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. area/api Indicates the PR includes changes for the API area/cli Indicates the PR includes changes for CLI area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/documentation Indicates the PR includes changes for documentation area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants