Skip to content

CNTRLPLANE-2939: Coordinate CRD lifecycle with Cluster CAPI Operator#7936

Closed
csrwng wants to merge 2 commits into
openshift:mainfrom
csrwng:capi-compatibility
Closed

CNTRLPLANE-2939: Coordinate CRD lifecycle with Cluster CAPI Operator#7936
csrwng wants to merge 2 commits into
openshift:mainfrom
csrwng:capi-compatibility

Conversation

@csrwng

@csrwng csrwng commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

When a management cluster has the Cluster CAPI Operator (CCAPIO) installed, HyperShift's hypershift install command installs CAPI CRDs that may conflict with CRDs already managed by CCAPIO. This PR adds coordination logic to the install flow:

  1. Dry-run validation: All CRDs are validated via server-side dry-run before applying, catching webhook rejections and schema conflicts early — regardless of CCAPIO presence
  2. CCAPIO detection: Uses the discovery API to check if the ClusterAPI kind is served under operator.openshift.io/v1alpha1
  3. CRD ownership signaling: Gets-or-creates the ClusterAPI config singleton and populates spec.unmanagedCustomResourceDefinitions with HyperShift's CAPI CRD names (groups ending in .cluster.x-k8s.io), telling CCAPIO to skip these CRDs

The coordination only mutates the ClusterAPI config after dry-run passes, so there are no side effects if CRDs cannot be applied.

Install flow (after this PR):

generate manifests
  → dry-run validate all CRDs
  → if ClusterAPI API available: ensure unmanaged CRDs in config
  → apply CRDs
  → wait for CRDs to be established (if requested)
  → apply remaining objects

Vendor bump

Bumps github.com/openshift/api from v0.0.0-20260120150926-4c643a652d54 to v0.0.0-20260227165130-5a7add616a90 to vendor the ClusterAPI config type (operator.openshift.io/v1alpha1). The selected commit remains compatible with k8s.io v0.34.x.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/CNTRLPLANE-2939

Special notes for your reviewer:

  • The unmanagedCustomResourceDefinitions field is append-only (CEL enforced) — once a CRD name is added, it cannot be removed
  • In the pre-upgrade scenario (4.N-1), CCAPIO webhooks are not running so dry-run passes trivially — this is expected and correct
  • Helm chart rendering (hyperShiftOperatorManifests with nil client) is unaffected — CCAPIO coordination only runs in InstallHyperShiftOperator

Checklist:

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

