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