Skip to content

Commit d6caaa2

Browse files
committed
try to discover baseDomain from ingresses
1 parent aa85cc8 commit d6caaa2

File tree

5 files changed

+120
-11
lines changed

5 files changed

+120
-11
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+
- ingresses
61+
verbs:
62+
- get
63+
- list
64+
- watch
5765
- apiGroups:
5866
- coordination.k8s.io
5967
resources:

deploy/operator/dist/install.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,14 @@ rules:
27252725
- get
27262726
- patch
27272727
- update
2728+
- apiGroups:
2729+
- config.openshift.io
2730+
resources:
2731+
- ingresses
2732+
verbs:
2733+
- get
2734+
- list
2735+
- watch
27282736
- apiGroups:
27292737
- coordination.k8s.io
27302738
resources:
@@ -2999,8 +3007,8 @@ spec:
29993007
- --health-probe-bind-address=:8081
30003008
command:
30013009
- /manager
3002-
image: quay.io/jumpstarter-dev/jumpstarter-operator:latest
3003-
imagePullPolicy: IfNotPresent
3010+
image: quay.io/evakhoni/jumpstarter-operator:dev
3011+
imagePullPolicy: Always
30043012
livenessProbe:
30053013
httpGet:
30063014
path: /healthz

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

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

1919
import (
20+
"context"
21+
"fmt"
22+
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/apimachinery/pkg/runtime/schema"
2025
"k8s.io/client-go/discovery"
26+
"k8s.io/client-go/dynamic"
2127
"k8s.io/client-go/rest"
2228
"sigs.k8s.io/controller-runtime/pkg/log"
2329
)
@@ -48,3 +54,72 @@ func discoverAPIResource(config *rest.Config, groupVersion, kind string) bool {
4854

4955
return false
5056
}
57+
58+
// detectOpenShiftBaseDomain attempts to detect the cluster's base domain from OpenShift's
59+
// ingresses.config.openshift.io/cluster resource. Returns empty string if not available.
60+
func detectOpenShiftBaseDomain(config *rest.Config) string {
61+
logger := log.Log.WithName("basedomain-detection")
62+
63+
// Create dynamic client for unstructured access to OpenShift config API
64+
dynamicClient, err := dynamic.NewForConfig(config)
65+
if err != nil {
66+
logger.Error(err, "Failed to create dynamic client for baseDomain detection")
67+
return ""
68+
}
69+
70+
// Define the GVR for ingresses.config.openshift.io
71+
ingressGVR := schema.GroupVersionResource{
72+
Group: "config.openshift.io",
73+
Version: "v1",
74+
Resource: "ingresses",
75+
}
76+
77+
// Get the cluster-scoped "cluster" ingress config
78+
ingressConfig, err := dynamicClient.Resource(ingressGVR).Get(context.Background(), "cluster", metav1.GetOptions{})
79+
if err != nil {
80+
// This is expected on non-OpenShift clusters, log at debug level
81+
logger.V(1).Info("Could not fetch OpenShift ingress config (expected on non-OpenShift clusters)", "error", err.Error())
82+
return ""
83+
}
84+
85+
// Extract spec.domain from the unstructured object
86+
domain, found, err := unstructuredNestedString(ingressConfig.Object, "spec", "domain")
87+
if err != nil || !found {
88+
logger.Info("OpenShift ingress config found but spec.domain not available")
89+
return ""
90+
}
91+
92+
// Add jumpstarter prefix to the cluster's apps domain
93+
baseDomain := fmt.Sprintf("jumpstarter.%s", domain)
94+
logger.Info("Auto-detected OpenShift baseDomain", "clusterDomain", domain, "baseDomain", baseDomain)
95+
return baseDomain
96+
}
97+
98+
// unstructuredNestedString extracts a nested string from an unstructured object
99+
func unstructuredNestedString(obj map[string]interface{}, fields ...string) (string, bool, error) {
100+
val, found, err := nestedField(obj, fields...)
101+
if !found || err != nil {
102+
return "", found, err
103+
}
104+
s, ok := val.(string)
105+
if !ok {
106+
return "", false, nil
107+
}
108+
return s, true, nil
109+
}
110+
111+
// nestedField extracts a nested field from a map
112+
func nestedField(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
113+
var val interface{} = obj
114+
for _, field := range fields {
115+
m, ok := val.(map[string]interface{})
116+
if !ok {
117+
return nil, false, nil
118+
}
119+
val, ok = m[field]
120+
if !ok {
121+
return nil, false, nil
122+
}
123+
}
124+
return val, true, nil
125+
}

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ import (
3434

3535
// Reconciler provides endpoint reconciliation functionality
3636
type Reconciler struct {
37-
Client client.Client
38-
Scheme *runtime.Scheme
39-
IngressAvailable bool
40-
RouteAvailable bool
37+
Client client.Client
38+
Scheme *runtime.Scheme
39+
IngressAvailable bool
40+
RouteAvailable bool
41+
DefaultBaseDomain string // Default baseDomain auto-detected from cluster (e.g., OpenShift ingress config)
4142
}
4243

4344
// NewReconciler creates a new endpoint reconciler
@@ -48,21 +49,35 @@ func NewReconciler(client client.Client, scheme *runtime.Scheme, config *rest.Co
4849
ingressAvailable := discoverAPIResource(config, "networking.k8s.io/v1", "Ingress")
4950
routeAvailable := discoverAPIResource(config, "route.openshift.io/v1", "Route")
5051

52+
// Attempt to auto-detect default baseDomain on OpenShift clusters
53+
var defaultBaseDomain string
54+
if routeAvailable {
55+
defaultBaseDomain = detectOpenShiftBaseDomain(config)
56+
}
57+
5158
log.Info("API discovery completed",
5259
"ingressAvailable", ingressAvailable,
53-
"routeAvailable", routeAvailable)
60+
"routeAvailable", routeAvailable,
61+
"defaultBaseDomain", defaultBaseDomain)
5462

5563
return &Reconciler{
56-
Client: client,
57-
Scheme: scheme,
58-
IngressAvailable: ingressAvailable,
59-
RouteAvailable: routeAvailable,
64+
Client: client,
65+
Scheme: scheme,
66+
IngressAvailable: ingressAvailable,
67+
RouteAvailable: routeAvailable,
68+
DefaultBaseDomain: defaultBaseDomain,
6069
}
6170
}
6271

6372
// ApplyDefaults applies endpoint defaults to a JumpstarterSpec using the
6473
// reconciler's discovered cluster capabilities (Route vs Ingress availability).
74+
// If baseDomain is not provided in the spec, it will use the default baseDomain
75+
// (auto-detected from OpenShift cluster config) if available.
6576
func (r *Reconciler) ApplyDefaults(spec *operatorv1alpha1.JumpstarterSpec) {
77+
// Use default baseDomain if not provided in spec
78+
if spec.BaseDomain == "" && r.DefaultBaseDomain != "" {
79+
spec.BaseDomain = r.DefaultBaseDomain
80+
}
6681
ApplyEndpointDefaults(spec, r.RouteAvailable, r.IngressAvailable)
6782
}
6883

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ type JumpstarterReconciler struct {
8686
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes/status,verbs=get;update;patch
8787
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes/custom-host,verbs=get;create;update;patch
8888

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

0 commit comments

Comments
 (0)