Skip to content

RFE-9274: add disable-external-dns-management annotation#8708

Open
chdeshpa-hue wants to merge 1 commit into
openshift:mainfrom
chdeshpa-hue:rfe-9274-disable-external-dns-management
Open

RFE-9274: add disable-external-dns-management annotation#8708
chdeshpa-hue wants to merge 1 commit into
openshift:mainfrom
chdeshpa-hue:rfe-9274-disable-external-dns-management

Conversation

@chdeshpa-hue

@chdeshpa-hue chdeshpa-hue commented Jun 10, 2026

Copy link
Copy Markdown

RFE-9274: Allow users to opt out of external-dns annotation injection on LoadBalancer services

Repository: openshift/hypershift
Fixes: RFE-9274
Files modified: 9


Objective

Add a hypershift.openshift.io/disable-external-dns-management: "true" annotation on HostedCluster that prevents the Control Plane Operator from injecting the external-dns.alpha.kubernetes.io/hostname annotation on LoadBalancer services. This enables users who manage their own DNS (BYODNS) to use HyperShift's LoadBalancer publishing strategy without a false-negative ExternalDNSReachable condition or coupling to an external-dns controller they don't run.


Problem Statement

When spec.services[].servicePublishingStrategy.loadBalancer.hostname is configured, the CPO unconditionally injects external-dns.alpha.kubernetes.io/hostname on the KAS, Konnectivity, and (on Azure) OAuth LoadBalancer services.

In environments without an external-dns controller — Agent-based HCP on bare metal, KubeVirt with MetalLB, air-gapped clusters — this produces a permanent false-negative:

CONDITION            STATUS   REASON                      MESSAGE
ExternalDNSReachable False    ExternalDNSHostNotReachable  lookup api.mycluster.example.com on 172.30.0.10:53: no such host

No persistent workaround exists. Manually removing the annotation from the service is overwritten by CPO reconciliation within ~30 seconds.


Scope

This PR does:

  • Suppress external-dns.alpha.kubernetes.io/hostname injection when the annotation is set
  • Actively remove the annotation on pre-existing services (day-2 opt-out)
  • Report ExternalDNSReachable: True (ExternalDNSManagementDisabled) when opted out
  • Propagate the annotation from HostedCluster to HostedControlPlane via mirroredAnnotations

This PR does NOT:

  • Change the CRD schema or add new API fields
  • Separate api from api-int DNS resolution
  • Modify LoadBalancer provisioning behavior
  • Affect Route-based or NodePort-based service publishing
  • Change PKI/SAN generation, ControlPlaneEndpoint, kubeconfig, or node HAProxy behavior

Platform applicability:

Service Trigger condition Platforms affected
KAS LoadBalancer strategy + public endpoint AWS, GCP, Azure
Konnectivity LoadBalancer strategy Any
OAuth LoadBalancer strategy Azure only

Design Decision

Approach Pros Cons Verdict
API field manageDNS *bool on strategy Type-safe, discoverable CRD change, API review, deepcopy regen, vendor sync, no precedent for bool toggles on strategy structs Rejected
Runtime external-dns operator detection Zero config Race condition on install/remove, violates separation of concerns Rejected
Separate dnsHostname vs serviceHostname Clean separation Breaking API change, confusing dual-hostname semantics Rejected
Annotation disable-external-dns-management Zero CRD change, backward compatible, follows 7+ existing disable-* patterns, promotable to field later Not discoverable via oc explain Chosen

Closest code pattern: DisablePKIReconciliationAnnotation — identical guard structure:

if hcp.Annotations[hyperv1.DisablePKIReconciliationAnnotation] == "true" { ... }

Implementation

File Change
api/hypershift/v1beta1/hostedcluster_types.go Add DisableExternalDNSManagementAnnotation constant
hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go Add to mirroredAnnotations (HC -> HCP propagation)
control-plane-operator/controllers/hostedcontrolplane/kas/service.go Guard KAS + Konnectivity annotation injection (2 locations)
control-plane-operator/controllers/hostedcontrolplane/oauth/service.go Add disableExternalDNS parameter, guard injection
control-plane-operator/controllers/hostedcontrolplane/infra/infra.go Pass disable flag to OAuth reconciler
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go Early return in reconcileExternalDNSStatusCondition
kas/service_test.go 6 new test cases
oauth/service_test.go 2 new test cases
vendor/.../hostedcluster_types.go Sync vendored constant

