github.com/cilium/cilium@v1.16.2/operator/watchers/node.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package watchers
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  
    10  	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
    11  	"k8s.io/apimachinery/pkg/runtime/schema"
    12  	"k8s.io/client-go/tools/cache"
    13  	"k8s.io/client-go/util/workqueue"
    14  
    15  	"github.com/cilium/cilium/pkg/k8s/informer"
    16  	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
    17  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    18  	slimclientset "github.com/cilium/cilium/pkg/k8s/slim/k8s/client/clientset/versioned"
    19  	"github.com/cilium/cilium/pkg/k8s/utils"
    20  )
    21  
    22  var (
    23  	// nodeSyncOnce is used to make sure nodesInit is only setup once.
    24  	nodeSyncOnce sync.Once
    25  
    26  	// slimNodeStore contains all cluster nodes store as slim_core.Node
    27  	slimNodeStore cache.Store
    28  
    29  	// slimNodeStoreSynced is closed once the slimNodeStore is synced
    30  	// with k8s.
    31  	slimNodeStoreSynced = make(chan struct{})
    32  
    33  	nodeController cache.Controller
    34  
    35  	nodeQueue = workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "node-queue")
    36  )
    37  
    38  // NodeQueueShutDown is a wrapper to expose ShutDown for the global nodeQueue.
    39  // It is meant to be used in unit test like the identity-gc one in operator/identity/
    40  // in order to avoid goleak complaining about leaked goroutines.
    41  func NodeQueueShutDown() {
    42  	nodeQueue.ShutDown()
    43  }
    44  
    45  type slimNodeGetter interface {
    46  	GetK8sSlimNode(nodeName string) (*slim_corev1.Node, error)
    47  	ListK8sSlimNode() []*slim_corev1.Node
    48  }
    49  
    50  type nodeGetter struct{}
    51  
    52  // GetK8sSlimNode returns a slim_corev1.Node from the local store.
    53  // The return structure should only be used for read purposes and should never
    54  // be written into it.
    55  func (nodeGetter) GetK8sSlimNode(nodeName string) (*slim_corev1.Node, error) {
    56  	nodeInterface, exists, err := slimNodeStore.GetByKey(nodeName)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	if !exists {
    61  		return nil, k8sErrors.NewNotFound(schema.GroupResource{
    62  			Group:    "core",
    63  			Resource: "Node",
    64  		}, nodeName)
    65  	}
    66  	return nodeInterface.(*slim_corev1.Node), nil
    67  }
    68  
    69  func (nodeGetter) ListK8sSlimNode() []*slim_corev1.Node {
    70  	nodesInt := slimNodeStore.List()
    71  	out := make([]*slim_corev1.Node, 0, len(nodesInt))
    72  	for i := range nodesInt {
    73  		out = append(out, nodesInt[i].(*slim_corev1.Node))
    74  	}
    75  	return out
    76  }
    77  
    78  // nodesInit starts up a node watcher to handle node events.
    79  func nodesInit(wg *sync.WaitGroup, slimClient slimclientset.Interface, stopCh <-chan struct{}) {
    80  	nodeSyncOnce.Do(func() {
    81  		slimNodeStore, nodeController = informer.NewInformer(
    82  			utils.ListerWatcherFromTyped[*slim_corev1.NodeList](slimClient.CoreV1().Nodes()),
    83  			&slim_corev1.Node{},
    84  			0,
    85  			cache.ResourceEventHandlerFuncs{
    86  				AddFunc: func(obj interface{}) {
    87  					key, _ := queueKeyFunc(obj)
    88  					nodeQueue.Add(key)
    89  				},
    90  				UpdateFunc: func(_, newObj interface{}) {
    91  					key, _ := queueKeyFunc(newObj)
    92  					nodeQueue.Add(key)
    93  				},
    94  			},
    95  			transformToNode,
    96  		)
    97  		wg.Add(1)
    98  		go func() {
    99  			defer wg.Done()
   100  			defer nodeQueue.ShutDown()
   101  			nodeController.Run(stopCh)
   102  		}()
   103  
   104  		cache.WaitForCacheSync(stopCh, nodeController.HasSynced)
   105  		close(slimNodeStoreSynced)
   106  	})
   107  }
   108  
   109  func transformToNode(obj interface{}) (interface{}, error) {
   110  	switch concreteObj := obj.(type) {
   111  	case *slim_corev1.Node:
   112  		n := &slim_corev1.Node{
   113  			TypeMeta: concreteObj.TypeMeta,
   114  			ObjectMeta: slim_metav1.ObjectMeta{
   115  				Name:            concreteObj.Name,
   116  				ResourceVersion: concreteObj.ResourceVersion,
   117  			},
   118  			Spec: slim_corev1.NodeSpec{
   119  				Taints: concreteObj.Spec.Taints,
   120  			},
   121  			Status: slim_corev1.NodeStatus{
   122  				Conditions: concreteObj.Status.Conditions,
   123  			},
   124  		}
   125  		*concreteObj = slim_corev1.Node{}
   126  		return n, nil
   127  	case cache.DeletedFinalStateUnknown:
   128  		node, ok := concreteObj.Obj.(*slim_corev1.Node)
   129  		if !ok {
   130  			return nil, fmt.Errorf("unknown object type %T", concreteObj.Obj)
   131  		}
   132  		dfsu := cache.DeletedFinalStateUnknown{
   133  			Key: concreteObj.Key,
   134  			Obj: &slim_corev1.Node{
   135  				TypeMeta: node.TypeMeta,
   136  				ObjectMeta: slim_metav1.ObjectMeta{
   137  					Name:            node.Name,
   138  					ResourceVersion: node.ResourceVersion,
   139  				},
   140  				Spec: slim_corev1.NodeSpec{
   141  					Taints: node.Spec.Taints,
   142  				},
   143  				Status: slim_corev1.NodeStatus{
   144  					Conditions: node.Status.Conditions,
   145  				},
   146  			},
   147  		}
   148  		// Small GC optimization
   149  		*node = slim_corev1.Node{}
   150  		return dfsu, nil
   151  	default:
   152  		return nil, fmt.Errorf("unknown object type %T", concreteObj)
   153  	}
   154  }