Просмотр исходного кода

cmd/k8s-operator: support being the default loadbalancer controller

Updates #502

Signed-off-by: Mike Beaumont <[email protected]>
Mike Beaumont 2 лет назад
Родитель
Сommit
ce4bf41dcf
3 измененных файлов с 61 добавлено и 7 удалено
  1. 7 3
      cmd/k8s-operator/operator.go
  2. 49 0
      cmd/k8s-operator/operator_test.go
  3. 5 4
      cmd/k8s-operator/svc.go

+ 7 - 3
cmd/k8s-operator/operator.go

@@ -183,6 +183,9 @@ waitOnline:
 // startReconcilers starts the controller-runtime manager and registers the
 // ServiceReconciler.
 func startReconcilers(zlog *zap.SugaredLogger, tsNamespace string, restConfig *rest.Config, tsClient *tailscale.Client, image, priorityClassName, tags string) {
+	var (
+		isDefaultLoadBalancer = defaultBool("OPERATOR_DEFAULT_LOAD_BALANCER", false)
+	)
 	startlog := zlog.Named("startReconcilers")
 	// For secrets and statefulsets, we only get permission to touch the objects
 	// in the controller's own namespace. This cannot be expressed by
@@ -234,9 +237,10 @@ func startReconcilers(zlog *zap.SugaredLogger, tsNamespace string, restConfig *r
 		Watches(&appsv1.StatefulSet{}, reconcileFilter).
 		Watches(&corev1.Secret{}, reconcileFilter).
 		Complete(&ServiceReconciler{
-			ssr:    ssr,
-			Client: mgr.GetClient(),
-			logger: zlog.Named("service-reconciler"),
+			ssr:                   ssr,
+			Client:                mgr.GetClient(),
+			logger:                zlog.Named("service-reconciler"),
+			isDefaultLoadBalancer: isDefaultLoadBalancer,
 		})
 	if err != nil {
 		startlog.Fatalf("could not create controller: %v", err)

+ 49 - 0
cmd/k8s-operator/operator_test.go

@@ -650,6 +650,52 @@ func TestCustomPriorityClassName(t *testing.T) {
 	expectEqual(t, fc, expectedSTS(shortName, fullName, "custom-priority-class-name", "tailscale-critical"))
 }
 
+func TestDefaultLoadBalancer(t *testing.T) {
+	fc := fake.NewFakeClient()
+	ft := &fakeTSClient{}
+	zl, err := zap.NewDevelopment()
+	if err != nil {
+		t.Fatal(err)
+	}
+	sr := &ServiceReconciler{
+		Client: fc,
+		ssr: &tailscaleSTSReconciler{
+			Client:            fc,
+			tsClient:          ft,
+			defaultTags:       []string{"tag:k8s"},
+			operatorNamespace: "operator-ns",
+			proxyImage:        "tailscale/tailscale",
+		},
+		logger:                zl.Sugar(),
+		isDefaultLoadBalancer: true,
+	}
+
+	// Create a service that we should manage, and check that the initial round
+	// of objects looks right.
+	mustCreate(t, fc, &corev1.Service{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "test",
+			Namespace: "default",
+			// The apiserver is supposed to set the UID, but the fake client
+			// doesn't. So, set it explicitly because other code later depends
+			// on it being set.
+			UID: types.UID("1234-UID"),
+		},
+		Spec: corev1.ServiceSpec{
+			ClusterIP: "10.20.30.40",
+			Type:      corev1.ServiceTypeLoadBalancer,
+		},
+	})
+
+	expectReconciled(t, sr, "default", "test")
+
+	fullName, shortName := findGenName(t, fc, "default", "test")
+
+	expectEqual(t, fc, expectedSecret(fullName))
+	expectEqual(t, fc, expectedHeadlessService(shortName))
+	expectEqual(t, fc, expectedSTS(shortName, fullName, "default-test", ""))
+}
+
 func expectedSecret(name string) *corev1.Secret {
 	return &corev1.Secret{
 		TypeMeta: metav1.TypeMeta{
@@ -780,6 +826,9 @@ func findGenName(t *testing.T, client client.Client, ns, name string) (full, noS
 	if err != nil {
 		t.Fatalf("finding secret for %q: %v", name, err)
 	}
+	if s == nil {
+		t.Fatalf("no secret found for %q", name)
+	}
 	return s.GetName(), strings.TrimSuffix(s.GetName(), "-0")
 }
 

+ 5 - 4
cmd/k8s-operator/svc.go

@@ -20,8 +20,9 @@ import (
 
 type ServiceReconciler struct {
 	client.Client
-	ssr    *tailscaleSTSReconciler
-	logger *zap.SugaredLogger
+	ssr                   *tailscaleSTSReconciler
+	logger                *zap.SugaredLogger
+	isDefaultLoadBalancer bool
 }
 
 func childResourceLabels(name, ns, typ string) map[string]string {
@@ -177,8 +178,8 @@ func (a *ServiceReconciler) shouldExpose(svc *corev1.Service) bool {
 func (a *ServiceReconciler) hasLoadBalancerClass(svc *corev1.Service) bool {
 	return svc != nil &&
 		svc.Spec.Type == corev1.ServiceTypeLoadBalancer &&
-		svc.Spec.LoadBalancerClass != nil &&
-		*svc.Spec.LoadBalancerClass == "tailscale"
+		(svc.Spec.LoadBalancerClass != nil && *svc.Spec.LoadBalancerClass == "tailscale" ||
+			svc.Spec.LoadBalancerClass == nil && a.isDefaultLoadBalancer)
 }
 
 func (a *ServiceReconciler) hasAnnotation(svc *corev1.Service) bool {