|
|
|
|
@ -8,6 +8,8 @@ package main |
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"encoding/json" |
|
|
|
|
"maps" |
|
|
|
|
"reflect" |
|
|
|
|
"testing" |
|
|
|
|
|
|
|
|
|
"slices" |
|
|
|
|
@ -18,87 +20,78 @@ import ( |
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
|
|
|
|
"k8s.io/apimachinery/pkg/types" |
|
|
|
|
"k8s.io/client-go/tools/record" |
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client" |
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake" |
|
|
|
|
"tailscale.com/ipn" |
|
|
|
|
"tailscale.com/ipn/ipnstate" |
|
|
|
|
tsapi "tailscale.com/k8s-operator/apis/v1alpha1" |
|
|
|
|
"tailscale.com/tailcfg" |
|
|
|
|
"tailscale.com/types/ptr" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func TestIngressPGReconciler(t *testing.T) { |
|
|
|
|
tsIngressClass := &networkingv1.IngressClass{ |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{Name: "tailscale"}, |
|
|
|
|
Spec: networkingv1.IngressClassSpec{Controller: "tailscale.com/ts-ingress"}, |
|
|
|
|
} |
|
|
|
|
ingPGR, fc, ft := setupIngressTest(t) |
|
|
|
|
|
|
|
|
|
// Pre-create the ProxyGroup
|
|
|
|
|
pg := &tsapi.ProxyGroup{ |
|
|
|
|
ing := &networkingv1.Ingress{ |
|
|
|
|
TypeMeta: metav1.TypeMeta{Kind: "Ingress", APIVersion: "networking.k8s.io/v1"}, |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{ |
|
|
|
|
Name: "test-pg", |
|
|
|
|
Generation: 1, |
|
|
|
|
Name: "test-ingress", |
|
|
|
|
Namespace: "default", |
|
|
|
|
UID: types.UID("1234-UID"), |
|
|
|
|
Annotations: map[string]string{ |
|
|
|
|
"tailscale.com/proxy-group": "test-pg", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
Spec: tsapi.ProxyGroupSpec{ |
|
|
|
|
Type: tsapi.ProxyGroupTypeIngress, |
|
|
|
|
Spec: networkingv1.IngressSpec{ |
|
|
|
|
IngressClassName: ptr.To("tailscale"), |
|
|
|
|
DefaultBackend: &networkingv1.IngressBackend{ |
|
|
|
|
Service: &networkingv1.IngressServiceBackend{ |
|
|
|
|
Name: "test", |
|
|
|
|
Port: networkingv1.ServiceBackendPort{ |
|
|
|
|
Number: 8080, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
TLS: []networkingv1.IngressTLS{ |
|
|
|
|
{Hosts: []string{"my-svc.tailnetxyz.ts.net"}}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
mustCreate(t, fc, ing) |
|
|
|
|
|
|
|
|
|
// Pre-create the ConfigMap for the ProxyGroup
|
|
|
|
|
pgConfigMap := &corev1.ConfigMap{ |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{ |
|
|
|
|
Name: "test-pg-ingress-config", |
|
|
|
|
Namespace: "operator-ns", |
|
|
|
|
}, |
|
|
|
|
BinaryData: map[string][]byte{ |
|
|
|
|
"serve-config.json": []byte(`{"Services":{}}`), |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
// Verify initial reconciliation
|
|
|
|
|
expectReconciled(t, ingPGR, "default", "test-ingress") |
|
|
|
|
verifyServeConfig(t, fc, "svc:my-svc", false) |
|
|
|
|
verifyVIPService(t, ft, "svc:my-svc", []string{"443"}) |
|
|
|
|
|
|
|
|
|
fc := fake.NewClientBuilder(). |
|
|
|
|
WithScheme(tsapi.GlobalScheme). |
|
|
|
|
WithObjects(pg, pgConfigMap, tsIngressClass). |
|
|
|
|
WithStatusSubresource(pg). |
|
|
|
|
Build() |
|
|
|
|
mustUpdateStatus(t, fc, "", pg.Name, func(pg *tsapi.ProxyGroup) { |
|
|
|
|
pg.Status.Conditions = []metav1.Condition{ |
|
|
|
|
{ |
|
|
|
|
Type: string(tsapi.ProxyGroupReady), |
|
|
|
|
Status: metav1.ConditionTrue, |
|
|
|
|
ObservedGeneration: 1, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
mustUpdate(t, fc, "default", "test-ingress", func(ing *networkingv1.Ingress) { |
|
|
|
|
ing.Annotations["tailscale.com/tags"] = "tag:custom,tag:test" |
|
|
|
|
}) |
|
|
|
|
ft := &fakeTSClient{} |
|
|
|
|
fakeTsnetServer := &fakeTSNetServer{certDomains: []string{"foo.com"}} |
|
|
|
|
zl, err := zap.NewDevelopment() |
|
|
|
|
expectReconciled(t, ingPGR, "default", "test-ingress") |
|
|
|
|
|
|
|
|
|
// Verify VIPService uses custom tags
|
|
|
|
|
vipSvc, err := ft.getVIPService(context.Background(), "svc:my-svc") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
t.Fatalf("getting VIPService: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
lc := &fakeLocalClient{ |
|
|
|
|
status: &ipnstate.Status{ |
|
|
|
|
CurrentTailnet: &ipnstate.TailnetStatus{ |
|
|
|
|
MagicDNSSuffix: "ts.net", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
if vipSvc == nil { |
|
|
|
|
t.Fatal("VIPService not created") |
|
|
|
|
} |
|
|
|
|
ingPGR := &IngressPGReconciler{ |
|
|
|
|
Client: fc, |
|
|
|
|
tsClient: ft, |
|
|
|
|
tsnetServer: fakeTsnetServer, |
|
|
|
|
defaultTags: []string{"tag:k8s"}, |
|
|
|
|
tsNamespace: "operator-ns", |
|
|
|
|
logger: zl.Sugar(), |
|
|
|
|
recorder: record.NewFakeRecorder(10), |
|
|
|
|
lc: lc, |
|
|
|
|
wantTags := []string{"tag:custom", "tag:test"} // custom tags only
|
|
|
|
|
gotTags := slices.Clone(vipSvc.Tags) |
|
|
|
|
slices.Sort(gotTags) |
|
|
|
|
slices.Sort(wantTags) |
|
|
|
|
if !slices.Equal(gotTags, wantTags) { |
|
|
|
|
t.Errorf("incorrect VIPService tags: got %v, want %v", gotTags, wantTags) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Test 1: Default tags
|
|
|
|
|
ing := &networkingv1.Ingress{ |
|
|
|
|
// Create second Ingress
|
|
|
|
|
ing2 := &networkingv1.Ingress{ |
|
|
|
|
TypeMeta: metav1.TypeMeta{Kind: "Ingress", APIVersion: "networking.k8s.io/v1"}, |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{ |
|
|
|
|
Name: "test-ingress", |
|
|
|
|
Name: "my-other-ingress", |
|
|
|
|
Namespace: "default", |
|
|
|
|
UID: types.UID("1234-UID"), |
|
|
|
|
UID: types.UID("5678-UID"), |
|
|
|
|
Annotations: map[string]string{ |
|
|
|
|
"tailscale.com/proxy-group": "test-pg", |
|
|
|
|
}, |
|
|
|
|
@ -114,16 +107,28 @@ func TestIngressPGReconciler(t *testing.T) { |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
TLS: []networkingv1.IngressTLS{ |
|
|
|
|
{Hosts: []string{"my-svc.tailnetxyz.ts.net"}}, |
|
|
|
|
{Hosts: []string{"my-other-svc.tailnetxyz.ts.net"}}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
mustCreate(t, fc, ing) |
|
|
|
|
mustCreate(t, fc, ing2) |
|
|
|
|
|
|
|
|
|
// Verify initial reconciliation
|
|
|
|
|
expectReconciled(t, ingPGR, "default", "test-ingress") |
|
|
|
|
// Verify second Ingress reconciliation
|
|
|
|
|
expectReconciled(t, ingPGR, "default", "my-other-ingress") |
|
|
|
|
verifyServeConfig(t, fc, "svc:my-other-svc", false) |
|
|
|
|
verifyVIPService(t, ft, "svc:my-other-svc", []string{"443"}) |
|
|
|
|
|
|
|
|
|
// Get and verify the ConfigMap was updated
|
|
|
|
|
// Verify first Ingress is still working
|
|
|
|
|
verifyServeConfig(t, fc, "svc:my-svc", false) |
|
|
|
|
verifyVIPService(t, ft, "svc:my-svc", []string{"443"}) |
|
|
|
|
|
|
|
|
|
// Delete second Ingress
|
|
|
|
|
if err := fc.Delete(context.Background(), ing2); err != nil { |
|
|
|
|
t.Fatalf("deleting second Ingress: %v", err) |
|
|
|
|
} |
|
|
|
|
expectReconciled(t, ingPGR, "default", "my-other-ingress") |
|
|
|
|
|
|
|
|
|
// Verify second Ingress cleanup
|
|
|
|
|
cm := &corev1.ConfigMap{} |
|
|
|
|
if err := fc.Get(context.Background(), types.NamespacedName{ |
|
|
|
|
Name: "test-pg-ingress-config", |
|
|
|
|
@ -137,46 +142,16 @@ func TestIngressPGReconciler(t *testing.T) { |
|
|
|
|
t.Fatalf("unmarshaling serve config: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verify first Ingress is still configured
|
|
|
|
|
if cfg.Services["svc:my-svc"] == nil { |
|
|
|
|
t.Error("expected serve config to contain VIPService configuration") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verify VIPService uses default tags
|
|
|
|
|
vipSvc, err := ft.getVIPService(context.Background(), "svc:my-svc") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("getting VIPService: %v", err) |
|
|
|
|
} |
|
|
|
|
if vipSvc == nil { |
|
|
|
|
t.Fatal("VIPService not created") |
|
|
|
|
} |
|
|
|
|
wantTags := []string{"tag:k8s"} // default tags
|
|
|
|
|
if !slices.Equal(vipSvc.Tags, wantTags) { |
|
|
|
|
t.Errorf("incorrect VIPService tags: got %v, want %v", vipSvc.Tags, wantTags) |
|
|
|
|
t.Error("first Ingress service config was incorrectly removed") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Test 2: Custom tags
|
|
|
|
|
mustUpdate(t, fc, "default", "test-ingress", func(ing *networkingv1.Ingress) { |
|
|
|
|
ing.Annotations["tailscale.com/tags"] = "tag:custom,tag:test" |
|
|
|
|
}) |
|
|
|
|
expectReconciled(t, ingPGR, "default", "test-ingress") |
|
|
|
|
|
|
|
|
|
// Verify VIPService uses custom tags
|
|
|
|
|
vipSvc, err = ft.getVIPService(context.Background(), "svc:my-svc") |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("getting VIPService: %v", err) |
|
|
|
|
} |
|
|
|
|
if vipSvc == nil { |
|
|
|
|
t.Fatal("VIPService not created") |
|
|
|
|
} |
|
|
|
|
wantTags = []string{"tag:custom", "tag:test"} // custom tags only
|
|
|
|
|
gotTags := slices.Clone(vipSvc.Tags) |
|
|
|
|
slices.Sort(gotTags) |
|
|
|
|
slices.Sort(wantTags) |
|
|
|
|
if !slices.Equal(gotTags, wantTags) { |
|
|
|
|
t.Errorf("incorrect VIPService tags: got %v, want %v", gotTags, wantTags) |
|
|
|
|
// Verify second Ingress was cleaned up
|
|
|
|
|
if cfg.Services["svc:my-other-svc"] != nil { |
|
|
|
|
t.Error("second Ingress service config was not cleaned up") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Delete the Ingress and verify cleanup
|
|
|
|
|
// Delete the first Ingress and verify cleanup
|
|
|
|
|
if err := fc.Delete(context.Background(), ing); err != nil { |
|
|
|
|
t.Fatalf("deleting Ingress: %v", err) |
|
|
|
|
} |
|
|
|
|
@ -335,3 +310,233 @@ func TestValidateIngress(t *testing.T) { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestIngressPGReconciler_HTTPEndpoint(t *testing.T) { |
|
|
|
|
ingPGR, fc, ft := setupIngressTest(t) |
|
|
|
|
|
|
|
|
|
// Create test Ingress with HTTP endpoint enabled
|
|
|
|
|
ing := &networkingv1.Ingress{ |
|
|
|
|
TypeMeta: metav1.TypeMeta{Kind: "Ingress", APIVersion: "networking.k8s.io/v1"}, |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{ |
|
|
|
|
Name: "test-ingress", |
|
|
|
|
Namespace: "default", |
|
|
|
|
UID: types.UID("1234-UID"), |
|
|
|
|
Annotations: map[string]string{ |
|
|
|
|
"tailscale.com/proxy-group": "test-pg", |
|
|
|
|
"tailscale.com/http-endpoint": "enabled", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
Spec: networkingv1.IngressSpec{ |
|
|
|
|
IngressClassName: ptr.To("tailscale"), |
|
|
|
|
DefaultBackend: &networkingv1.IngressBackend{ |
|
|
|
|
Service: &networkingv1.IngressServiceBackend{ |
|
|
|
|
Name: "test", |
|
|
|
|
Port: networkingv1.ServiceBackendPort{ |
|
|
|
|
Number: 8080, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
TLS: []networkingv1.IngressTLS{ |
|
|
|
|
{Hosts: []string{"my-svc"}}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
if err := fc.Create(context.Background(), ing); err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Verify initial reconciliation with HTTP enabled
|
|
|
|
|
expectReconciled(t, ingPGR, "default", "test-ingress") |
|
|
|
|
verifyVIPService(t, ft, "svc:my-svc", []string{"80", "443"}) |
|
|
|
|
verifyServeConfig(t, fc, "svc:my-svc", true) |
|
|
|
|
|
|
|
|
|
// Verify Ingress status
|
|
|
|
|
ing = &networkingv1.Ingress{} |
|
|
|
|
if err := fc.Get(context.Background(), types.NamespacedName{ |
|
|
|
|
Name: "test-ingress", |
|
|
|
|
Namespace: "default", |
|
|
|
|
}, ing); err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
wantStatus := []networkingv1.IngressPortStatus{ |
|
|
|
|
{Port: 443, Protocol: "TCP"}, |
|
|
|
|
{Port: 80, Protocol: "TCP"}, |
|
|
|
|
} |
|
|
|
|
if !reflect.DeepEqual(ing.Status.LoadBalancer.Ingress[0].Ports, wantStatus) { |
|
|
|
|
t.Errorf("incorrect status ports: got %v, want %v", |
|
|
|
|
ing.Status.LoadBalancer.Ingress[0].Ports, wantStatus) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Remove HTTP endpoint annotation
|
|
|
|
|
mustUpdate(t, fc, "default", "test-ingress", func(ing *networkingv1.Ingress) { |
|
|
|
|
delete(ing.Annotations, "tailscale.com/http-endpoint") |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// Verify reconciliation after removing HTTP
|
|
|
|
|
expectReconciled(t, ingPGR, "default", "test-ingress") |
|
|
|
|
verifyVIPService(t, ft, "svc:my-svc", []string{"443"}) |
|
|
|
|
verifyServeConfig(t, fc, "svc:my-svc", false) |
|
|
|
|
|
|
|
|
|
// Verify Ingress status
|
|
|
|
|
ing = &networkingv1.Ingress{} |
|
|
|
|
if err := fc.Get(context.Background(), types.NamespacedName{ |
|
|
|
|
Name: "test-ingress", |
|
|
|
|
Namespace: "default", |
|
|
|
|
}, ing); err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
wantStatus = []networkingv1.IngressPortStatus{ |
|
|
|
|
{Port: 443, Protocol: "TCP"}, |
|
|
|
|
} |
|
|
|
|
if !reflect.DeepEqual(ing.Status.LoadBalancer.Ingress[0].Ports, wantStatus) { |
|
|
|
|
t.Errorf("incorrect status ports: got %v, want %v", |
|
|
|
|
ing.Status.LoadBalancer.Ingress[0].Ports, wantStatus) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func verifyVIPService(t *testing.T, ft *fakeTSClient, serviceName string, wantPorts []string) { |
|
|
|
|
t.Helper() |
|
|
|
|
vipSvc, err := ft.getVIPService(context.Background(), tailcfg.ServiceName(serviceName)) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatalf("getting VIPService %q: %v", serviceName, err) |
|
|
|
|
} |
|
|
|
|
if vipSvc == nil { |
|
|
|
|
t.Fatalf("VIPService %q not created", serviceName) |
|
|
|
|
} |
|
|
|
|
gotPorts := slices.Clone(vipSvc.Ports) |
|
|
|
|
slices.Sort(gotPorts) |
|
|
|
|
slices.Sort(wantPorts) |
|
|
|
|
if !slices.Equal(gotPorts, wantPorts) { |
|
|
|
|
t.Errorf("incorrect ports for VIPService %q: got %v, want %v", serviceName, gotPorts, wantPorts) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func verifyServeConfig(t *testing.T, fc client.Client, serviceName string, wantHTTP bool) { |
|
|
|
|
t.Helper() |
|
|
|
|
|
|
|
|
|
cm := &corev1.ConfigMap{} |
|
|
|
|
if err := fc.Get(context.Background(), types.NamespacedName{ |
|
|
|
|
Name: "test-pg-ingress-config", |
|
|
|
|
Namespace: "operator-ns", |
|
|
|
|
}, cm); err != nil { |
|
|
|
|
t.Fatalf("getting ConfigMap: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cfg := &ipn.ServeConfig{} |
|
|
|
|
if err := json.Unmarshal(cm.BinaryData["serve-config.json"], cfg); err != nil { |
|
|
|
|
t.Fatalf("unmarshaling serve config: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
t.Logf("Looking for service %q in config: %+v", serviceName, cfg) |
|
|
|
|
|
|
|
|
|
svc := cfg.Services[tailcfg.ServiceName(serviceName)] |
|
|
|
|
if svc == nil { |
|
|
|
|
t.Fatalf("service %q not found in serve config, services: %+v", serviceName, maps.Keys(cfg.Services)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
wantHandlers := 1 |
|
|
|
|
if wantHTTP { |
|
|
|
|
wantHandlers = 2 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check TCP handlers
|
|
|
|
|
if len(svc.TCP) != wantHandlers { |
|
|
|
|
t.Errorf("incorrect number of TCP handlers for service %q: got %d, want %d", serviceName, len(svc.TCP), wantHandlers) |
|
|
|
|
} |
|
|
|
|
if wantHTTP { |
|
|
|
|
if h, ok := svc.TCP[uint16(80)]; !ok { |
|
|
|
|
t.Errorf("HTTP (port 80) handler not found for service %q", serviceName) |
|
|
|
|
} else if !h.HTTP { |
|
|
|
|
t.Errorf("HTTP not enabled for port 80 handler for service %q", serviceName) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if h, ok := svc.TCP[uint16(443)]; !ok { |
|
|
|
|
t.Errorf("HTTPS (port 443) handler not found for service %q", serviceName) |
|
|
|
|
} else if !h.HTTPS { |
|
|
|
|
t.Errorf("HTTPS not enabled for port 443 handler for service %q", serviceName) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check Web handlers
|
|
|
|
|
if len(svc.Web) != wantHandlers { |
|
|
|
|
t.Errorf("incorrect number of Web handlers for service %q: got %d, want %d", serviceName, len(svc.Web), wantHandlers) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func setupIngressTest(t *testing.T) (*IngressPGReconciler, client.Client, *fakeTSClient) { |
|
|
|
|
t.Helper() |
|
|
|
|
|
|
|
|
|
tsIngressClass := &networkingv1.IngressClass{ |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{Name: "tailscale"}, |
|
|
|
|
Spec: networkingv1.IngressClassSpec{Controller: "tailscale.com/ts-ingress"}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Pre-create the ProxyGroup
|
|
|
|
|
pg := &tsapi.ProxyGroup{ |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{ |
|
|
|
|
Name: "test-pg", |
|
|
|
|
Generation: 1, |
|
|
|
|
}, |
|
|
|
|
Spec: tsapi.ProxyGroupSpec{ |
|
|
|
|
Type: tsapi.ProxyGroupTypeIngress, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Pre-create the ConfigMap for the ProxyGroup
|
|
|
|
|
pgConfigMap := &corev1.ConfigMap{ |
|
|
|
|
ObjectMeta: metav1.ObjectMeta{ |
|
|
|
|
Name: "test-pg-ingress-config", |
|
|
|
|
Namespace: "operator-ns", |
|
|
|
|
}, |
|
|
|
|
BinaryData: map[string][]byte{ |
|
|
|
|
"serve-config.json": []byte(`{"Services":{}}`), |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fc := fake.NewClientBuilder(). |
|
|
|
|
WithScheme(tsapi.GlobalScheme). |
|
|
|
|
WithObjects(pg, pgConfigMap, tsIngressClass). |
|
|
|
|
WithStatusSubresource(pg). |
|
|
|
|
Build() |
|
|
|
|
|
|
|
|
|
// Set ProxyGroup status to ready
|
|
|
|
|
pg.Status.Conditions = []metav1.Condition{ |
|
|
|
|
{ |
|
|
|
|
Type: string(tsapi.ProxyGroupReady), |
|
|
|
|
Status: metav1.ConditionTrue, |
|
|
|
|
ObservedGeneration: 1, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
if err := fc.Status().Update(context.Background(), pg); err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ft := &fakeTSClient{} |
|
|
|
|
fakeTsnetServer := &fakeTSNetServer{certDomains: []string{"foo.com"}} |
|
|
|
|
zl, err := zap.NewDevelopment() |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
lc := &fakeLocalClient{ |
|
|
|
|
status: &ipnstate.Status{ |
|
|
|
|
CurrentTailnet: &ipnstate.TailnetStatus{ |
|
|
|
|
MagicDNSSuffix: "ts.net", |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ingPGR := &IngressPGReconciler{ |
|
|
|
|
Client: fc, |
|
|
|
|
tsClient: ft, |
|
|
|
|
tsnetServer: fakeTsnetServer, |
|
|
|
|
defaultTags: []string{"tag:k8s"}, |
|
|
|
|
tsNamespace: "operator-ns", |
|
|
|
|
logger: zl.Sugar(), |
|
|
|
|
recorder: record.NewFakeRecorder(10), |
|
|
|
|
lc: lc, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ingPGR, fc, ft |
|
|
|
|
} |
|
|
|
|
|