1
16
17 package rootcacertpublisher
18
19 import (
20 "context"
21 "reflect"
22 "testing"
23
24 "github.com/google/go-cmp/cmp"
25 v1 "k8s.io/api/core/v1"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/client-go/informers"
29 "k8s.io/client-go/kubernetes/fake"
30 corev1listers "k8s.io/client-go/listers/core/v1"
31 clienttesting "k8s.io/client-go/testing"
32 "k8s.io/client-go/tools/cache"
33 "k8s.io/kubernetes/pkg/controller"
34 )
35
36 func TestConfigMapCreation(t *testing.T) {
37 ns := metav1.NamespaceDefault
38 fakeRootCA := []byte("fake-root-ca")
39
40 caConfigMap := defaultCrtConfigMapPtr(fakeRootCA)
41 addFieldCM := defaultCrtConfigMapPtr(fakeRootCA)
42 addFieldCM.Data["test"] = "test"
43 modifyFieldCM := defaultCrtConfigMapPtr([]byte("abc"))
44 otherConfigMap := &v1.ConfigMap{
45 ObjectMeta: metav1.ObjectMeta{
46 Name: "other",
47 Namespace: ns,
48 ResourceVersion: "1",
49 },
50 }
51 updateOtherConfigMap := &v1.ConfigMap{
52 ObjectMeta: metav1.ObjectMeta{
53 Name: "other",
54 Namespace: ns,
55 ResourceVersion: "1",
56 },
57 Data: map[string]string{"aa": "bb"},
58 }
59
60 existNS := &v1.Namespace{
61 ObjectMeta: metav1.ObjectMeta{Name: ns},
62 Status: v1.NamespaceStatus{
63 Phase: v1.NamespaceActive,
64 },
65 }
66 newNs := &v1.Namespace{
67 ObjectMeta: metav1.ObjectMeta{Name: "new"},
68 Status: v1.NamespaceStatus{
69 Phase: v1.NamespaceActive,
70 },
71 }
72 terminatingNS := &v1.Namespace{
73 ObjectMeta: metav1.ObjectMeta{Name: ns},
74 Status: v1.NamespaceStatus{
75 Phase: v1.NamespaceTerminating,
76 },
77 }
78
79 type action struct {
80 verb string
81 name string
82 }
83 testcases := map[string]struct {
84 ExistingConfigMaps []*v1.ConfigMap
85 AddedNamespace *v1.Namespace
86 UpdatedNamespace *v1.Namespace
87 DeletedConfigMap *v1.ConfigMap
88 UpdatedConfigMap *v1.ConfigMap
89 ExpectActions []action
90 }{
91 "create new namespace": {
92 AddedNamespace: newNs,
93 ExpectActions: []action{{verb: "create", name: RootCACertConfigMapName}},
94 },
95 "delete other configmap": {
96 ExistingConfigMaps: []*v1.ConfigMap{otherConfigMap, caConfigMap},
97 DeletedConfigMap: otherConfigMap,
98 },
99 "delete ca configmap": {
100 ExistingConfigMaps: []*v1.ConfigMap{otherConfigMap, caConfigMap},
101 DeletedConfigMap: caConfigMap,
102 ExpectActions: []action{{verb: "create", name: RootCACertConfigMapName}},
103 },
104 "update ca configmap with adding field": {
105 ExistingConfigMaps: []*v1.ConfigMap{caConfigMap},
106 UpdatedConfigMap: addFieldCM,
107 ExpectActions: []action{{verb: "update", name: RootCACertConfigMapName}},
108 },
109 "update ca configmap with modifying field": {
110 ExistingConfigMaps: []*v1.ConfigMap{caConfigMap},
111 UpdatedConfigMap: modifyFieldCM,
112 ExpectActions: []action{{verb: "update", name: RootCACertConfigMapName}},
113 },
114 "update with other configmap": {
115 ExistingConfigMaps: []*v1.ConfigMap{caConfigMap, otherConfigMap},
116 UpdatedConfigMap: updateOtherConfigMap,
117 },
118 "update namespace with terminating state": {
119 UpdatedNamespace: terminatingNS,
120 },
121 }
122
123 for k, tc := range testcases {
124 t.Run(k, func(t *testing.T) {
125 client := fake.NewSimpleClientset(caConfigMap, existNS)
126 informers := informers.NewSharedInformerFactory(fake.NewSimpleClientset(), controller.NoResyncPeriodFunc())
127 cmInformer := informers.Core().V1().ConfigMaps()
128 nsInformer := informers.Core().V1().Namespaces()
129 controller, err := NewPublisher(cmInformer, nsInformer, client, fakeRootCA)
130 if err != nil {
131 t.Fatalf("error creating ServiceAccounts controller: %v", err)
132 }
133
134 cmStore := cmInformer.Informer().GetStore()
135
136 controller.syncHandler = controller.syncNamespace
137
138 for _, s := range tc.ExistingConfigMaps {
139 cmStore.Add(s)
140 }
141
142 if tc.AddedNamespace != nil {
143 controller.namespaceAdded(tc.AddedNamespace)
144 }
145 if tc.UpdatedNamespace != nil {
146 controller.namespaceUpdated(nil, tc.UpdatedNamespace)
147 }
148
149 if tc.DeletedConfigMap != nil {
150 cmStore.Delete(tc.DeletedConfigMap)
151 controller.configMapDeleted(tc.DeletedConfigMap)
152 }
153
154 if tc.UpdatedConfigMap != nil {
155 cmStore.Add(tc.UpdatedConfigMap)
156 controller.configMapUpdated(nil, tc.UpdatedConfigMap)
157 }
158 ctx := context.TODO()
159 for controller.queue.Len() != 0 {
160 controller.processNextWorkItem(ctx)
161 }
162
163 actions := client.Actions()
164 if reflect.DeepEqual(actions, tc.ExpectActions) {
165 t.Errorf("Unexpected actions:\n%s", cmp.Diff(actions, tc.ExpectActions))
166 }
167 })
168 }
169 }
170
171 func defaultCrtConfigMapPtr(rootCA []byte) *v1.ConfigMap {
172 tmp := v1.ConfigMap{
173 ObjectMeta: metav1.ObjectMeta{
174 Name: RootCACertConfigMapName,
175 },
176 Data: map[string]string{
177 "ca.crt": string(rootCA),
178 },
179 }
180 tmp.Namespace = metav1.NamespaceDefault
181 return &tmp
182 }
183
184 func TestConfigMapUpdateNoHotLoop(t *testing.T) {
185 testcases := map[string]struct {
186 ExistingConfigMaps []runtime.Object
187 ExpectActions func(t *testing.T, actions []clienttesting.Action)
188 }{
189 "update-configmap-annotation": {
190 ExistingConfigMaps: []runtime.Object{
191 &v1.ConfigMap{
192 ObjectMeta: metav1.ObjectMeta{
193 Namespace: "default",
194 Name: RootCACertConfigMapName,
195 },
196 Data: map[string]string{"ca.crt": "fake"},
197 },
198 },
199 ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
200 if len(actions) != 1 {
201 t.Fatal(actions)
202 }
203 if actions[0].GetVerb() != "update" {
204 t.Fatal(actions)
205 }
206 actualObj := actions[0].(clienttesting.UpdateAction).GetObject()
207 if actualObj.(*v1.ConfigMap).Annotations[DescriptionAnnotation] != Description {
208 t.Fatal(actions)
209 }
210 if !reflect.DeepEqual(actualObj.(*v1.ConfigMap).Data["ca.crt"], "fake") {
211 t.Fatal(actions)
212 }
213 },
214 },
215 "no-update-configmap-if-annotation-present-and-equal": {
216 ExistingConfigMaps: []runtime.Object{
217 &v1.ConfigMap{
218 ObjectMeta: metav1.ObjectMeta{
219 Namespace: "default",
220 Name: RootCACertConfigMapName,
221 Annotations: map[string]string{DescriptionAnnotation: Description},
222 },
223 Data: map[string]string{"ca.crt": "fake"},
224 },
225 },
226 ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
227 if len(actions) != 0 {
228 t.Fatal(actions)
229 }
230 },
231 },
232 "no-update-configmap-if-annotation-present-and-different": {
233 ExistingConfigMaps: []runtime.Object{
234 &v1.ConfigMap{
235 ObjectMeta: metav1.ObjectMeta{
236 Namespace: "default",
237 Name: RootCACertConfigMapName,
238 Annotations: map[string]string{DescriptionAnnotation: "different"},
239 },
240 Data: map[string]string{"ca.crt": "fake"},
241 },
242 },
243 ExpectActions: func(t *testing.T, actions []clienttesting.Action) {
244 if len(actions) != 0 {
245 t.Fatal(actions)
246 }
247 },
248 },
249 }
250
251 for k, tc := range testcases {
252 t.Run(k, func(t *testing.T) {
253 client := fake.NewSimpleClientset(tc.ExistingConfigMaps...)
254 configMapIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
255 for _, obj := range tc.ExistingConfigMaps {
256 configMapIndexer.Add(obj)
257 }
258
259
260 controller := Publisher{
261 client: client,
262 rootCA: []byte("fake"),
263 cmLister: corev1listers.NewConfigMapLister(configMapIndexer),
264 cmListerSynced: func() bool { return true },
265 nsListerSynced: func() bool { return true },
266 }
267 ctx := context.TODO()
268 err := controller.syncNamespace(ctx, "default")
269 if err != nil {
270 t.Fatal(err)
271 }
272 tc.ExpectActions(t, client.Actions())
273 })
274 }
275 }
276
View as plain text