istio
243 строки · 8.1 Кб
1// Copyright Istio Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package controller
16
17import (
18"context"
19"fmt"
20"reflect"
21"testing"
22"time"
23
24v1 "k8s.io/api/core/v1"
25metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26"k8s.io/client-go/kubernetes"
27
28meshconfig "istio.io/api/mesh/v1alpha1"
29"istio.io/istio/pilot/pkg/keycertbundle"
30"istio.io/istio/pkg/config/constants"
31"istio.io/istio/pkg/config/mesh"
32"istio.io/istio/pkg/kube"
33"istio.io/istio/pkg/kube/inject"
34"istio.io/istio/pkg/kube/kclient"
35filter "istio.io/istio/pkg/kube/namespace"
36"istio.io/istio/pkg/test"
37"istio.io/istio/pkg/test/util/retry"
38)
39
40func TestNamespaceController(t *testing.T) {
41client := kube.NewFakeClient()
42t.Cleanup(client.Shutdown)
43watcher := keycertbundle.NewWatcher()
44caBundle := []byte("caBundle")
45watcher.SetAndNotify(nil, nil, caBundle)
46meshWatcher := mesh.NewTestWatcher(&meshconfig.MeshConfig{})
47stop := test.NewStop(t)
48discoveryNamespacesFilter := filter.NewDiscoveryNamespacesFilter(
49kclient.New[*v1.Namespace](client),
50meshWatcher,
51stop,
52)
53kube.SetObjectFilter(client, discoveryNamespacesFilter)
54nc := NewNamespaceController(client, watcher)
55client.RunAndWait(stop)
56go nc.Run(stop)
57retry.UntilOrFail(t, nc.queue.HasSynced)
58
59expectedData := map[string]string{
60constants.CACertNamespaceConfigMapDataName: string(caBundle),
61}
62createNamespace(t, client.Kube(), "foo", nil)
63expectConfigMap(t, nc.configmaps, CACertNamespaceConfigMap, "foo", expectedData)
64
65// Make sure random configmap does not get updated
66cmData := createConfigMap(t, client.Kube(), "not-root", "foo", "k")
67expectConfigMap(t, nc.configmaps, "not-root", "foo", cmData)
68
69newCaBundle := []byte("caBundle-new")
70watcher.SetAndNotify(nil, nil, newCaBundle)
71newData := map[string]string{
72constants.CACertNamespaceConfigMapDataName: string(newCaBundle),
73}
74expectConfigMap(t, nc.configmaps, CACertNamespaceConfigMap, "foo", newData)
75
76deleteConfigMap(t, client.Kube(), "foo")
77expectConfigMap(t, nc.configmaps, CACertNamespaceConfigMap, "foo", newData)
78
79ignoredNamespaces := inject.IgnoredNamespaces.Copy().Delete(constants.KubeSystemNamespace)
80for _, namespace := range ignoredNamespaces.UnsortedList() {
81// Create namespace in ignored list, make sure its not created
82createNamespace(t, client.Kube(), namespace, newData)
83// Configmap in that namespace should not do anything either
84createConfigMap(t, client.Kube(), "not-root", namespace, "k")
85expectConfigMapNotExist(t, nc.configmaps, namespace)
86}
87}
88
89func TestNamespaceControllerWithDiscoverySelectors(t *testing.T) {
90client := kube.NewFakeClient()
91t.Cleanup(client.Shutdown)
92watcher := keycertbundle.NewWatcher()
93caBundle := []byte("caBundle")
94watcher.SetAndNotify(nil, nil, caBundle)
95meshWatcher := mesh.NewTestWatcher(&meshconfig.MeshConfig{
96DiscoverySelectors: []*metav1.LabelSelector{
97{
98MatchLabels: map[string]string{
99"discovery-selectors": "enabled",
100},
101},
102},
103})
104stop := test.NewStop(t)
105discoveryNamespacesFilter := filter.NewDiscoveryNamespacesFilter(
106kclient.New[*v1.Namespace](client),
107meshWatcher,
108stop,
109)
110kube.SetObjectFilter(client, discoveryNamespacesFilter)
111nc := NewNamespaceController(client, watcher)
112client.RunAndWait(stop)
113go nc.Run(stop)
114retry.UntilOrFail(t, nc.queue.HasSynced)
115
116expectedData := map[string]string{
117constants.CACertNamespaceConfigMapDataName: string(caBundle),
118}
119
120nsA := "nsA"
121nsB := "nsB"
122
123// Create a namespace with discovery selector enabled
124createNamespace(t, client.Kube(), nsA, map[string]string{"discovery-selectors": "enabled"})
125// Create a namespace without discovery selector enabled
126createNamespace(t, client.Kube(), nsB, map[string]string{})
127expectConfigMap(t, nc.configmaps, CACertNamespaceConfigMap, nsA, expectedData)
128// config map should not be created for discovery selector disabled namespace
129expectConfigMapNotExist(t, nc.configmaps, nsB)
130}
131
132func TestNamespaceControllerDiscovery(t *testing.T) {
133client := kube.NewFakeClient()
134t.Cleanup(client.Shutdown)
135watcher := keycertbundle.NewWatcher()
136caBundle := []byte("caBundle")
137watcher.SetAndNotify(nil, nil, caBundle)
138meshWatcher := mesh.NewTestWatcher(&meshconfig.MeshConfig{
139DiscoverySelectors: []*metav1.LabelSelector{{
140MatchLabels: map[string]string{"kubernetes.io/metadata.name": "selected"},
141}},
142})
143stop := test.NewStop(t)
144discoveryNamespacesFilter := filter.NewDiscoveryNamespacesFilter(
145kclient.New[*v1.Namespace](client),
146meshWatcher,
147stop,
148)
149kube.SetObjectFilter(client, discoveryNamespacesFilter)
150nc := NewNamespaceController(client, watcher)
151client.RunAndWait(stop)
152go nc.Run(stop)
153retry.UntilOrFail(t, nc.queue.HasSynced)
154
155expectedData := map[string]string{
156constants.CACertNamespaceConfigMapDataName: string(caBundle),
157}
158createNamespace(t, client.Kube(), "not-selected", map[string]string{"kubernetes.io/metadata.name": "not-selected"})
159createNamespace(t, client.Kube(), "selected", map[string]string{"kubernetes.io/metadata.name": "selected"})
160
161expectConfigMap(t, nc.configmaps, CACertNamespaceConfigMap, "selected", expectedData)
162expectConfigMapNotExist(t, nc.configmaps, "not-selected")
163
164meshWatcher.Update(&meshconfig.MeshConfig{
165DiscoverySelectors: []*metav1.LabelSelector{{
166MatchLabels: map[string]string{"kubernetes.io/metadata.name": "not-selected"},
167}},
168}, time.Second)
169expectConfigMap(t, nc.configmaps, CACertNamespaceConfigMap, "not-selected", expectedData)
170expectConfigMapNotExist(t, nc.configmaps, "selected")
171}
172
173func deleteConfigMap(t *testing.T, client kubernetes.Interface, ns string) {
174t.Helper()
175_, err := client.CoreV1().ConfigMaps(ns).Get(context.TODO(), CACertNamespaceConfigMap, metav1.GetOptions{})
176if err != nil {
177t.Fatal(err)
178}
179if err := client.CoreV1().ConfigMaps(ns).Delete(context.TODO(), CACertNamespaceConfigMap, metav1.DeleteOptions{}); err != nil {
180t.Fatal(err)
181}
182}
183
184func createConfigMap(t *testing.T, client kubernetes.Interface, name, ns, key string) map[string]string {
185t.Helper()
186data := map[string]string{key: "v"}
187_, err := client.CoreV1().ConfigMaps(ns).Create(context.Background(), &v1.ConfigMap{
188ObjectMeta: metav1.ObjectMeta{
189Name: name,
190Namespace: ns,
191},
192Data: data,
193}, metav1.CreateOptions{})
194if err != nil {
195t.Fatal(err)
196}
197return data
198}
199
200func createNamespace(t *testing.T, client kubernetes.Interface, ns string, labels map[string]string) {
201t.Helper()
202if _, err := client.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{
203ObjectMeta: metav1.ObjectMeta{Name: ns, Labels: labels},
204}, metav1.CreateOptions{}); err != nil {
205t.Fatal(err)
206}
207}
208
209func updateNamespace(t *testing.T, client kubernetes.Interface, ns string, labels map[string]string) {
210t.Helper()
211if _, err := client.CoreV1().Namespaces().Update(context.TODO(), &v1.Namespace{
212ObjectMeta: metav1.ObjectMeta{Name: ns, Labels: labels},
213}, metav1.UpdateOptions{}); err != nil {
214t.Fatal(err)
215}
216}
217
218// nolint:unparam
219func expectConfigMap(t *testing.T, configmaps kclient.Client[*v1.ConfigMap], name, ns string, data map[string]string) {
220t.Helper()
221retry.UntilSuccessOrFail(t, func() error {
222cm := configmaps.Get(name, ns)
223if cm == nil {
224return fmt.Errorf("not found")
225}
226if !reflect.DeepEqual(cm.Data, data) {
227return fmt.Errorf("data mismatch, expected %+v got %+v", data, cm.Data)
228}
229return nil
230}, retry.Timeout(time.Second*10))
231}
232
233func expectConfigMapNotExist(t *testing.T, configmaps kclient.Client[*v1.ConfigMap], ns string) {
234t.Helper()
235err := retry.Until(func() bool {
236cm := configmaps.Get(CACertNamespaceConfigMap, ns)
237return cm != nil
238}, retry.Timeout(time.Millisecond*25))
239
240if err == nil {
241t.Fatalf("%s namespace should not have istio-ca-root-cert configmap.", ns)
242}
243}
244