Core logic (KAS service):

if strategy.LoadBalancer != nil && strategy.LoadBalancer.Hostname != "" {
    if hcp.Annotations[hyperv1.DisableExternalDNSManagementAnnotation] != "true" {
        svc.Annotations[hyperv1.ExternalDNSHostnameAnnotation] = strategy.LoadBalancer.Hostname
    } else {
        delete(svc.Annotations, hyperv1.ExternalDNSHostnameAnnotation)
    }
}

Condition controller:

if hostedControlPlane.Annotations[hyperv1.DisableExternalDNSManagementAnnotation] == "true" {
    newCondition = metav1.Condition{
        Type:   string(hyperv1.ExternalDNSReachable),
        Status: metav1.ConditionTrue,
        Reason: "ExternalDNSManagementDisabled",
        Message: "External DNS management is disabled by annotation",
    }
    meta.SetStatusCondition(&hostedControlPlane.Status.Conditions, newCondition)
    return
}

Testing Results

Unit Tests

Test function Cases Result
TestReconcileServiceDisableExternalDNS (KAS) annotation absent -> set, annotation=true -> suppressed, annotation=false -> set, day-2 opt-out -> deleted PASS
TestReconcileKonnectivityServiceDisableExternalDNS annotation absent -> set, annotation=true -> suppressed PASS
TestOauthServiceDisableExternalDNS disabled=false -> set, disabled=true -> suppressed PASS
All pre-existing KAS tests 8 cases PASS (no regression)
All pre-existing OAuth tests 10 cases PASS (no regression)

Integration Tests (Live Cluster)

Environment: OCP 4.22.0, AWS eu-north-1, MCE v2.16.1, MetalLB L2, custom CPO image with fix.

Test 1: AWS/None — Day-2 Opt-Out

Existing cluster with annotation already injected by stock CPO. Applied fix to verify active removal.

$ oc get svc kube-apiserver -n clusters-rfe9274-test -o jsonpath='{.metadata.annotations}'
{"metallb.io/ip-allocated-from-pool":"hub-vpc-pool"}

$ oc get hostedcluster rfe9274-test -n clusters \
    -o jsonpath='{.status.conditions[?(@.type=="ExternalDNSReachable")]}'
{"reason":"ExternalDNSManagementDisabled","status":"True","type":"ExternalDNSReachable"}

Before: external-dns.alpha.kubernetes.io/hostname present, condition False.
After: Annotation deleted by CPO on reconcile, condition True.

Test 2: KubeVirt — Greenfield

Fresh cluster with annotation set from creation. Proves annotation is never injected.

$ oc get svc kube-apiserver -n clusters-rfe9274-kubevirt -o jsonpath='{.metadata.annotations}'
{"metallb.io/ip-allocated-from-pool":"hub-vpc-pool"}

$ oc get nodepool rfe9274-kubevirt-workers -n clusters
NAME                       CLUSTER            DESIRED   CURRENT   VERSION
rfe9274-kubevirt-workers   rfe9274-kubevirt   2         2         4.21.0

Workers joined via user-managed DNS (private Route53 zone -> MetalLB IP). Full cluster lifecycle validated.

Test 3: Agent — Greenfield

Agent-based workers (EC2 instances booting RHCOS discovery ISO) with user-managed DNS via internal NLB.

$ oc get svc kube-apiserver -n clusters-rfe9274-agent -o jsonpath='{.metadata.annotations}'
{"metallb.io/ip-allocated-from-pool":"hub-vpc-pool"}

$ KUBECONFIG=/tmp/rfe9274-agent-guest.kubeconfig oc get nodes
NAME             STATUS   ROLES    AGE     VERSION
ip-10-0-75-43   Ready    worker   4m30s   v1.34.2
ip-10-0-78-88   Ready    worker   4m29s   v1.34.2

Workers joined, CSRs approved, guest cluster fully operational.


Limitations and Known Gaps

Gap Severity Mitigation
OAuth path (Azure-only) not live-tested Low Unit-tested; logic identical to KAS guard; no Azure environment available
HO in MCE v2.16.x does not propagate the annotation Medium Users must manually annotate HCP until v2.17+ picks up main branch
No upstream e2e test Medium Needs integration with HyperShift e2e framework (follow-up PR)
MetalLB L2 VIPs unreachable from off-cluster nodes on AWS N/A Infra constraint of test env, not a product issue; solved with internal NLB in tests