🤖 Generated with Claude Code via /jira:solve [CNTRLPLANE-2939](https://issues.redhat.com/browse/CNTRLPLANE-2939)

Summary by CodeRabbit

  • New Features

    • Pre-flight dry-run validation for Custom Resource Definitions during installation
    • Automatic detection and coordination with the Cluster API Operator to prevent CRD conflicts during deployment
  • Tests

    • Expanded test coverage for installation flow, CRD validation, Cluster API detection, and CRD coordination logic
  • Chores

    • Updated OpenShift API dependency version

Vendor a newer version of openshift/api that includes the ClusterAPI
config type (operator.openshift.io/v1alpha1). This type is needed to
coordinate CRD lifecycle with the Cluster CAPI Operator (CCAPIO) by
populating the unmanagedCustomResourceDefinitions field.

The selected commit (5a7add616a90, Feb 27 2026) includes
types_clusterapi.go while remaining compatible with the existing
k8s.io v0.34.x dependency chain.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@openshift-ci-robot

Copy link
Copy Markdown

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot

openshift-ci-robot commented Mar 11, 2026

Copy link
Copy Markdown

@csrwng: This pull request references CNTRLPLANE-2939 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:

When a management cluster has the Cluster CAPI Operator (CCAPIO) installed, HyperShift's hypershift install command installs CAPI CRDs that may conflict with CRDs already managed by CCAPIO. This PR adds coordination logic to the install flow:

  1. Dry-run validation: All CRDs are validated via server-side dry-run before applying, catching webhook rejections and schema conflicts early — regardless of CCAPIO presence
  2. CCAPIO detection: Uses the discovery API to check if the ClusterAPI kind is served under operator.openshift.io/v1alpha1
  3. CRD ownership signaling: Gets-or-creates the ClusterAPI config singleton and populates spec.unmanagedCustomResourceDefinitions with HyperShift's CAPI CRD names (groups ending in .cluster.x-k8s.io), telling CCAPIO to skip these CRDs

The coordination only mutates the ClusterAPI config after dry-run passes, so there are no side effects if CRDs cannot be applied.

Install flow (after this PR):

generate manifests
 → dry-run validate all CRDs
 → if ClusterAPI API available: ensure unmanaged CRDs in config
 → apply CRDs
 → wait for CRDs to be established (if requested)
 → apply remaining objects

Vendor bump

Bumps github.com/openshift/api from v0.0.0-20260120150926-4c643a652d54 to v0.0.0-20260227165130-5a7add616a90 to vendor the ClusterAPI config type (operator.openshift.io/v1alpha1). The selected commit remains compatible with k8s.io v0.34.x.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/CNTRLPLANE-2939

Special notes for your reviewer:

  • The unmanagedCustomResourceDefinitions field is append-only (CEL enforced) — once a CRD name is added, it cannot be removed
  • In the pre-upgrade scenario (4.N-1), CCAPIO webhooks are not running so dry-run passes trivially — this is expected and correct
  • Helm chart rendering (hyperShiftOperatorManifests with nil client) is unaffected — CCAPIO coordination only runs in InstallHyperShiftOperator

Checklist:

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

🤖 Generated with Claude Code via /jira:solve [CNTRLPLANE-2939](https://issues.redhat.com/browse/CNTRLPLANE-2939)

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 added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Mar 11, 2026
@openshift-ci openshift-ci Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. do-not-merge/needs-area labels Mar 11, 2026
@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: f0c95244-366c-4b37-8641-3078856719ea

📥 Commits

Reviewing files that changed from the base of the PR and between f5d6247 and 74f5301.

📒 Files selected for processing (2)
  • cmd/install/install.go
  • cmd/install/install_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/install/install_test.go

📝 Walkthrough

Walkthrough

The PR bumps the OpenShift API dependency and adds server-side dry-run CRD validation, Cluster API discovery, and coordination with the Cluster API Operator into the install flow. It introduces a discovery abstraction and functions to detect ClusterAPI, validate CRDs via server-side dry-run, and persist a ClusterAPI config entry that marks Hypershift-managed CRDs as unmanaged when ClusterAPI is present. Tests for discovery detection and unmanaged-CRD reconciliation were added.

Sequence Diagram

sequenceDiagram
    participant Installer as InstallCommand
    participant Discovery as DiscoveryClient
    participant API as Kubernetes API Server
    participant CAPIConfig as Cluster API Operator Config
    participant Client as Kubernetes Client

    Installer->>API: dryRunValidateCRDs (server-side dry-run)
    API-->>Installer: validation results

    Installer->>Discovery: isClusterAPIRegistered()
    Discovery->>API: Query API resources
    API-->>Discovery: APIResourceList
    Discovery-->>Installer: registered? (true/false)

    alt ClusterAPI registered
        Installer->>Client: ensureUnmanagedCRDs(ctx, capiCRDs)
        Client->>API: Get existing CAPI Config
        API-->>Client: existing Config or NotFound
        Client->>CAPIConfig: Create/Update with unmanaged CRDs
        CAPIConfig->>API: Apply Config
        API-->>Client: Config persisted
        Client-->>Installer: ensureUnmanagedCRDs complete
    end

    Installer->>Client: Apply CRD manifests
    Client->>API: Create/Update CRDs
    API-->>Client: CRDs applied
    Client-->>Installer: Install complete
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ❓ Inconclusive Test file uses traditional Go testing.T style rather than Ginkgo BDD framework; unable to verify Ginkgo-specific quality criteria without confirming test implementation format. Clarify if Ginkgo tests are required for this package; if so, verify tests follow Describe/It patterns with proper BeforeEach/AfterEach setup and timeout-protected Eventually calls.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: coordination of CRD lifecycle with the Cluster CAPI Operator, which matches the core functionality added across the modified files.
Stable And Deterministic Test Names ✅ Passed The PR test files use standard Go testing conventions (func Test*) rather than Ginkgo framework, so dynamic test name concerns do not apply.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@openshift-ci

openshift-ci Bot commented Mar 11, 2026

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

@openshift-ci-robot

openshift-ci-robot commented Mar 11, 2026

Copy link
Copy Markdown

@csrwng: This pull request references CNTRLPLANE-2939 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:

When a management cluster has the Cluster CAPI Operator (CCAPIO) installed, HyperShift's hypershift install command installs CAPI CRDs that may conflict with CRDs already managed by CCAPIO. This PR adds coordination logic to the install flow:

  1. Dry-run validation: All CRDs are validated via server-side dry-run before applying, catching webhook rejections and schema conflicts early — regardless of CCAPIO presence
  2. CCAPIO detection: Uses the discovery API to check if the ClusterAPI kind is served under operator.openshift.io/v1alpha1
  3. CRD ownership signaling: Gets-or-creates the ClusterAPI config singleton and populates spec.unmanagedCustomResourceDefinitions with HyperShift's CAPI CRD names (groups ending in .cluster.x-k8s.io), telling CCAPIO to skip these CRDs

The coordination only mutates the ClusterAPI config after dry-run passes, so there are no side effects if CRDs cannot be applied.

Install flow (after this PR):

generate manifests
 → dry-run validate all CRDs
 → if ClusterAPI API available: ensure unmanaged CRDs in config
 → apply CRDs
 → wait for CRDs to be established (if requested)
 → apply remaining objects

Vendor bump

Bumps github.com/openshift/api from v0.0.0-20260120150926-4c643a652d54 to v0.0.0-20260227165130-5a7add616a90 to vendor the ClusterAPI config type (operator.openshift.io/v1alpha1). The selected commit remains compatible with k8s.io v0.34.x.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/CNTRLPLANE-2939

Special notes for your reviewer:

  • The unmanagedCustomResourceDefinitions field is append-only (CEL enforced) — once a CRD name is added, it cannot be removed
  • In the pre-upgrade scenario (4.N-1), CCAPIO webhooks are not running so dry-run passes trivially — this is expected and correct
  • Helm chart rendering (hyperShiftOperatorManifests with nil client) is unaffected — CCAPIO coordination only runs in InstallHyperShiftOperator

Checklist:

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

🤖 Generated with Claude Code via /jira:solve [CNTRLPLANE-2939](https://issues.redhat.com/browse/CNTRLPLANE-2939)

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/api Indicates the PR includes changes for the API label Mar 11, 2026
@openshift-ci

openshift-ci Bot commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: csrwng

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

@openshift-ci openshift-ci Bot added area/cli Indicates the PR includes changes for CLI approved Indicates a PR has been approved by an approver from all required OWNERS files. and removed do-not-merge/needs-area labels Mar 11, 2026
@openshift-ci-robot

openshift-ci-robot commented Mar 11, 2026

Copy link
Copy Markdown

@csrwng: This pull request references CNTRLPLANE-2939 which is a valid jira issue.

Details

In response to this:

What this PR does / why we need it:

When a management cluster has the Cluster CAPI Operator (CCAPIO) installed, HyperShift's hypershift install command installs CAPI CRDs that may conflict with CRDs already managed by CCAPIO. This PR adds coordination logic to the install flow:

  1. Dry-run validation: All CRDs are validated via server-side dry-run before applying, catching webhook rejections and schema conflicts early — regardless of CCAPIO presence
  2. CCAPIO detection: Uses the discovery API to check if the ClusterAPI kind is served under operator.openshift.io/v1alpha1
  3. CRD ownership signaling: Gets-or-creates the ClusterAPI config singleton and populates spec.unmanagedCustomResourceDefinitions with HyperShift's CAPI CRD names (groups ending in .cluster.x-k8s.io), telling CCAPIO to skip these CRDs

The coordination only mutates the ClusterAPI config after dry-run passes, so there are no side effects if CRDs cannot be applied.

Install flow (after this PR):

generate manifests
 → dry-run validate all CRDs
 → if ClusterAPI API available: ensure unmanaged CRDs in config
 → apply CRDs
 → wait for CRDs to be established (if requested)
 → apply remaining objects

Vendor bump

Bumps github.com/openshift/api from v0.0.0-20260120150926-4c643a652d54 to v0.0.0-20260227165130-5a7add616a90 to vendor the ClusterAPI config type (operator.openshift.io/v1alpha1). The selected commit remains compatible with k8s.io v0.34.x.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/CNTRLPLANE-2939

Special notes for your reviewer:

  • The unmanagedCustomResourceDefinitions field is append-only (CEL enforced) — once a CRD name is added, it cannot be removed
  • In the pre-upgrade scenario (4.N-1), CCAPIO webhooks are not running so dry-run passes trivially — this is expected and correct
  • Helm chart rendering (hyperShiftOperatorManifests with nil client) is unaffected — CCAPIO coordination only runs in InstallHyperShiftOperator

Checklist:

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

🤖 Generated with Claude Code via /jira:solve [CNTRLPLANE-2939](https://issues.redhat.com/browse/CNTRLPLANE-2939)

Summary by CodeRabbit

  • New Features
  • Added pre-flight dry-run validation for Custom Resource Definitions during installation
  • Added automatic detection and coordination with Cluster API Operator to prevent CRD conflicts during deployment

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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/install/install_test.go`:
- Around line 596-604: The test’s expectNoChange branch returns early and misses
asserting that an existing ClusterAPI singleton remains unchanged; update the
branch in the test (the block that checks tc.expectNoChange and uses client.Get
and operatorv1alpha1.ClusterAPI) to handle both cases: if tc.existingConfig ==
nil assert that no ClusterAPI exists (as now), else fetch the singleton and
assert it equals tc.existingConfig (or at least that key fields remain
unchanged) so the “already listed” scenario is validated instead of being
skipped.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: e386402e-9635-4bb6-add9-6e15711b5c8b

📥 Commits

Reviewing files that changed from the base of the PR and between f3112d0 and 88b46ac.

⛔ Files ignored due to path filters (101)
  • api/go.sum is excluded by !**/*.sum
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/zz_generated*
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AutoNodeKarpenter.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterUpdateAcceptRisks.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUpstreamParity.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/HyperShiftOnlyDynamicResourceAllocation.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AutoNodeKarpenter.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterUpdateAcceptRisks.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUpstreamParity.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/HyperShiftOnlyDynamicResourceAllocation.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/NetworkDiagnosticsConfig.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/vendor/github.com/openshift/api/config/v1/types_apiserver.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_authentication.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_infrastructure.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_ingress.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_insights.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_network.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_tlssecurityprofile.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !**/zz_generated*
  • api/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • api/vendor/github.com/openshift/api/operator/v1/types_network.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/operator/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !**/zz_generated*
  • api/vendor/modules.txt is excluded by !**/vendor/**
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • go.sum is excluded by !**/*.sum
  • vendor/github.com/openshift/api/.coderabbit.yaml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/.golangci.yaml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/Makefile is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/types_compatibilityrequirement.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/apps/v1/types.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/apps/v1/zz_prerelease_lifecycle_generated.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_apiserver.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_authentication.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_infrastructure.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_ingress.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_insights.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_network.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_tlssecurityprofile.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1alpha1/register.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1alpha1/types_backup.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1alpha1/types_cluster_monitoring.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1alpha1/types_crio_credential_provider_config.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1alpha1/types_insights.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1alpha1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1alpha1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1alpha2/types_insights.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1alpha2/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/console/v1/types_console_sample.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/etcd/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/etcd/install.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/etcd/v1alpha1/Makefile is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/etcd/v1alpha1/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/etcd/v1alpha1/register.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/etcd/v1alpha1/types_pacemakercluster.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/etcd/v1alpha1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/etcd/v1alpha1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/etcd/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/features.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/install.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/machine/v1/types_controlplanemachineset.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/machine/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1beta1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/machineconfiguration/v1/types.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/operator/v1/types_network.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/operator/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1alpha1/register.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/operator/v1alpha1/types_clusterapi.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/modules.txt is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (4)
  • api/go.mod
  • cmd/install/install.go
  • cmd/install/install_test.go
  • go.mod

Comment thread cmd/install/install_test.go
Comment thread cmd/install/install.go
@csrwng csrwng force-pushed the capi-compatibility branch from 88b46ac to f5d6247 Compare March 11, 2026 22:41
@csrwng

csrwng commented Mar 11, 2026

Copy link
Copy Markdown
Contributor Author

/test unit
/test verify
/test e2e-aws-minimal

@openshift-ci-robot

openshift-ci-robot commented Mar 11, 2026

Copy link
Copy Markdown

@csrwng: This pull request references CNTRLPLANE-2939 which is a valid jira issue.

Details

In response to this:

What this PR does / why we need it:

When a management cluster has the Cluster CAPI Operator (CCAPIO) installed, HyperShift's hypershift install command installs CAPI CRDs that may conflict with CRDs already managed by CCAPIO. This PR adds coordination logic to the install flow:

  1. Dry-run validation: All CRDs are validated via server-side dry-run before applying, catching webhook rejections and schema conflicts early — regardless of CCAPIO presence
  2. CCAPIO detection: Uses the discovery API to check if the ClusterAPI kind is served under operator.openshift.io/v1alpha1
  3. CRD ownership signaling: Gets-or-creates the ClusterAPI config singleton and populates spec.unmanagedCustomResourceDefinitions with HyperShift's CAPI CRD names (groups ending in .cluster.x-k8s.io), telling CCAPIO to skip these CRDs

The coordination only mutates the ClusterAPI config after dry-run passes, so there are no side effects if CRDs cannot be applied.

Install flow (after this PR):

generate manifests
 → dry-run validate all CRDs
 → if ClusterAPI API available: ensure unmanaged CRDs in config
 → apply CRDs
 → wait for CRDs to be established (if requested)
 → apply remaining objects

Vendor bump

Bumps github.com/openshift/api from v0.0.0-20260120150926-4c643a652d54 to v0.0.0-20260227165130-5a7add616a90 to vendor the ClusterAPI config type (operator.openshift.io/v1alpha1). The selected commit remains compatible with k8s.io v0.34.x.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/CNTRLPLANE-2939

Special notes for your reviewer:

  • The unmanagedCustomResourceDefinitions field is append-only (CEL enforced) — once a CRD name is added, it cannot be removed
  • In the pre-upgrade scenario (4.N-1), CCAPIO webhooks are not running so dry-run passes trivially — this is expected and correct
  • Helm chart rendering (hyperShiftOperatorManifests with nil client) is unaffected — CCAPIO coordination only runs in InstallHyperShiftOperator

Checklist:

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

🤖 Generated with Claude Code via /jira:solve [CNTRLPLANE-2939](https://issues.redhat.com/browse/CNTRLPLANE-2939)

Summary by CodeRabbit

  • New Features

  • Added pre-flight dry-run validation for Custom Resource Definitions during installation

  • Added automatic detection and coordination with Cluster API Operator to prevent CRD conflicts during deployment

  • Tests

  • Expanded test coverage for installation flow, CRD validation, Cluster API detection, and CRD coordination logic

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.

When the ClusterAPI CRD (clusterapis.operator.openshift.io) is
registered on the management cluster, the install command now:

1. Detects CCAPIO presence by checking for the ClusterAPI CRD
2. Gets-or-creates the ClusterAPI config singleton and populates
   unmanagedCustomResourceDefinitions with HyperShift's CAPI CRD
   names (those with groups ending in .cluster.x-k8s.io)
3. Validates all CRDs via server-side dry-run before applying,
   regardless of CCAPIO presence, to catch webhook rejections
   and schema conflicts early

This prevents conflicts between HyperShift and CCAPIO when both
attempt to manage the same CAPI CRDs on the management cluster.

Ref: CNTRLPLANE-2939

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@csrwng csrwng force-pushed the capi-compatibility branch from f5d6247 to 74f5301 Compare March 12, 2026 03:31
@csrwng

csrwng commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

/test e2e-aws-minimal

@openshift-ci-robot

openshift-ci-robot commented Mar 12, 2026

Copy link
Copy Markdown

@csrwng: This pull request references CNTRLPLANE-2939 which is a valid jira issue.

Details

In response to this:

What this PR does / why we need it:

When a management cluster has the Cluster CAPI Operator (CCAPIO) installed, HyperShift's hypershift install command installs CAPI CRDs that may conflict with CRDs already managed by CCAPIO. This PR adds coordination logic to the install flow:

  1. Dry-run validation: All CRDs are validated via server-side dry-run before applying, catching webhook rejections and schema conflicts early — regardless of CCAPIO presence
  2. CCAPIO detection: Uses the discovery API to check if the ClusterAPI kind is served under operator.openshift.io/v1alpha1
  3. CRD ownership signaling: Gets-or-creates the ClusterAPI config singleton and populates spec.unmanagedCustomResourceDefinitions with HyperShift's CAPI CRD names (groups ending in .cluster.x-k8s.io), telling CCAPIO to skip these CRDs

The coordination only mutates the ClusterAPI config after dry-run passes, so there are no side effects if CRDs cannot be applied.

Install flow (after this PR):

generate manifests
 → dry-run validate all CRDs
 → if ClusterAPI API available: ensure unmanaged CRDs in config
 → apply CRDs
 → wait for CRDs to be established (if requested)
 → apply remaining objects

Vendor bump

Bumps github.com/openshift/api from v0.0.0-20260120150926-4c643a652d54 to v0.0.0-20260227165130-5a7add616a90 to vendor the ClusterAPI config type (operator.openshift.io/v1alpha1). The selected commit remains compatible with k8s.io v0.34.x.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/CNTRLPLANE-2939

Special notes for your reviewer:

  • The unmanagedCustomResourceDefinitions field is append-only (CEL enforced) — once a CRD name is added, it cannot be removed
  • In the pre-upgrade scenario (4.N-1), CCAPIO webhooks are not running so dry-run passes trivially — this is expected and correct
  • Helm chart rendering (hyperShiftOperatorManifests with nil client) is unaffected — CCAPIO coordination only runs in InstallHyperShiftOperator

Checklist:

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

🤖 Generated with Claude Code via /jira:solve [CNTRLPLANE-2939](https://issues.redhat.com/browse/CNTRLPLANE-2939)

Summary by CodeRabbit

  • New Features

  • Pre-flight dry-run validation for Custom Resource Definitions during installation

  • Automatic detection and coordination with the Cluster API Operator to prevent CRD conflicts during deployment

  • Tests

  • Expanded test coverage for installation flow, CRD validation, Cluster API detection, and CRD coordination logic

  • Chores

  • Updated OpenShift API dependency version

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.

@csrwng csrwng marked this pull request as ready for review March 12, 2026 03:44
@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 Mar 12, 2026
@openshift-ci openshift-ci Bot requested review from devguyio and sjenning March 12, 2026 03:45
@openshift-ci

openshift-ci Bot commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

@csrwng: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-azure-self-managed 74f5301 link true /test e2e-azure-self-managed
ci/prow/e2e-aws-minimal 74f5301 link false /test e2e-aws-minimal

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.

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 17, 2026
@openshift-merge-robot

Copy link
Copy Markdown
Contributor

PR needs rebase.

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.

@muraee

muraee commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

/close
in favor of #7996

@openshift-ci openshift-ci Bot closed this Mar 18, 2026
@openshift-ci

openshift-ci Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

@muraee: Closed this PR.

Details

In response to this:

/close
in favor of #7996

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.

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 jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants