@@ -17,7 +17,13 @@ limitations under the License.
1717package endpoints
1818
1919import (
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+ }
0 commit comments