Version Matrix

MCE Version HO Propagation CPO Guard User Action
v2.16.x (current GA) Manual HCP annotation required Active (with custom CPO) Annotate both HC and HCP
v2.17+ (this PR) Automatic (via mirroredAnnotations) Active Annotate HC only

Backward Compatibility

Component Impact
Default behavior (no annotation) UNCHANGED — hostname injects as before
PKI / SAN generation UNCHANGED — always uses hostname
ControlPlaneEndpoint UNCHANGED — always set from hostname
kubeconfig server URL UNCHANGED
Node HAProxy backend UNCHANGED — re-resolves hostname on TCP failure
Route external-dns (RouteVisibility) UNCHANGED — separate mechanism

Usage

apiVersion: hypershift.openshift.io/v1beta1
kind: HostedCluster
metadata:
  name: my-cluster
  namespace: clusters
  annotations:
    hypershift.openshift.io/disable-external-dns-management: "true"
spec:
  services:
    - service: APIServer
      servicePublishingStrategy:
        type: LoadBalancer
        loadBalancer:
          hostname: api.mycluster.example.com

Pre-requisite: Users must create DNS records (api.<name>.<domain>) pointing to the LoadBalancer IP/hostname before worker nodes can join.


Related

  • RFE-9274 — Original feature request
  • mirroredAnnotations in HO controller already includes the propagation logic in main
  • Future: Part 2 — separate api-int endpoint for internal-only resolution (design discussion pending)
  • Future: Upstream e2e test PR using HyperShift test framework

Summary by CodeRabbit

Release Notes

  • New Features
    • Added support for disabling external DNS management on hosted clusters via annotation. Users can now prevent automatic external-dns hostname annotation on LoadBalancer services by setting the appropriate annotation on the hosted cluster.

When loadBalancer.hostname is configured, the CPO unconditionally
injects external-dns.alpha.kubernetes.io/hostname on LoadBalancer
services. In environments without an external-dns controller this
causes a permanent false-negative ExternalDNSReachable condition.

Add hypershift.openshift.io/disable-external-dns-management annotation
that suppresses the external-dns hostname annotation injection on KAS,
Konnectivity, and OAuth LoadBalancer services. On day-2 opt-out the
annotation is actively removed from pre-existing services. The
ExternalDNSReachable condition reports True with reason
ExternalDNSManagementDisabled when opted out.

Co-authored-by: Cursor <cursoragent@cursor.com>
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

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 Jun 10, 2026

Copy link
Copy Markdown

@chdeshpa-hue: This pull request references RFE-9274 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 feature request to target the "5.0.0" version, but no target version was set.

Details

In response to this:

RFE-9274: Allow users to opt out of external-dns annotation injection on LoadBalancer services

Repository: openshift/hypershift
Fixes: RFE-9274
Files modified: 9


Objective

Add a hypershift.openshift.io/disable-external-dns-management: "true" annotation on HostedCluster that prevents the Control Plane Operator from injecting the external-dns.alpha.kubernetes.io/hostname annotation on LoadBalancer services. This enables users who manage their own DNS (BYODNS) to use HyperShift's LoadBalancer publishing strategy without a false-negative ExternalDNSReachable condition or coupling to an external-dns controller they don't run.


Problem Statement

When spec.services[].servicePublishingStrategy.loadBalancer.hostname is configured, the CPO unconditionally injects external-dns.alpha.kubernetes.io/hostname on the KAS, Konnectivity, and (on Azure) OAuth LoadBalancer services.

In environments without an external-dns controller — Agent-based HCP on bare metal, KubeVirt with MetalLB, air-gapped clusters — this produces a permanent false-negative:

CONDITION            STATUS   REASON                      MESSAGE
ExternalDNSReachable False    ExternalDNSHostNotReachable  lookup api.mycluster.example.com on 172.30.0.10:53: no such host

No persistent workaround exists. Manually removing the annotation from the service is overwritten by CPO reconciliation within ~30 seconds.


Scope

This PR does:

  • Suppress external-dns.alpha.kubernetes.io/hostname injection when the annotation is set
  • Actively remove the annotation on pre-existing services (day-2 opt-out)
  • Report ExternalDNSReachable: True (ExternalDNSManagementDisabled) when opted out
  • Propagate the annotation from HostedCluster to HostedControlPlane via mirroredAnnotations

This PR does NOT:

  • Change the CRD schema or add new API fields
  • Separate api from api-int DNS resolution
  • Modify LoadBalancer provisioning behavior
  • Affect Route-based or NodePort-based service publishing
  • Change PKI/SAN generation, ControlPlaneEndpoint, kubeconfig, or node HAProxy behavior

Platform applicability:

Service Trigger condition Platforms affected
KAS LoadBalancer strategy + public endpoint AWS, GCP, Azure
Konnectivity LoadBalancer strategy Any
OAuth LoadBalancer strategy Azure only

Design Decision

Approach Pros Cons Verdict
API field manageDNS *bool on strategy Type-safe, discoverable CRD change, API review, deepcopy regen, vendor sync, no precedent for bool toggles on strategy structs Rejected
Runtime external-dns operator detection Zero config Race condition on install/remove, violates separation of concerns Rejected
Separate dnsHostname vs serviceHostname Clean separation Breaking API change, confusing dual-hostname semantics Rejected
Annotation disable-external-dns-management Zero CRD change, backward compatible, follows 7+ existing disable-* patterns, promotable to field later Not discoverable via oc explain Chosen

Closest code pattern: DisablePKIReconciliationAnnotation — identical guard structure:

if hcp.Annotations[hyperv1.DisablePKIReconciliationAnnotation] == "true" { ... }

Implementation

File Change
api/hypershift/v1beta1/hostedcluster_types.go Add DisableExternalDNSManagementAnnotation constant
hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go Add to mirroredAnnotations (HC -> HCP propagation)
control-plane-operator/controllers/hostedcontrolplane/kas/service.go Guard KAS + Konnectivity annotation injection (2 locations)
control-plane-operator/controllers/hostedcontrolplane/oauth/service.go Add disableExternalDNS parameter, guard injection
control-plane-operator/controllers/hostedcontrolplane/infra/infra.go Pass disable flag to OAuth reconciler
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go Early return in reconcileExternalDNSStatusCondition
kas/service_test.go 6 new test cases
oauth/service_test.go 2 new test cases
vendor/.../hostedcluster_types.go Sync vendored constant

Core logic (KAS service):

if strategy.LoadBalancer != nil && strategy.LoadBalancer.Hostname != "" {
   if hcp.Annotations[hyperv1.DisableExternalDNSManagementAnnotation] != "true" {
       svc.Annotations[hyperv1.ExternalDNSHostnameAnnotation] = strategy.LoadBalancer.Hostname
   } else {
       delete(svc.Annotations, hyperv1.ExternalDNSHostnameAnnotation)
   }
}

Condition controller:

if hostedControlPlane.Annotations[hyperv1.DisableExternalDNSManagementAnnotation] == "true" {
   newCondition = metav1.Condition{
       Type:   string(hyperv1.ExternalDNSReachable),
       Status: metav1.ConditionTrue,
       Reason: "ExternalDNSManagementDisabled",
       Message: "External DNS management is disabled by annotation",
   }
   meta.SetStatusCondition(&hostedControlPlane.Status.Conditions, newCondition)
   return
}

Testing Results

Unit Tests

Test function Cases Result
TestReconcileServiceDisableExternalDNS (KAS) annotation absent -> set, annotation=true -> suppressed, annotation=false -> set, day-2 opt-out -> deleted PASS
TestReconcileKonnectivityServiceDisableExternalDNS annotation absent -> set, annotation=true -> suppressed PASS
TestOauthServiceDisableExternalDNS disabled=false -> set, disabled=true -> suppressed PASS
All pre-existing KAS tests 8 cases PASS (no regression)
All pre-existing OAuth tests 10 cases PASS (no regression)

Integration Tests (Live Cluster)

Environment: OCP 4.22.0, AWS eu-north-1, MCE v2.16.1, MetalLB L2, custom CPO image with fix.

Test 1: AWS/None — Day-2 Opt-Out

Existing cluster with annotation already injected by stock CPO. Applied fix to verify active removal.

$ oc get svc kube-apiserver -n clusters-rfe9274-test -o jsonpath='{.metadata.annotations}'
{"metallb.io/ip-allocated-from-pool":"hub-vpc-pool"}

$ oc get hostedcluster rfe9274-test -n clusters \
   -o jsonpath='{.status.conditions[?(@.type=="ExternalDNSReachable")]}'
{"reason":"ExternalDNSManagementDisabled","status":"True","type":"ExternalDNSReachable"}

