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  }