istio
157 строк · 5.4 Кб
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 (
18v1 "k8s.io/api/core/v1"
19metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20"k8s.io/apimachinery/pkg/labels"
21"k8s.io/apimachinery/pkg/types"
22
23"istio.io/istio/pilot/pkg/features"
24"istio.io/istio/pilot/pkg/keycertbundle"
25"istio.io/istio/pkg/config/constants"
26"istio.io/istio/pkg/kube"
27"istio.io/istio/pkg/kube/controllers"
28"istio.io/istio/pkg/kube/inject"
29"istio.io/istio/pkg/kube/kclient"
30"istio.io/istio/pkg/util/sets"
31"istio.io/istio/security/pkg/k8s"
32)
33
34const (
35// CACertNamespaceConfigMap is the name of the ConfigMap in each namespace storing the root cert of non-Kube CA.
36CACertNamespaceConfigMap = "istio-ca-root-cert"
37
38// maxRetries is the number of times a namespace will be retried before it is dropped out of the queue.
39// With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the
40// sequence of delays between successive queuing of a namespace.
41//
42// 5ms, 10ms, 20ms, 40ms, 80ms
43maxRetries = 5
44)
45
46var configMapLabel = map[string]string{"istio.io/config": "true"}
47
48// NamespaceController manages reconciles a configmap in each namespace with a desired set of data.
49type NamespaceController struct {
50caBundleWatcher *keycertbundle.Watcher
51
52queue controllers.Queue
53
54namespaces kclient.Client[*v1.Namespace]
55configmaps kclient.Client[*v1.ConfigMap]
56
57ignoredNamespaces sets.Set[string]
58}
59
60// NewNamespaceController returns a pointer to a newly constructed NamespaceController instance.
61func NewNamespaceController(kubeClient kube.Client, caBundleWatcher *keycertbundle.Watcher) *NamespaceController {
62c := &NamespaceController{
63caBundleWatcher: caBundleWatcher,
64}
65c.queue = controllers.NewQueue("namespace controller",
66controllers.WithReconciler(c.reconcileCACert),
67controllers.WithMaxAttempts(maxRetries))
68
69c.configmaps = kclient.NewFiltered[*v1.ConfigMap](kubeClient, kclient.Filter{
70FieldSelector: "metadata.name=" + CACertNamespaceConfigMap,
71ObjectFilter: kube.FilterIfEnhancedFilteringEnabled(kubeClient),
72})
73c.namespaces = kclient.NewFiltered[*v1.Namespace](kubeClient, kclient.Filter{
74ObjectFilter: kube.FilterIfEnhancedFilteringEnabled(kubeClient),
75})
76// kube-system is not skipped to enable deploying ztunnel in that namespace
77c.ignoredNamespaces = inject.IgnoredNamespaces.Copy().Delete(constants.KubeSystemNamespace)
78
79c.configmaps.AddEventHandler(controllers.FilteredObjectSpecHandler(c.queue.AddObject, func(o controllers.Object) bool {
80// skip special kubernetes system namespaces
81return !c.ignoredNamespaces.Contains(o.GetNamespace())
82}))
83
84c.namespaces.AddEventHandler(controllers.FilteredObjectSpecHandler(c.queue.AddObject, func(o controllers.Object) bool {
85if features.InformerWatchNamespace != "" && features.InformerWatchNamespace != o.GetName() {
86// We are only watching one namespace, and its not this one
87return false
88}
89if c.ignoredNamespaces.Contains(o.GetName()) {
90// skip special kubernetes system namespaces
91return false
92}
93return true
94}))
95return c
96}
97
98// Run starts the NamespaceController until a value is sent to stopCh.
99func (nc *NamespaceController) Run(stopCh <-chan struct{}) {
100if !kube.WaitForCacheSync("namespace controller", stopCh, nc.namespaces.HasSynced, nc.configmaps.HasSynced) {
101return
102}
103
104go nc.startCaBundleWatcher(stopCh)
105nc.queue.Run(stopCh)
106controllers.ShutdownAll(nc.configmaps, nc.namespaces)
107}
108
109// startCaBundleWatcher listens for updates to the CA bundle and update cm in each namespace
110func (nc *NamespaceController) startCaBundleWatcher(stop <-chan struct{}) {
111id, watchCh := nc.caBundleWatcher.AddWatcher()
112defer nc.caBundleWatcher.RemoveWatcher(id)
113for {
114select {
115case <-watchCh:
116for _, ns := range nc.namespaces.List("", labels.Everything()) {
117nc.namespaceChange(ns)
118}
119case <-stop:
120return
121}
122}
123}
124
125// reconcileCACert will reconcile the ca root cert configmap for the specified namespace
126// If the configmap is not found, it will be created.
127// If the namespace is filtered out by discovery selector, the configmap will be deleted.
128func (nc *NamespaceController) reconcileCACert(o types.NamespacedName) error {
129ns := o.Namespace
130if ns == "" {
131// For Namespace object, it will not have o.Namespace field set
132ns = o.Name
133}
134
135meta := metav1.ObjectMeta{
136Name: CACertNamespaceConfigMap,
137Namespace: ns,
138Labels: configMapLabel,
139}
140return k8s.InsertDataToConfigMap(nc.configmaps, meta, nc.caBundleWatcher.GetCABundle())
141}
142
143// On namespace change, update the config map.
144// If terminating, this will be skipped
145func (nc *NamespaceController) namespaceChange(ns *v1.Namespace) {
146if ns.Status.Phase != v1.NamespaceTerminating {
147nc.syncNamespace(ns.Name)
148}
149}
150
151func (nc *NamespaceController) syncNamespace(ns string) {
152// skip special kubernetes system namespaces
153if nc.ignoredNamespaces.Contains(ns) {
154return
155}
156nc.queue.Add(types.NamespacedName{Name: ns})
157}
158