Before: external-dns.alpha.kubernetes.io/hostname present, condition False.
After: Annotation deleted by CPO on reconcile, condition True.

Test 2: KubeVirt — Greenfield

Fresh cluster with annotation set from creation. Proves annotation is never injected.

$ oc get svc kube-apiserver -n clusters-rfe9274-kubevirt -o jsonpath='{.metadata.annotations}'
{"metallb.io/ip-allocated-from-pool":"hub-vpc-pool"}

$ oc get nodepool rfe9274-kubevirt-workers -n clusters
NAME                       CLUSTER            DESIRED   CURRENT   VERSION
rfe9274-kubevirt-workers   rfe9274-kubevirt   2         2         4.21.0

Workers joined via user-managed DNS (private Route53 zone -> MetalLB IP). Full cluster lifecycle validated.

Test 3: Agent — Greenfield

Agent-based workers (EC2 instances booting RHCOS discovery ISO) with user-managed DNS via internal NLB.

$ oc get svc kube-apiserver -n clusters-rfe9274-agent -o jsonpath='{.metadata.annotations}'
{"metallb.io/ip-allocated-from-pool":"hub-vpc-pool"}

$ KUBECONFIG=/tmp/rfe9274-agent-guest.kubeconfig oc get nodes
NAME             STATUS   ROLES    AGE     VERSION
ip-10-0-75-43   Ready    worker   4m30s   v1.34.2
ip-10-0-78-88   Ready    worker   4m29s   v1.34.2

Workers joined, CSRs approved, guest cluster fully operational.


Limitations and Known Gaps

Gap Severity Mitigation
OAuth path (Azure-only) not live-tested Low Unit-tested; logic identical to KAS guard; no Azure environment available
HO in MCE v2.16.x does not propagate the annotation Medium Users must manually annotate HCP until v2.17+ picks up main branch
No upstream e2e test Medium Needs integration with HyperShift e2e framework (follow-up PR)
MetalLB L2 VIPs unreachable from off-cluster nodes on AWS N/A Infra constraint of test env, not a product issue; solved with internal NLB in tests

Version Matrix

MCE Version HO Propagation CPO Guard User Action
v2.16.x (current GA) Manual HCP annotation required Active (with custom CPO) Annotate both HC and HCP
v2.17+ (this PR) Automatic (via mirroredAnnotations) Active Annotate HC only

Backward Compatibility

Component Impact
Default behavior (no annotation) UNCHANGED — hostname injects as before
PKI / SAN generation UNCHANGED — always uses hostname
ControlPlaneEndpoint UNCHANGED — always set from hostname
kubeconfig server URL UNCHANGED
Node HAProxy backend UNCHANGED — re-resolves hostname on TCP failure
Route external-dns (RouteVisibility) UNCHANGED — separate mechanism

Usage

apiVersion: hypershift.openshift.io/v1beta1
kind: HostedCluster
metadata:
 name: my-cluster
 namespace: clusters
 annotations:
   hypershift.openshift.io/disable-external-dns-management: "true"
spec:
 services:
   - service: APIServer
     servicePublishingStrategy:
       type: LoadBalancer
       loadBalancer:
         hostname: api.mycluster.example.com

Pre-requisite: Users must create DNS records (api.<name>.<domain>) pointing to the LoadBalancer IP/hostname before worker nodes can join.


Related

  • RFE-9274 — Original feature request
  • mirroredAnnotations in HO controller already includes the propagation logic in main
  • Future: Part 2 — separate api-int endpoint for internal-only resolution (design discussion pending)
  • Future: Upstream e2e test PR using HyperShift test framework

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 Jun 10, 2026
@openshift-ci openshift-ci Bot added do-not-merge/needs-area area/api Indicates the PR includes changes for the API area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release and removed do-not-merge/needs-area labels Jun 10, 2026
@openshift-ci

openshift-ci Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: chdeshpa-hue
Once this PR has been reviewed and has the lgtm label, please assign enxebre for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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 requested review from Nirshal and jparrill June 10, 2026 04:08
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces the ability to disable external DNS management on LoadBalancer services via a new HostedCluster annotation. A constant for DisableExternalDNSManagementAnnotation is defined in the API types and propagated from HostedCluster to HostedControlPlane. Three service reconcilers (KAS, Konnectivity, and OAuth) are updated to conditionally set or delete external DNS hostname annotations based on this annotation value. The ExternalDNS status condition logic also respects the annotation by short-circuiting and marking the condition as satisfied without performing DNS reachability checks.

