1 package watcher
2
3 import (
4 "testing"
5 "time"
6
7 "github.com/linkerd/linkerd2/controller/k8s"
8 )
9
10 func TestClusterStoreHandlers(t *testing.T) {
11 for _, tt := range []struct {
12 name string
13 k8sConfigs []string
14 enableEndpointSlices bool
15 expectedClusters map[string]clusterConfig
16 deleteClusters map[string]struct{}
17 }{
18 {
19 name: "add and remove remote watcher when Secret is valid",
20 k8sConfigs: []string{
21 validRemoteSecret,
22 },
23 enableEndpointSlices: true,
24 expectedClusters: map[string]clusterConfig{
25 "remote": {
26 TrustDomain: "identity.org",
27 ClusterDomain: "cluster.local",
28 },
29 },
30 deleteClusters: map[string]struct{}{
31 "remote": {},
32 },
33 },
34 {
35 name: "add and remove more than one watcher when Secrets are valid",
36 k8sConfigs: []string{
37 validRemoteSecret,
38 validTargetSecret,
39 },
40 enableEndpointSlices: false,
41 expectedClusters: map[string]clusterConfig{
42 "remote": {
43 TrustDomain: "identity.org",
44 ClusterDomain: "cluster.local",
45 },
46 "target": {
47 TrustDomain: "cluster.target.local",
48 ClusterDomain: "cluster.target.local",
49 },
50 },
51 deleteClusters: map[string]struct{}{
52 "remote": {},
53 },
54 },
55 {
56 name: "malformed secrets shouldn't result in created watchers",
57 k8sConfigs: []string{
58 validRemoteSecret,
59 noClusterSecret,
60 noDomainSecret,
61 noIdentitySecret,
62 invalidTypeSecret,
63 },
64 enableEndpointSlices: true,
65 expectedClusters: map[string]clusterConfig{
66 "remote": {
67 TrustDomain: "identity.org",
68 ClusterDomain: "cluster.local",
69 },
70 },
71 deleteClusters: map[string]struct{}{
72 "remote": {},
73 },
74 },
75 } {
76 tt := tt
77 t.Run(tt.name, func(t *testing.T) {
78 k8sAPI, err := k8s.NewFakeAPI(tt.k8sConfigs...)
79 if err != nil {
80 t.Fatalf("NewFakeAPI returned an error: %s", err)
81 }
82
83 cs, err := NewClusterStoreWithDecoder(k8sAPI.Client, "linkerd", tt.enableEndpointSlices, CreateMockDecoder())
84 if err != nil {
85 t.Fatalf("Unexpected error when starting watcher cache: %s", err)
86 }
87
88 cs.Sync(nil)
89
90
91 time.Sleep(50 * time.Millisecond)
92
93 cs.RLock()
94 actualLen := len(cs.store)
95 cs.RUnlock()
96
97 if actualLen != len(tt.expectedClusters) {
98 t.Fatalf("Unexpected error: expected to see %d cache entries, got: %d", len(tt.expectedClusters), actualLen)
99 }
100
101 for k, expected := range tt.expectedClusters {
102 _, cfg, found := cs.Get(k)
103 if !found {
104 t.Fatalf("Unexpected error: cluster %s is missing from the cache", k)
105 }
106
107 if cfg.ClusterDomain != expected.ClusterDomain {
108 t.Fatalf("Unexpected error: expected cluster domain %s for cluster '%s', got: %s", expected.ClusterDomain, k, cfg.ClusterDomain)
109 }
110
111 if cfg.TrustDomain != expected.TrustDomain {
112 t.Fatalf("Unexpected error: expected cluster domain %s for cluster '%s', got: %s", expected.TrustDomain, k, cfg.TrustDomain)
113 }
114 }
115
116
117 for k := range tt.deleteClusters {
118 watcher, _, found := cs.Get(k)
119 if !found {
120 t.Fatalf("Unexpected error: watcher %s should exist in the cache", k)
121 }
122
123
124 cs.removeCluster(k)
125
126 time.Sleep(50 * time.Millisecond)
127 var hasStopped bool
128 if tt.enableEndpointSlices {
129 hasStopped = watcher.k8sAPI.ES().Informer().IsStopped()
130 } else {
131 hasStopped = watcher.k8sAPI.Endpoint().Informer().IsStopped()
132 }
133 if !hasStopped {
134 t.Fatalf("Unexpected error: informers for watcher %s should be stopped", k)
135 }
136
137 if _, _, found := cs.Get(k); found {
138 t.Fatalf("Unexpected error: watcher %s should have been removed from the cache", k)
139 }
140
141 }
142
143 cs.UnregisterGauges()
144 })
145 }
146 }
147
148 var validRemoteSecret = `
149 apiVersion: v1
150 kind: Secret
151 type: mirror.linkerd.io/remote-kubeconfig
152 metadata:
153 namespace: linkerd
154 name: remote-cluster-credentials
155 labels:
156 multicluster.linkerd.io/cluster-name: remote
157 annotations:
158 multicluster.linkerd.io/trust-domain: identity.org
159 multicluster.linkerd.io/cluster-domain: cluster.local
160 data:
161 kubeconfig: dmVyeSB0b3Agc2VjcmV0IGluZm9ybWF0aW9uIGhlcmUK
162 `
163
164 var validTargetSecret = `
165 apiversion: v1
166 kind: Secret
167 type: mirror.linkerd.io/remote-kubeconfig
168 metadata:
169 namespace: linkerd
170 name: target-cluster-credentials
171 labels:
172 multicluster.linkerd.io/cluster-name: target
173 annotations:
174 multicluster.linkerd.io/trust-domain: cluster.target.local
175 multicluster.linkerd.io/cluster-domain: cluster.target.local
176 data:
177 kubeconfig: dmvyesb0b3agc2vjcmv0igluzm9ybwf0aw9uighlcmuk
178 `
179
180 var noDomainSecret = `
181 apiVersion: v1
182 kind: Secret
183 type: mirror.linkerd.io/remote-kubeconfig
184 metadata:
185 namespace: linkerd
186 name: target-1-cluster-credentials
187 labels:
188 multicluster.linkerd.io/cluster-name: target-1
189 annotations:
190 multicluster.linkerd.io/trust-domain: cluster.local
191 data:
192 kubeconfig: dmVyeSB0b3Agc2VjcmV0IGluZm9ybWF0aW9uIGhlcmUK
193 `
194
195 var noClusterSecret = `
196 apiVersion: v1
197 kind: Secret
198 type: mirror.linkerd.io/remote-kubeconfig
199 metadata:
200 namespace: linkerd
201 name: target-2-cluster-credentials
202 annotations:
203 multicluster.linkerd.io/trust-domain: cluster.local
204 multicluster.linkerd.io/cluster-domain: cluster.local
205 data:
206 kubeconfig: dmVyeSB0b3Agc2VjcmV0IGluZm9ybWF0aW9uIGhlcmUK
207 `
208
209 var noIdentitySecret = `
210 apiversion: v1
211 kind: Secret
212 type: mirror.linkerd.io/remote-kubeconfig
213 metadata:
214 namespace: linkerd
215 name: target-3-cluster-credentials
216 labels:
217 multicluster.linkerd.io/cluster-name: target-3
218 annotations:
219 multicluster.linkerd.io/cluster-domain: cluster.local
220 data:
221 kubeconfig: dmvyesb0b3agc2vjcmv0igluzm9ybwf0aw9uighlcmuk
222 `
223 var invalidTypeSecret = `
224 apiversion: v1
225 kind: Secret
226 type: kubernetes.io/tls
227 metadata:
228 namespace: linkerd
229 name: target-cluster-credentials
230 labels:
231 multicluster.linkerd.io/cluster-name: target
232 annotations:
233 multicluster.linkerd.io/trust-domain: cluster.local
234 multicluster.linkerd.io/cluster-domain: cluster.local
235 data:
236 kubeconfig: dmvyesb0b3agc2vjcmv0igluzm9ybwf0aw9uighlcmuk
237 `
238
View as plain text