diff --git a/api/v1alpha1/sandbox_types.go b/api/v1alpha1/sandbox_types.go index b34b57fda..058853348 100644 --- a/api/v1alpha1/sandbox_types.go +++ b/api/v1alpha1/sandbox_types.go @@ -157,8 +157,7 @@ type Lifecycle struct { // SandboxStatus defines the observed state of Sandbox. type SandboxStatus struct { // serviceFQDN that is valid for default cluster settings - // Limitation: Hardcoded to the domain .cluster.local - // e.g. sandbox-example.default.svc.cluster.local + // The domain defaults to cluster.local but is configurable via the controller's --cluster-domain flag. // +optional ServiceFQDN string `json:"serviceFQDN,omitempty"` diff --git a/cmd/agent-sandbox-controller/main.go b/cmd/agent-sandbox-controller/main.go index 73c23f89b..5ce571c36 100644 --- a/cmd/agent-sandbox-controller/main.go +++ b/cmd/agent-sandbox-controller/main.go @@ -50,6 +50,7 @@ func main() { var leaderElectionNamespace string var probeAddr string var extensions bool + var clusterDomain string var enableTracing bool var enablePprof bool var enablePprofDebug bool @@ -61,6 +62,7 @@ func main() { var sandboxClaimConcurrentWorkers int var sandboxWarmPoolConcurrentWorkers int var sandboxTemplateConcurrentWorkers int + flag.StringVar(&clusterDomain, "cluster-domain", "cluster.local", "Kubernetes cluster domain for service FQDN generation") flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") flag.BoolVar(&enableLeaderElection, "leader-elect", true, @@ -213,9 +215,10 @@ func main() { asmetrics.RegisterSandboxCollector(mgr.GetClient(), mgr.GetLogger().WithName("sandbox-collector")) if err = (&controllers.SandboxReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Tracer: instrumenter, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Tracer: instrumenter, + ClusterDomain: clusterDomain, }).SetupWithManager(mgr, sandboxConcurrentWorkers); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Sandbox") os.Exit(1) diff --git a/controllers/sandbox_controller.go b/controllers/sandbox_controller.go index 94f443e3d..70071dbf0 100644 --- a/controllers/sandbox_controller.go +++ b/controllers/sandbox_controller.go @@ -99,8 +99,9 @@ func init() { // SandboxReconciler reconciles a Sandbox object type SandboxReconciler struct { client.Client - Scheme *runtime.Scheme - Tracer asmetrics.Instrumenter + Scheme *runtime.Scheme + Tracer asmetrics.Instrumenter + ClusterDomain string } //+kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes,verbs=get;list;watch;create;update;patch;delete @@ -388,7 +389,7 @@ func (r *SandboxReconciler) reconcileService(ctx context.Context, sandbox *sandb // Already owned by this sandbox — no action needed. } - setServiceStatus(sandbox, service) + r.setServiceStatus(sandbox, service) return service, nil } @@ -420,15 +421,14 @@ func (r *SandboxReconciler) reconcileService(ctx context.Context, sandbox *sandb return nil, err } - setServiceStatus(sandbox, service) + r.setServiceStatus(sandbox, service) return service, nil } // setServiceStatus updates the sandbox status with the service name and FQDN. -// TODO(barney-s): hardcoded to svc.cluster.local which is the default. Need a way to change it. -func setServiceStatus(sandbox *sandboxv1alpha1.Sandbox, service *corev1.Service) { +func (r *SandboxReconciler) setServiceStatus(sandbox *sandboxv1alpha1.Sandbox, service *corev1.Service) { sandbox.Status.Service = service.Name - sandbox.Status.ServiceFQDN = service.Name + "." + service.Namespace + ".svc.cluster.local" + sandbox.Status.ServiceFQDN = service.Name + "." + service.Namespace + ".svc." + r.ClusterDomain } func (r *SandboxReconciler) reconcilePod(ctx context.Context, sandbox *sandboxv1alpha1.Sandbox, nameHash string) (*corev1.Pod, error) { diff --git a/controllers/sandbox_controller_test.go b/controllers/sandbox_controller_test.go index c610b88d9..b816d9003 100644 --- a/controllers/sandbox_controller_test.go +++ b/controllers/sandbox_controller_test.go @@ -840,9 +840,10 @@ func TestReconcile(t *testing.T) { sb.Annotations = tc.sandboxAnnotations } r := SandboxReconciler{ - Client: newFakeClient(append(tc.initialObjs, sb)...), - Scheme: Scheme, - Tracer: asmetrics.NewNoOp(), + Client: newFakeClient(append(tc.initialObjs, sb)...), + Scheme: Scheme, + Tracer: asmetrics.NewNoOp(), + ClusterDomain: "cluster.local", } _, err := r.Reconcile(t.Context(), ctrl.Request{ @@ -1344,9 +1345,10 @@ func TestReconcilePod(t *testing.T) { sandbox := tc.sandbox.DeepCopy() r := SandboxReconciler{ - Client: newFakeClient(append(tc.initialObjs, sandbox)...), - Scheme: Scheme, - Tracer: asmetrics.NewNoOp(), + Client: newFakeClient(append(tc.initialObjs, sandbox)...), + Scheme: Scheme, + Tracer: asmetrics.NewNoOp(), + ClusterDomain: "cluster.local", } pod, err := r.reconcilePod(t.Context(), sandbox, nameHash) @@ -1577,9 +1579,10 @@ func TestReconcileService(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { r := SandboxReconciler{ - Client: newFakeClient(append(tc.initialObjs, tc.sandbox)...), - Scheme: Scheme, - Tracer: asmetrics.NewNoOp(), + Client: newFakeClient(append(tc.initialObjs, tc.sandbox)...), + Scheme: Scheme, + Tracer: asmetrics.NewNoOp(), + ClusterDomain: "cluster.local", } svc, err := r.reconcileService(t.Context(), tc.sandbox, nameHash) @@ -1908,3 +1911,39 @@ func TestSandboxExpiry(t *testing.T) { }) } } + +func TestSetServiceStatusCustomDomain(t *testing.T) { + testCases := []struct { + name string + clusterDomain string + wantFQDN string + }{ + { + name: "default cluster.local domain", + clusterDomain: "cluster.local", + wantFQDN: "my-svc.my-ns.svc.cluster.local", + }, + { + name: "custom cluster domain", + clusterDomain: "custom.domain", + wantFQDN: "my-svc.my-ns.svc.custom.domain", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + r := &SandboxReconciler{ + ClusterDomain: tc.clusterDomain, + } + sandbox := &sandboxv1alpha1.Sandbox{} + service := &corev1.Service{} + service.Name = "my-svc" + service.Namespace = "my-ns" + + r.setServiceStatus(sandbox, service) + + require.Equal(t, "my-svc", sandbox.Status.Service) + require.Equal(t, tc.wantFQDN, sandbox.Status.ServiceFQDN) + }) + } +}