🚥 Pre-merge checks | ✅ 10 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Test Structure And Quality ❓ Inconclusive PR adds standard Go unit tests with testing.T and t.Run(), not Ginkgo tests with Describe/It blocks. Check specifies Ginkgo test code quality but is not applicable here. Clarify if check applies to standard Go unit tests or only Ginkgo integration tests. PR contains unit tests using Gomega without Ginkgo framework.
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a disable-external-dns-management annotation to prevent external-dns hostname injection on LoadBalancer services.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All test names in newly added tests are stable and deterministic, using static string literals with no dynamic values, timestamps, UUIDs, or generated identifiers.
Topology-Aware Scheduling Compatibility ✅ Passed PR introduces no Deployment manifests or scheduling constraints. Changes only modify Service reconcilers for DNS annotation management and add annotation constants.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PR adds only Go unit tests, not Ginkgo e2e tests. All tests use mocked objects with no IPv4 assumptions or external connectivity requirements. Check applies only to Ginkgo e2e tests.
No-Weak-Crypto ✅ Passed PR introduces no weak crypto patterns (MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB), custom crypto implementations, or non-constant-time secret comparisons. Changes are DNS annotation management only.
Container-Privileges ✅ Passed PR contains only Go source code changes; no Kubernetes container manifests with privilege specifications are added or modified.
No-Sensitive-Data-In-Logs ✅ Passed No new logging statements added. Changes only involve annotation handling, condition messages, and conditional service reconciliation - no sensitive data exposure.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.06897% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.51%. Comparing base (a6c3012) to head (8c37fb0).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...ostedcontrolplane/hostedcontrolplane_controller.go 0.00% 10 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8708      +/-   ##
==========================================
+ Coverage   41.50%   41.51%   +0.01%     
==========================================
  Files         758      758              
  Lines       93689    93711      +22     
==========================================
+ Hits        38882    38906      +24     
+ Misses      52070    52069       -1     
+ Partials     2737     2736       -1     
Files with missing lines Coverage Δ
...ator/controllers/hostedcontrolplane/infra/infra.go 50.54% <100.00%> (ø)
...ator/controllers/hostedcontrolplane/kas/service.go 45.50% <100.00%> (+5.14%) ⬆️
...or/controllers/hostedcontrolplane/oauth/service.go 75.49% <100.00%> (+0.74%) ⬆️
...trollers/hostedcluster/hostedcluster_controller.go 45.43% <100.00%> (+0.01%) ⬆️
...ostedcontrolplane/hostedcontrolplane_controller.go 45.51% <0.00%> (-0.20%) ⬇️
Flag Coverage Δ
cmd-support 34.86% <ø> (ø)
cpo-hostedcontrolplane 43.68% <60.71%> (+0.08%) ⬆️
cpo-other 43.17% <ø> (ø)
hypershift-operator 51.57% <100.00%> (+<0.01%) ⬆️
other 31.64% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@openshift-ci

