Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 9e98c12

Browse files
committed
operator: auto detect base domain
1 parent be25dc3 commit 9e98c12

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

deploy/operator/config/rbac/role.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ rules:
5454
- get
5555
- patch
5656
- update
57+
- apiGroups:
58+
- config.openshift.io
59+
resources:
60+
- dnses
61+
verbs:
62+
- get
63+
- list
64+
- watch
5765
- apiGroups:
5866
- coordination.k8s.io
5967
resources:

deploy/operator/internal/controller/jumpstarter/endpoints/discovery.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ limitations under the License.
1717
package endpoints
1818

1919
import (
20+
"context"
21+
"fmt"
22+
23+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24+
"k8s.io/apimachinery/pkg/runtime/schema"
2025
"k8s.io/client-go/discovery"
2126
"k8s.io/client-go/rest"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
2228
"sigs.k8s.io/controller-runtime/pkg/log"
2329
)
2430

@@ -48,3 +54,48 @@ func discoverAPIResource(config *rest.Config, groupVersion, kind string) bool {
4854

4955
return false
5056
}
57+
58+
// DiscoverBaseDomain attempts to auto-detect the baseDomain from OpenShift DNS cluster config
59+
// It returns the detected baseDomain in the format "namespace.apps.baseDomain" for
60+
// OpenShift clusters, or an error if it cannot be determined.
61+
func DiscoverBaseDomain(ctx context.Context, c client.Client, namespace string) (string, error) {
62+
logger := log.FromContext(ctx)
63+
64+
// Try to fetch the OpenShift DNS cluster configuration
65+
dns := &unstructured.Unstructured{}
66+
dns.SetGroupVersionKind(schema.GroupVersionKind{
67+
Group: "config.openshift.io",
68+
Version: "v1",
69+
Kind: "DNS",
70+
})
71+
72+
err := c.Get(ctx, client.ObjectKey{Name: "cluster"}, dns)
73+
if err != nil {
74+
logger.Error(err, "Failed to get OpenShift DNS cluster config - baseDomain cannot be auto-detected")
75+
return "", fmt.Errorf("failed to auto-detect baseDomain from OpenShift DNS cluster config: %w", err)
76+
}
77+
78+
// Extract spec.baseDomain from the DNS object
79+
spec, found, err := unstructured.NestedMap(dns.Object, "spec")
80+
if err != nil || !found {
81+
logger.Error(err, "Failed to get spec from OpenShift DNS cluster config")
82+
return "", fmt.Errorf("failed to get spec from OpenShift DNS cluster config: spec not found")
83+
}
84+
85+
openShiftBaseDomain, found, err := unstructured.NestedString(spec, "baseDomain")
86+
if err != nil || !found || openShiftBaseDomain == "" {
87+
logger.Error(err, "Failed to get baseDomain from OpenShift DNS cluster config")
88+
return "", fmt.Errorf("failed to get baseDomain from OpenShift DNS cluster config: baseDomain not found or empty")
89+
}
90+
91+
// Format the baseDomain as "namespace.apps.openShiftBaseDomain"
92+
// This matches the Helm template behavior when .noNs is false
93+
detectedBaseDomain := fmt.Sprintf("%s.apps.%s", namespace, openShiftBaseDomain)
94+
95+
logger.Info("Auto-detected baseDomain from OpenShift DNS cluster config",
96+
"openShiftBaseDomain", openShiftBaseDomain,
97+
"detectedBaseDomain", detectedBaseDomain,
98+
"namespace", namespace)
99+
100+
return detectedBaseDomain, nil
101+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
Copyright 2025.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package endpoints
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
. "github.com/onsi/gomega"
22+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
23+
"k8s.io/apimachinery/pkg/runtime/schema"
24+
)
25+
26+
var _ = Describe("DiscoverBaseDomain", func() {
27+
// Note: These tests require OpenShift CRDs to be available in the test environment.
28+
// They will be skipped if the CRDs are not present, which is expected in non-OpenShift environments.
29+
30+
Context("when OpenShift is available", func() {
31+
BeforeEach(func() {
32+
// Check if OpenShift CRDs are available
33+
dns := &unstructured.Unstructured{}
34+
dns.SetGroupVersionKind(schema.GroupVersionKind{
35+
Group: "config.openshift.io",
36+
Version: "v1",
37+
Kind: "DNS",
38+
})
39+
dns.SetName("cluster")
40+
dns.Object["spec"] = map[string]interface{}{
41+
"baseDomain": "test-check.com",
42+
}
43+
44+
// Try to create a test DNS object to check if the CRD is available
45+
err := k8sClient.Create(ctx, dns)
46+
if err != nil {
47+
Skip("Skipping OpenShift baseDomain auto-detection tests: OpenShift CRDs not available in test environment")
48+
}
49+
// Clean up test object
50+
_ = k8sClient.Delete(ctx, dns)
51+
})
52+
53+
Context("when OpenShift DNS cluster config exists", func() {
54+
It("should successfully auto-detect baseDomain", func() {
55+
// Create a mock OpenShift DNS cluster config
56+
dns := &unstructured.Unstructured{}
57+
dns.SetGroupVersionKind(schema.GroupVersionKind{
58+
Group: "config.openshift.io",
59+
Version: "v1",
60+
Kind: "DNS",
61+
})
62+
dns.SetName("cluster")
63+
dns.Object["spec"] = map[string]interface{}{
64+
"baseDomain": "example.com",
65+
}
66+
67+
// Create the DNS object in the cluster
68+
err := k8sClient.Create(ctx, dns)
69+
Expect(err).NotTo(HaveOccurred())
70+
71+
// Test auto-detection
72+
detectedBaseDomain, err := DiscoverBaseDomain(ctx, k8sClient, "test-namespace")
73+
Expect(err).NotTo(HaveOccurred())
74+
Expect(detectedBaseDomain).To(Equal("test-namespace.apps.example.com"))
75+
76+
// Cleanup
77+
err = k8sClient.Delete(ctx, dns)
78+
Expect(err).NotTo(HaveOccurred())
79+
})
80+
})
81+
82+
Context("when OpenShift DNS cluster config has empty baseDomain", func() {
83+
It("should return an error", func() {
84+
// Create a mock OpenShift DNS cluster config with empty baseDomain
85+
dns := &unstructured.Unstructured{}
86+
dns.SetGroupVersionKind(schema.GroupVersionKind{
87+
Group: "config.openshift.io",
88+
Version: "v1",
89+
Kind: "DNS",
90+
})
91+
dns.SetName("cluster")
92+
dns.Object["spec"] = map[string]interface{}{
93+
"baseDomain": "",
94+
}
95+
96+
// Create the DNS object in the cluster
97+
err := k8sClient.Create(ctx, dns)
98+
Expect(err).NotTo(HaveOccurred())
99+
100+
// Test auto-detection with empty baseDomain
101+
_, err = DiscoverBaseDomain(ctx, k8sClient, "test-namespace")
102+
Expect(err).To(HaveOccurred())
103+
Expect(err.Error()).To(ContainSubstring("baseDomain not found or empty"))
104+
105+
// Cleanup
106+
err = k8sClient.Delete(ctx, dns)
107+
Expect(err).NotTo(HaveOccurred())
108+
})
109+
})
110+
})
111+
112+
Context("when OpenShift DNS cluster config does not exist", func() {
113+
It("should return an error", func() {
114+
// Try to auto-detect when no DNS config exists
115+
// This test will work even without OpenShift CRDs because it just checks error handling
116+
_, err := DiscoverBaseDomain(ctx, k8sClient, "test-namespace")
117+
Expect(err).To(HaveOccurred())
118+
Expect(err.Error()).To(ContainSubstring("failed to auto-detect baseDomain"))
119+
})
120+
})
121+
})

deploy/operator/internal/controller/jumpstarter/jumpstarter_controller.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ type JumpstarterReconciler struct {
8585
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete
8686
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes/status,verbs=get;update;patch
8787

88+
// OpenShift config resources (for baseDomain auto-detection)
89+
// +kubebuilder:rbac:groups=config.openshift.io,resources=dnses,verbs=get;list;watch
90+
8891
// Monitoring resources
8992
// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors,verbs=get;list;watch;create;update;patch;delete
9093

@@ -127,6 +130,18 @@ func (r *JumpstarterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
127130
return ctrl.Result{}, nil
128131
}
129132

133+
// Auto-detect baseDomain if not provided
134+
if jumpstarter.Spec.BaseDomain == "" {
135+
log.Info("BaseDomain not provided, attempting auto-detection from OpenShift DNS cluster config")
136+
detectedBaseDomain, err := endpoints.DiscoverBaseDomain(ctx, r.Client, jumpstarter.Namespace)
137+
if err != nil {
138+
log.Error(err, "Failed to auto-detect baseDomain - baseDomain is required but was not provided and could not be auto-detected")
139+
return ctrl.Result{}, fmt.Errorf("baseDomain is required but was not provided and could not be auto-detected: %w", err)
140+
}
141+
jumpstarter.Spec.BaseDomain = detectedBaseDomain
142+
log.Info("Successfully auto-detected baseDomain", "baseDomain", detectedBaseDomain)
143+
}
144+
130145
// Reconcile RBAC resources first
131146
if err := r.reconcileRBAC(ctx, &jumpstarter); err != nil {
132147
log.Error(err, "Failed to reconcile RBAC")

0 commit comments

Comments
 (0)