mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-28 23:04:10 +00:00
k8s-operator: adding final tests
Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
This commit is contained in:
parent
ddbcbbbe52
commit
f2758c49a7
@ -1802,6 +1802,54 @@ func Test_metricsResourceCreation(t *testing.T) {
|
||||
// object). We cannot test this using the fake client.
|
||||
}
|
||||
|
||||
func TestIgnorePGService(t *testing.T) {
|
||||
// NOTE: creating proxygroup stuff just to be sure that it's all ignored
|
||||
_, _, fc, _ := setupServiceTest(t)
|
||||
|
||||
ft := &fakeTSClient{}
|
||||
zl, err := zap.NewDevelopment()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clock := tstest.NewClock(tstest.ClockOpts{})
|
||||
sr := &ServiceReconciler{
|
||||
Client: fc,
|
||||
ssr: &tailscaleSTSReconciler{
|
||||
Client: fc,
|
||||
tsClient: ft,
|
||||
defaultTags: []string{"tag:k8s"},
|
||||
operatorNamespace: "operator-ns",
|
||||
proxyImage: "tailscale/tailscale",
|
||||
},
|
||||
logger: zl.Sugar(),
|
||||
clock: clock,
|
||||
}
|
||||
|
||||
// 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"),
|
||||
Annotations: map[string]string{
|
||||
"tailscale.com/proxygroup": "test-pg",
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
ClusterIP: "10.20.30.40",
|
||||
Type: corev1.ServiceTypeClusterIP,
|
||||
},
|
||||
})
|
||||
|
||||
expectReconciled(t, sr, "default", "test")
|
||||
|
||||
findNoGenName(t, fc, "default", "test", "svc")
|
||||
}
|
||||
|
||||
func toFQDN(t *testing.T, s string) dnsname.FQDN {
|
||||
t.Helper()
|
||||
fqdn, err := dnsname.ToFQDN(s)
|
||||
|
@ -145,7 +145,7 @@ func setupServiceTest(t *testing.T) (*HAServiceReconciler, *corev1.Secret, clien
|
||||
Labels: pgSecretLabels("test-pg", "config"),
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
tsoperator.TailscaledConfigFileName(106): []byte("{}"),
|
||||
tsoperator.TailscaledConfigFileName(106): []byte(`{"Version":""}`),
|
||||
},
|
||||
}
|
||||
|
||||
@ -224,6 +224,110 @@ func setupServiceTest(t *testing.T) (*HAServiceReconciler, *corev1.Secret, clien
|
||||
return svcPGR, pgStateSecret, fc, ft
|
||||
}
|
||||
|
||||
func TestServicePGReconciler_MultiCluster(t *testing.T) {
|
||||
var ft *fakeTSClient
|
||||
var lc localClient
|
||||
for i := 0; i <= 10; i++ {
|
||||
pgr, stateSecret, fc, fti := setupServiceTest(t)
|
||||
if i == 0 {
|
||||
ft = fti
|
||||
lc = pgr.lc
|
||||
} else {
|
||||
pgr.tsClient = ft
|
||||
pgr.lc = lc
|
||||
}
|
||||
|
||||
svc, _ := setupTestService(t, "test-multi-cluster", "", "4.3.2.1", fc, stateSecret)
|
||||
expectReconciled(t, pgr, "default", svc.Name)
|
||||
|
||||
vipSvcs, err := ft.ListVIPServices(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("getting VIPService: %v", err)
|
||||
}
|
||||
|
||||
if len(vipSvcs) != 1 {
|
||||
t.Fatalf("unexpected number of VIPServices (%d)", len(vipSvcs))
|
||||
}
|
||||
|
||||
for name := range vipSvcs {
|
||||
t.Logf("found vip service with name %q", name.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnoreRegularService(t *testing.T) {
|
||||
pgr, _, fc, ft := setupServiceTest(t)
|
||||
|
||||
svc := &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"),
|
||||
Annotations: map[string]string{
|
||||
"tailscale.com/expose": "true",
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
ClusterIP: "10.20.30.40",
|
||||
Type: corev1.ServiceTypeClusterIP,
|
||||
},
|
||||
}
|
||||
|
||||
// hostname := svc.Namespace + svc.Name
|
||||
|
||||
mustCreate(t, fc, svc)
|
||||
expectReconciled(t, pgr, "default", "test")
|
||||
|
||||
verifyTailscaledConfig(t, fc, nil)
|
||||
|
||||
vipSvcs, err := ft.ListVIPServices(context.Background())
|
||||
if err == nil {
|
||||
t.Fatalf("failed to list VIPServices")
|
||||
}
|
||||
|
||||
if len(vipSvcs) > 0 {
|
||||
t.Fatal("unexpected vip services found")
|
||||
}
|
||||
}
|
||||
|
||||
func removeEl(s []string, value string) []string {
|
||||
result := s[:0]
|
||||
for _, v := range s {
|
||||
if v != value {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func updateIngressConfigSecret(t *testing.T, fc client.Client, stateSecret *corev1.Secret, serviceName string, clusterIP string) {
|
||||
ingressConfig := ingressservices.Configs{
|
||||
fmt.Sprintf("svc:%s", serviceName): ingressservices.Config{
|
||||
IPv4Mapping: &ingressservices.Mapping{
|
||||
VIPServiceIP: netip.MustParseAddr(vipTestIP),
|
||||
ClusterIP: netip.MustParseAddr(clusterIP),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ingressStatus := ingressservices.Status{
|
||||
Configs: ingressConfig,
|
||||
PodIPv4: "4.3.2.1",
|
||||
}
|
||||
|
||||
icJson, err := json.Marshal(ingressStatus)
|
||||
if err != nil {
|
||||
t.Fatal("failed to json marshal ingress config")
|
||||
}
|
||||
|
||||
mustUpdate(t, fc, stateSecret.Namespace, stateSecret.Name, func(sec *corev1.Secret) {
|
||||
mak.Set(&sec.Data, ingressservices.IngressConfigKey, icJson)
|
||||
})
|
||||
}
|
||||
|
||||
func setupTestService(t *testing.T, svcName string, hostname string, clusterIP string, fc client.Client, stateSecret *corev1.Secret) (svc *corev1.Service, eps *discoveryv1.EndpointSlice) {
|
||||
uid := rand.IntN(100)
|
||||
svc = &corev1.Service{
|
||||
@ -271,124 +375,3 @@ func setupTestService(t *testing.T, svcName string, hostname string, clusterIP s
|
||||
|
||||
return svc, eps
|
||||
}
|
||||
|
||||
// func TestServicePGReconciler_MultiCluster(t *testing.T) {
|
||||
// ingPGR, fc, ft := setupIngressTest(t)
|
||||
// ingPGR.operatorID = "operator-1"
|
||||
//
|
||||
// // Create initial Ingress
|
||||
// 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",
|
||||
// },
|
||||
// },
|
||||
// Spec: networkingv1.IngressSpec{
|
||||
// IngressClassName: ptr.To("tailscale"),
|
||||
// TLS: []networkingv1.IngressTLS{
|
||||
// {Hosts: []string{"my-svc"}},
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// mustCreate(t, fc, ing)
|
||||
//
|
||||
// // Simulate existing VIPService from another cluster
|
||||
// existingVIPSvc := &tailscale.VIPService{
|
||||
// Name: "svc:my-svc",
|
||||
// Annotations: map[string]string{
|
||||
// ownerAnnotation: `{"ownerrefs":[{"operatorID":"operator-2"}]}`,
|
||||
// },
|
||||
// }
|
||||
// ft.vipServices = map[tailcfg.ServiceName]*tailscale.VIPService{
|
||||
// "svc:my-svc": existingVIPSvc,
|
||||
// }
|
||||
//
|
||||
// // Verify reconciliation adds our operator reference
|
||||
// expectReconciled(t, ingPGR, "default", "test-ingress")
|
||||
//
|
||||
// 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 found")
|
||||
// }
|
||||
//
|
||||
// o, err := parseOwnerAnnotation(vipSvc)
|
||||
// if err != nil {
|
||||
// t.Fatalf("parsing owner annotation: %v", err)
|
||||
// }
|
||||
//
|
||||
// wantOwnerRefs := []OwnerRef{
|
||||
// {OperatorID: "operator-2"},
|
||||
// {OperatorID: "operator-1"},
|
||||
// }
|
||||
// if !reflect.DeepEqual(o.OwnerRefs, wantOwnerRefs) {
|
||||
// t.Errorf("incorrect owner refs\ngot: %+v\nwant: %+v", o.OwnerRefs, wantOwnerRefs)
|
||||
// }
|
||||
//
|
||||
// // Delete the Ingress and verify VIPService still exists with one owner ref
|
||||
// if err := fc.Delete(context.Background(), ing); err != nil {
|
||||
// t.Fatalf("deleting Ingress: %v", err)
|
||||
// }
|
||||
// expectRequeue(t, ingPGR, "default", "test-ingress")
|
||||
//
|
||||
// vipSvc, err = ft.GetVIPService(context.Background(), "svc:my-svc")
|
||||
// if err != nil {
|
||||
// t.Fatalf("getting VIPService after deletion: %v", err)
|
||||
// }
|
||||
// if vipSvc == nil {
|
||||
// t.Fatal("VIPService was incorrectly deleted")
|
||||
// }
|
||||
//
|
||||
// o, err = parseOwnerAnnotation(vipSvc)
|
||||
// if err != nil {
|
||||
// t.Fatalf("parsing owner annotation: %v", err)
|
||||
// }
|
||||
//
|
||||
// wantOwnerRefs = []OwnerRef{
|
||||
// {OperatorID: "operator-2"},
|
||||
// }
|
||||
// if !reflect.DeepEqual(o.OwnerRefs, wantOwnerRefs) {
|
||||
// t.Errorf("incorrect owner refs after deletion\ngot: %+v\nwant: %+v", o.OwnerRefs, wantOwnerRefs)
|
||||
// }
|
||||
// }
|
||||
|
||||
func removeEl(s []string, value string) []string {
|
||||
result := s[:0]
|
||||
for _, v := range s {
|
||||
if v != value {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func updateIngressConfigSecret(t *testing.T, fc client.Client, stateSecret *corev1.Secret, serviceName string, clusterIP string) {
|
||||
ingressConfig := ingressservices.Configs{
|
||||
fmt.Sprintf("svc:%s", serviceName): ingressservices.Config{
|
||||
IPv4Mapping: &ingressservices.Mapping{
|
||||
VIPServiceIP: netip.MustParseAddr(vipTestIP),
|
||||
ClusterIP: netip.MustParseAddr(clusterIP),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ingressStatus := ingressservices.Status{
|
||||
Configs: ingressConfig,
|
||||
PodIPv4: "4.3.2.1",
|
||||
}
|
||||
|
||||
icJson, err := json.Marshal(ingressStatus)
|
||||
if err != nil {
|
||||
t.Fatal("failed to json marshal ingress config")
|
||||
}
|
||||
|
||||
mustUpdate(t, fc, stateSecret.Namespace, stateSecret.Name, func(sec *corev1.Secret) {
|
||||
mak.Set(&sec.Data, ingressservices.IngressConfigKey, icJson)
|
||||
})
|
||||
}
|
||||
|
@ -565,6 +565,23 @@ func expectedSecret(t *testing.T, cl client.Client, opts configOpts) *corev1.Sec
|
||||
return s
|
||||
}
|
||||
|
||||
func findNoGenName(t *testing.T, client client.Client, ns, name, typ string) {
|
||||
t.Helper()
|
||||
labels := map[string]string{
|
||||
kubetypes.LabelManaged: "true",
|
||||
LabelParentName: name,
|
||||
LabelParentNamespace: ns,
|
||||
LabelParentType: typ,
|
||||
}
|
||||
s, err := getSingleObject[corev1.Secret](context.Background(), client, "operator-ns", labels)
|
||||
if err != nil {
|
||||
t.Fatalf("finding secrets for %q: %v", name, err)
|
||||
}
|
||||
if s != nil {
|
||||
t.Fatalf("found unexpected secret with name %q", s.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func findGenName(t *testing.T, client client.Client, ns, name, typ string) (full, noSuffix string) {
|
||||
t.Helper()
|
||||
labels := map[string]string{
|
||||
@ -893,6 +910,15 @@ func (c *fakeTSClient) GetVIPService(ctx context.Context, name tailcfg.ServiceNa
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func (c *fakeTSClient) ListVIPServices(ctx context.Context) (map[tailcfg.ServiceName]*tailscale.VIPService, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.vipServices == nil {
|
||||
return nil, &tailscale.ErrResponse{Status: http.StatusNotFound}
|
||||
}
|
||||
return c.vipServices, nil
|
||||
}
|
||||
|
||||
func (c *fakeTSClient) CreateOrUpdateVIPService(ctx context.Context, svc *tailscale.VIPService) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
Loading…
x
Reference in New Issue
Block a user