openshift-ci Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@chdeshpa-hue: 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.

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@control-plane-operator/controllers/hostedcontrolplane/kas/service.go`:
- Around line 80-84: The current logic only deletes
hyperv1.ExternalDNSHostnameAnnotation when the load balancer hostname is
non-empty, causing stale annotations to persist; update the block that
references hcp.Annotations[hyperv1.DisableExternalDNSManagementAnnotation], svc
and strategy.LoadBalancer.Hostname so that if the disable flag
(hyperv1.DisableExternalDNSManagementAnnotation) is "true" you unconditionally
delete hyperv1.ExternalDNSHostnameAnnotation from svc, otherwise set the
annotation when strategy.LoadBalancer.Hostname is non-empty and delete it when
hostname is empty — locate this logic around the KAS/Konnectivity and OAuth
reconcilers where svc and hcp are referenced and adjust the if/else to ensure
unconditional deletion on opt-out (and deletion when hostname is absent).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: ad0047bd-7c08-40b4-80bd-e4f12bb09d45

📥 Commits

Reviewing files that changed from the base of the PR and between 832b848 and 8c37fb0.

⛔ Files ignored due to path filters (1)
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (8)
  • api/hypershift/v1beta1/hostedcluster_types.go
  • control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go
  • control-plane-operator/controllers/hostedcontrolplane/infra/infra.go
  • control-plane-operator/controllers/hostedcontrolplane/kas/service.go
  • control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go
  • control-plane-operator/controllers/hostedcontrolplane/oauth/service.go
  • control-plane-operator/controllers/hostedcontrolplane/oauth/service_test.go
  • hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go

Comment on lines +80 to +84
if hcp.Annotations[hyperv1.DisableExternalDNSManagementAnnotation] != "true" {
svc.Annotations[hyperv1.ExternalDNSHostnameAnnotation] = strategy.LoadBalancer.Hostname
} else {
delete(svc.Annotations, hyperv1.ExternalDNSHostnameAnnotation)
}

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 | 🟠 Major | ⚡ Quick win

Root cause: ExternalDNS annotation cleanup is incorrectly tied to hostname presence across reconcilers.

In both KAS/Konnectivity and OAuth reconcilers, stale hyperv1.ExternalDNSHostnameAnnotation is only deleted when LB hostname is non-empty. Opt-out should delete unconditionally when disabled (and ideally when hostname is absent) to guarantee day-2 cleanup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@control-plane-operator/controllers/hostedcontrolplane/kas/service.go` around
lines 80 - 84, The current logic only deletes
hyperv1.ExternalDNSHostnameAnnotation when the load balancer hostname is
non-empty, causing stale annotations to persist; update the block that
references hcp.Annotations[hyperv1.DisableExternalDNSManagementAnnotation], svc
and strategy.LoadBalancer.Hostname so that if the disable flag
(hyperv1.DisableExternalDNSManagementAnnotation) is "true" you unconditionally
delete hyperv1.ExternalDNSHostnameAnnotation from svc, otherwise set the
annotation when strategy.LoadBalancer.Hostname is non-empty and delete it when
hostname is empty — locate this logic around the KAS/Konnectivity and OAuth
reconcilers where svc and hcp are referenced and adjust the if/else to ensure
unconditional deletion on opt-out (and deletion when hostname is absent).

@hypershift-jira-solve-ci

hypershift-jira-solve-ci Bot commented Jun 10, 2026

Copy link
Copy Markdown

Test Failure Analysis Complete

Job Information

Test Failure Analysis

Error

1: CT1 Title does not start with one of fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build: "RFE-9274: add disable-external-dns-management annotation"
make: *** [Makefile:614: run-gitlint] Error 1

Summary

The gitlint check failed because the commit message title RFE-9274: add disable-external-dns-management annotation does not follow the Conventional Commits format required by the repository's .gitlint configuration. The title must start with one of the allowed type prefixes (fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build) followed by a colon, but it starts with the Jira tracker ID RFE-9274: instead.

Root Cause

The openshift/hypershift repository enforces Conventional Commits via gitlint rule contrib-title-conventional-commits (CT1). The .gitlint configuration at the repo root specifies the allowed type prefixes:

[contrib-title-conventional-commits]
types = fix,feat,chore,docs,style,refactor,perf,test,revert,ci,build

The commit message title RFE-9274: add disable-external-dns-management annotation uses the Jira issue key RFE-9274 as the prefix instead of a conventional commit type. The gitlint CT1 rule parses the text before the first colon as the commit type and rejects it because RFE-9274 is not in the allowed types list.

This is purely a commit message formatting issue — there is no code or test problem.

Recommendations

Amend the commit message to use a Conventional Commits prefix. Since this PR adds a new annotation/feature, feat is the appropriate type. The Jira key can be included in the scope or body. Examples:

  1. Recommended — Jira key as scope:

    feat([RFE-9274](https://redhat.atlassian.net/browse/RFE-9274)): add disable-external-dns-management annotation
    
  2. Alternative — Jira key in body only:

    feat: add disable-external-dns-management annotation
    

    (keep RFE-9274 in the commit body)

To fix, run:

git commit --amend -m "feat(RFE-9274): add disable-external-dns-management annotation" -m "<existing body>"
git push --force-with-lease
Evidence
Evidence Detail
Failing rule CT1 (contrib-title-conventional-commits)
Commit title RFE-9274: add disable-external-dns-management annotation
Rejected prefix RFE-9274 — not in allowed types list
Allowed types fix, feat, chore, docs, style, refactor, perf, test, revert, ci, build
Config file .gitlint in repo root
Makefile target run-gitlint (line 614)
Exit code 2 (gitlint validation failure)
Commit range linted 832b8480..8c37fb06 (1 commit)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api Indicates the PR includes changes for the API area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants