github.com/cilium/cilium@v1.16.2/pkg/k8s/watchers/namespace.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package watchers 5 6 import ( 7 "context" 8 "errors" 9 "sync/atomic" 10 11 "github.com/cilium/hive/cell" 12 k8s_errors "k8s.io/apimachinery/pkg/api/errors" 13 "k8s.io/apimachinery/pkg/runtime/schema" 14 15 agentK8s "github.com/cilium/cilium/daemon/k8s" 16 "github.com/cilium/cilium/pkg/endpointmanager" 17 ciliumio "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 18 "github.com/cilium/cilium/pkg/k8s/resource" 19 slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1" 20 slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 21 k8sSynced "github.com/cilium/cilium/pkg/k8s/synced" 22 "github.com/cilium/cilium/pkg/labels" 23 "github.com/cilium/cilium/pkg/labelsfilter" 24 "github.com/cilium/cilium/pkg/logging/logfields" 25 "github.com/cilium/cilium/pkg/policy" 26 ) 27 28 type k8sNamespaceWatcherParams struct { 29 cell.In 30 31 Resources agentK8s.Resources 32 K8sResourceSynced *k8sSynced.Resources 33 K8sAPIGroups *k8sSynced.APIGroups 34 35 EndpointManager endpointmanager.EndpointManager 36 } 37 38 func newK8sNamespaceWatcher(params k8sNamespaceWatcherParams) *K8sNamespaceWatcher { 39 return &K8sNamespaceWatcher{ 40 k8sResourceSynced: params.K8sResourceSynced, 41 k8sAPIGroups: params.K8sAPIGroups, 42 resources: params.Resources, 43 endpointManager: params.EndpointManager, 44 stop: make(chan struct{}), 45 } 46 } 47 48 type K8sNamespaceWatcher struct { 49 // k8sResourceSynced maps a resource name to a channel. Once the given 50 // resource name is synchronized with k8s, the channel for which that 51 // resource name maps to is closed. 52 k8sResourceSynced *k8sSynced.Resources 53 // k8sAPIGroups is a set of k8s API in use. They are setup in watchers, 54 // and may be disabled while the agent runs. 55 k8sAPIGroups *k8sSynced.APIGroups 56 resources agentK8s.Resources 57 58 endpointManager endpointManager 59 60 stop chan struct{} 61 } 62 63 func (k *K8sNamespaceWatcher) namespacesInit() { 64 apiGroup := k8sAPIGroupNamespaceV1Core 65 66 var synced atomic.Bool 67 68 k.k8sResourceSynced.BlockWaitGroupToSyncResources( 69 k.stop, 70 nil, 71 func() bool { return synced.Load() }, 72 apiGroup, 73 ) 74 k.k8sAPIGroups.AddAPI(apiGroup) 75 76 nsUpdater := namespaceUpdater{ 77 oldIdtyLabels: make(map[string]labels.Labels), 78 endpointManager: k.endpointManager, 79 } 80 81 ctx, cancel := context.WithCancel(context.Background()) 82 events := k.resources.Namespaces.Events(ctx) 83 84 go func() { 85 for { 86 select { 87 case <-k.stop: 88 cancel() 89 case event, ok := <-events: 90 if !ok { 91 return 92 } 93 var err error 94 switch event.Kind { 95 case resource.Sync: 96 synced.Store(true) 97 case resource.Upsert: 98 err = nsUpdater.update(event.Object) 99 } 100 event.Done(err) 101 } 102 } 103 }() 104 } 105 106 func (k *K8sNamespaceWatcher) stopWatcher() { 107 close(k.stop) 108 } 109 110 type namespaceUpdater struct { 111 oldIdtyLabels map[string]labels.Labels 112 113 endpointManager endpointManager 114 } 115 116 func getNamespaceLabels(ns *slim_corev1.Namespace) labels.Labels { 117 lbls := ns.GetLabels() 118 labelMap := make(map[string]string, len(lbls)) 119 for k, v := range lbls { 120 labelMap[policy.JoinPath(ciliumio.PodNamespaceMetaLabels, k)] = v 121 } 122 return labels.Map2Labels(labelMap, labels.LabelSourceK8s) 123 } 124 125 func (u *namespaceUpdater) update(newNS *slim_corev1.Namespace) error { 126 newLabels := getNamespaceLabels(newNS) 127 128 oldIdtyLabels := u.oldIdtyLabels[newNS.Name] 129 newIdtyLabels, _ := labelsfilter.Filter(newLabels) 130 131 // Do not perform any other operations if the old labels are the same as 132 // the new labels. 133 if oldIdtyLabels.DeepEqual(&newIdtyLabels) { 134 return nil 135 } 136 137 eps := u.endpointManager.GetEndpoints() 138 failed := false 139 for _, ep := range eps { 140 epNS := ep.GetK8sNamespace() 141 if newNS.Name == epNS { 142 err := ep.ModifyIdentityLabels(labels.LabelSourceK8s, newIdtyLabels, oldIdtyLabels) 143 if err != nil { 144 log.WithError(err).WithField(logfields.EndpointID, ep.ID). 145 Warning("unable to update endpoint with new identity labels from namespace labels") 146 failed = true 147 } 148 } 149 } 150 if failed { 151 return errors.New("unable to update some endpoints with new namespace labels") 152 } 153 u.oldIdtyLabels[newNS.Name] = newIdtyLabels 154 return nil 155 } 156 157 // GetCachedNamespace returns a namespace from the local store. 158 func (k *K8sNamespaceWatcher) GetCachedNamespace(namespace string) (*slim_corev1.Namespace, error) { 159 nsName := &slim_corev1.Namespace{ 160 ObjectMeta: slim_metav1.ObjectMeta{ 161 Name: namespace, 162 }, 163 } 164 165 store, err := k.resources.Namespaces.Store(context.Background()) 166 if err != nil { 167 return nil, err 168 } 169 ns, exists, err := store.Get(nsName) 170 if err != nil { 171 return nil, err 172 } 173 if !exists { 174 return nil, k8s_errors.NewNotFound(schema.GroupResource{ 175 Group: "core", 176 Resource: "namespace", 177 }, namespace) 178 } 179 return ns.DeepCopy(), nil 180 }