@@ -24,17 +24,21 @@ import (
24
24
"time"
25
25
26
26
"k8s.io/api/core/v1"
27
+ apiequality "k8s.io/apimachinery/pkg/api/equality"
27
28
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
30
"k8s.io/apimachinery/pkg/runtime"
30
31
"k8s.io/apimachinery/pkg/util/wait"
31
32
"k8s.io/apimachinery/pkg/watch"
32
33
34
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
33
35
clientset "k8s.io/client-go/kubernetes"
34
36
"k8s.io/client-go/kubernetes/fake"
35
37
core "k8s.io/client-go/testing"
38
+ featuregatetesting "k8s.io/component-base/featuregate/testing"
36
39
37
40
corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
41
+ "k8s.io/kubernetes/pkg/features"
38
42
39
43
"github.com/stretchr/testify/assert"
40
44
)
@@ -51,11 +55,19 @@ func watchSecret(fakeClient clientset.Interface) watchObjectFunc {
51
55
}
52
56
}
53
57
58
+ func isSecretImmutable (object runtime.Object ) bool {
59
+ if secret , ok := object .(* v1.Secret ); ok {
60
+ return secret .Immutable != nil && * secret .Immutable
61
+ }
62
+ return false
63
+ }
64
+
54
65
func newSecretCache (fakeClient clientset.Interface ) * objectCache {
55
66
return & objectCache {
56
67
listObject : listSecret (fakeClient ),
57
68
watchObject : watchSecret (fakeClient ),
58
69
newObject : func () runtime.Object { return & v1.Secret {} },
70
+ isImmutable : isSecretImmutable ,
59
71
groupResource : corev1 .Resource ("secret" ),
60
72
items : make (map [objectKey ]* objectCacheItem ),
61
73
}
@@ -183,3 +195,138 @@ func TestSecretCacheMultipleRegistrations(t *testing.T) {
183
195
actions = fakeClient .Actions ()
184
196
assert .Equal (t , 2 , len (actions ), "unexpected actions: %#v" , actions )
185
197
}
198
+
199
+ func TestImmutableSecretStopsTheReflector (t * testing.T ) {
200
+ defer featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .ImmutableEphemeralVolumes , true )()
201
+
202
+ secret := func (rv string , immutable bool ) * v1.Secret {
203
+ result := & v1.Secret {
204
+ ObjectMeta : metav1.ObjectMeta {
205
+ Name : "name" ,
206
+ Namespace : "ns" ,
207
+ ResourceVersion : rv ,
208
+ },
209
+ }
210
+ if immutable {
211
+ trueVal := true
212
+ result .Immutable = & trueVal
213
+ }
214
+ return result
215
+ }
216
+
217
+ tests := []struct {
218
+ desc string
219
+ initial * v1.Secret
220
+ eventual * v1.Secret
221
+ }{
222
+ {
223
+ desc : "secret doesn't exist, created as mutable" ,
224
+ initial : nil ,
225
+ eventual : secret ("200" , false ),
226
+ },
227
+ {
228
+ desc : "secret doesn't exist, created as immutable" ,
229
+ initial : nil ,
230
+ eventual : secret ("200" , true ),
231
+ },
232
+ {
233
+ desc : "mutable secret modified to mutable" ,
234
+ initial : secret ("100" , false ),
235
+ eventual : secret ("200" , false ),
236
+ },
237
+ {
238
+ desc : "mutable secret modified to immutable" ,
239
+ initial : secret ("100" , false ),
240
+ eventual : secret ("200" , true ),
241
+ },
242
+ {
243
+ desc : "immutable secret" ,
244
+ initial : secret ("100" , true ),
245
+ eventual : nil ,
246
+ },
247
+ }
248
+
249
+ for _ , tc := range tests {
250
+ t .Run (tc .desc , func (t * testing.T ) {
251
+ fakeClient := & fake.Clientset {}
252
+ listReactor := func (a core.Action ) (bool , runtime.Object , error ) {
253
+ result := & v1.SecretList {
254
+ ListMeta : metav1.ListMeta {
255
+ ResourceVersion : "100" ,
256
+ },
257
+ }
258
+ if tc .initial != nil {
259
+ result .Items = []v1.Secret {* tc .initial }
260
+ }
261
+ return true , result , nil
262
+ }
263
+ fakeClient .AddReactor ("list" , "secrets" , listReactor )
264
+ fakeWatch := watch .NewFake ()
265
+ fakeClient .AddWatchReactor ("secrets" , core .DefaultWatchReactor (fakeWatch , nil ))
266
+
267
+ store := newSecretCache (fakeClient )
268
+
269
+ key := objectKey {namespace : "ns" , name : "name" }
270
+ itemExists := func () (bool , error ) {
271
+ store .lock .Lock ()
272
+ defer store .lock .Unlock ()
273
+ _ , ok := store .items [key ]
274
+ return ok , nil
275
+ }
276
+ reflectorRunning := func () bool {
277
+ store .lock .Lock ()
278
+ defer store .lock .Unlock ()
279
+ item := store .items [key ]
280
+
281
+ item .lock .Lock ()
282
+ defer item .lock .Unlock ()
283
+ select {
284
+ case <- item .stopCh :
285
+ return false
286
+ default :
287
+ return true
288
+ }
289
+ }
290
+
291
+ // AddReference should start reflector.
292
+ store .AddReference ("ns" , "name" )
293
+ if err := wait .Poll (10 * time .Millisecond , time .Second , itemExists ); err != nil {
294
+ t .Errorf ("item wasn't added to cache" )
295
+ }
296
+
297
+ obj , err := store .Get ("ns" , "name" )
298
+ if tc .initial != nil {
299
+ assert .True (t , apiequality .Semantic .DeepEqual (tc .initial , obj ))
300
+ } else {
301
+ assert .True (t , apierrors .IsNotFound (err ))
302
+ }
303
+
304
+ // Reflector should already be stopped for immutable secrets.
305
+ assert .Equal (t , tc .initial == nil || ! isSecretImmutable (tc .initial ), reflectorRunning ())
306
+
307
+ if tc .eventual == nil {
308
+ return
309
+ }
310
+ fakeWatch .Add (tc .eventual )
311
+
312
+ // Eventually Get should return that secret.
313
+ getFn := func () (bool , error ) {
314
+ object , err := store .Get ("ns" , "name" )
315
+ if err != nil {
316
+ if apierrors .IsNotFound (err ) {
317
+ return false , nil
318
+ }
319
+ return false , err
320
+ }
321
+ secret := object .(* v1.Secret )
322
+ return apiequality .Semantic .DeepEqual (tc .eventual , secret ), nil
323
+ }
324
+ if err := wait .PollImmediate (10 * time .Millisecond , time .Second , getFn ); err != nil {
325
+ t .Errorf ("unexpected error: %v" , err )
326
+ }
327
+
328
+ // Reflector should already be stopped for immutable secrets.
329
+ assert .Equal (t , tc .eventual == nil || ! isSecretImmutable (tc .eventual ), reflectorRunning ())
330
+ })
331
+ }
332
+ }
0 commit comments