github.com/cilium/cilium@v1.16.2/pkg/k8s/resource_ctors.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package k8s
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  
    10  	"github.com/cilium/cilium/pkg/allocator"
    11  	"github.com/cilium/cilium/pkg/identity/key"
    12  
    13  	"github.com/cilium/hive/cell"
    14  	"github.com/spf13/pflag"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	k8sRuntime "k8s.io/apimachinery/pkg/runtime"
    17  	"k8s.io/apimachinery/pkg/watch"
    18  	"k8s.io/client-go/tools/cache"
    19  
    20  	cilium_api_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    21  	cilium_api_v2alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    22  	"github.com/cilium/cilium/pkg/k8s/client"
    23  	"github.com/cilium/cilium/pkg/k8s/resource"
    24  	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
    25  	slim_discoveryv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/discovery/v1"
    26  	slim_discoveryv1beta1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/discovery/v1beta1"
    27  	slim_networkingv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/networking/v1"
    28  	"github.com/cilium/cilium/pkg/k8s/synced"
    29  	"github.com/cilium/cilium/pkg/k8s/types"
    30  	"github.com/cilium/cilium/pkg/k8s/utils"
    31  	"github.com/cilium/cilium/pkg/k8s/version"
    32  	"github.com/cilium/cilium/pkg/node"
    33  	"github.com/cilium/cilium/pkg/promise"
    34  )
    35  
    36  // Config defines the configuration options for k8s resources.
    37  type Config struct {
    38  	EnableK8sEndpointSlice bool
    39  
    40  	// K8sServiceProxyName is the value of service.kubernetes.io/service-proxy-name label,
    41  	// that identifies the service objects Cilium should handle.
    42  	// If the provided value is an empty string, Cilium will manage service objects when
    43  	// the label is not present. For more details -
    44  	// https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/2447-Make-kube-proxy-service-abstraction-optional
    45  	K8sServiceProxyName string
    46  }
    47  
    48  // DefaultConfig represents the default k8s resources config values.
    49  var DefaultConfig = Config{
    50  	EnableK8sEndpointSlice: true,
    51  }
    52  
    53  const (
    54  	NamespaceIndex = "namespace"
    55  	ByKeyIndex     = "by-key-index"
    56  )
    57  
    58  // Flags implements the cell.Flagger interface.
    59  func (def Config) Flags(flags *pflag.FlagSet) {
    60  	flags.Bool("enable-k8s-endpoint-slice", def.EnableK8sEndpointSlice, "Enables k8s EndpointSlice feature in Cilium if the k8s cluster supports it")
    61  	flags.String("k8s-service-proxy-name", def.K8sServiceProxyName, "Value of K8s service-proxy-name label for which Cilium handles the services (empty = all services without service.kubernetes.io/service-proxy-name label)")
    62  }
    63  
    64  // namespaceIndexFunc is an IndexFunc that indexes Namespace of Kubernetes
    65  // types by their namespace.
    66  func namespaceIndexFunc(obj any) ([]string, error) {
    67  	object, ok := obj.(utils.NamespaceNameGetter)
    68  	if !ok {
    69  		return nil, fmt.Errorf("unexpected object type: %T", obj)
    70  	}
    71  	return []string{object.GetNamespace()}, nil
    72  }
    73  
    74  func GetIdentitiesByKeyFunc(keyFunc func(map[string]string) allocator.AllocatorKey) func(obj interface{}) ([]string, error) {
    75  	return func(obj interface{}) ([]string, error) {
    76  		if identity, ok := obj.(*cilium_api_v2.CiliumIdentity); ok {
    77  			return []string{keyFunc(identity.SecurityLabels).GetKey()}, nil
    78  		}
    79  		return []string{}, fmt.Errorf("object other than CiliumIdentity was pushed to the store")
    80  	}
    81  }
    82  
    83  // Dependencies for Cilium resources that may be used by Cilium Agent.
    84  // When CRDSyncPromise is provided, watchers of resources using this
    85  // will block until all CRDs used by the agent have been registered.
    86  // Agent will fail to start if Cilium Operator does not register all
    87  // the CRDs in time.
    88  type CiliumResourceParams struct {
    89  	cell.In
    90  
    91  	Lifecycle      cell.Lifecycle
    92  	ClientSet      client.Clientset
    93  	CRDSyncPromise promise.Promise[synced.CRDSync] `optional:"true"`
    94  }
    95  
    96  // ServiceResource builds the Resource[Service] object.
    97  func ServiceResource(lc cell.Lifecycle, cfg Config, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*slim_corev1.Service], error) {
    98  	if !cs.IsEnabled() {
    99  		return nil, nil
   100  	}
   101  	optsModifier, err := utils.GetServiceAndEndpointListOptionsModifier(cfg.K8sServiceProxyName)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	indexers := cache.Indexers{
   106  		NamespaceIndex: namespaceIndexFunc,
   107  	}
   108  	lw := utils.ListerWatcherWithModifiers(
   109  		utils.ListerWatcherFromTyped[*slim_corev1.ServiceList](cs.Slim().CoreV1().Services("")),
   110  		append(opts, optsModifier)...,
   111  	)
   112  	return resource.New[*slim_corev1.Service](
   113  		lc, lw,
   114  		resource.WithMetric("Service"),
   115  		resource.WithIndexers(indexers),
   116  	), nil
   117  }
   118  
   119  func NodeResource(lc cell.Lifecycle, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*slim_corev1.Node], error) {
   120  	if !cs.IsEnabled() {
   121  		return nil, nil
   122  	}
   123  	lw := utils.ListerWatcherWithModifiers(
   124  		utils.ListerWatcherFromTyped[*slim_corev1.NodeList](cs.Slim().CoreV1().Nodes()),
   125  		opts...,
   126  	)
   127  	return resource.New[*slim_corev1.Node](lc, lw, resource.WithMetric("Node")), nil
   128  }
   129  
   130  func CiliumNodeResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2.CiliumNode], error) {
   131  	if !params.ClientSet.IsEnabled() {
   132  		return nil, nil
   133  	}
   134  	lw := utils.ListerWatcherWithModifiers(
   135  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumNodeList](params.ClientSet.CiliumV2().CiliumNodes()),
   136  		opts...,
   137  	)
   138  	return resource.New[*cilium_api_v2.CiliumNode](params.Lifecycle, lw,
   139  		resource.WithMetric("CiliumNode"),
   140  		resource.WithStoppableInformer(),
   141  		resource.WithCRDSync(params.CRDSyncPromise), // optional, can be nil
   142  	), nil
   143  }
   144  
   145  func PodResource(lc cell.Lifecycle, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*slim_corev1.Pod], error) {
   146  	if !cs.IsEnabled() {
   147  		return nil, nil
   148  	}
   149  	lw := utils.ListerWatcherWithModifiers(
   150  		utils.ListerWatcherFromTyped[*slim_corev1.PodList](cs.Slim().CoreV1().Pods("")),
   151  		opts...,
   152  	)
   153  	return resource.New[*slim_corev1.Pod](lc, lw, resource.WithMetric("Pod")), nil
   154  }
   155  
   156  func NamespaceResource(lc cell.Lifecycle, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*slim_corev1.Namespace], error) {
   157  	if !cs.IsEnabled() {
   158  		return nil, nil
   159  	}
   160  	lw := utils.ListerWatcherWithModifiers(
   161  		utils.ListerWatcherFromTyped[*slim_corev1.NamespaceList](cs.Slim().CoreV1().Namespaces()),
   162  		opts...,
   163  	)
   164  	return resource.New[*slim_corev1.Namespace](lc, lw, resource.WithMetric("Namespace")), nil
   165  }
   166  
   167  func LBIPPoolsResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumLoadBalancerIPPool], error) {
   168  	if !params.ClientSet.IsEnabled() {
   169  		return nil, nil
   170  	}
   171  	lw := utils.ListerWatcherWithModifiers(
   172  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumLoadBalancerIPPoolList](params.ClientSet.CiliumV2alpha1().CiliumLoadBalancerIPPools()),
   173  		opts...,
   174  	)
   175  	return resource.New[*cilium_api_v2alpha1.CiliumLoadBalancerIPPool](params.Lifecycle, lw, resource.WithMetric("CiliumLoadBalancerIPPool"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   176  }
   177  
   178  func CiliumIdentityResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2.CiliumIdentity], error) {
   179  	if !params.ClientSet.IsEnabled() {
   180  		return nil, nil
   181  	}
   182  	lw := utils.ListerWatcherWithModifiers(
   183  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumIdentityList](params.ClientSet.CiliumV2().CiliumIdentities()),
   184  		opts...,
   185  	)
   186  
   187  	indexers := cache.Indexers{
   188  		ByKeyIndex: GetIdentitiesByKeyFunc((&key.GlobalIdentity{}).PutKeyFromMap),
   189  	}
   190  
   191  	return resource.New[*cilium_api_v2.CiliumIdentity](params.Lifecycle, lw, resource.WithMetric("CiliumIdentityList"), resource.WithIndexers(indexers), resource.WithCRDSync(params.CRDSyncPromise)), nil
   192  }
   193  
   194  func NetworkPolicyResource(lc cell.Lifecycle, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*slim_networkingv1.NetworkPolicy], error) {
   195  	if !cs.IsEnabled() {
   196  		return nil, nil
   197  	}
   198  	lw := utils.ListerWatcherWithModifiers(
   199  		utils.ListerWatcherFromTyped[*slim_networkingv1.NetworkPolicyList](cs.Slim().NetworkingV1().NetworkPolicies("")),
   200  		opts...,
   201  	)
   202  	return resource.New[*slim_networkingv1.NetworkPolicy](lc, lw, resource.WithMetric("NetworkPolicy")), nil
   203  }
   204  
   205  func CiliumNetworkPolicyResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2.CiliumNetworkPolicy], error) {
   206  	if !params.ClientSet.IsEnabled() {
   207  		return nil, nil
   208  	}
   209  	lw := utils.ListerWatcherWithModifiers(
   210  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumNetworkPolicyList](params.ClientSet.CiliumV2().CiliumNetworkPolicies("")),
   211  		opts...,
   212  	)
   213  	return resource.New[*cilium_api_v2.CiliumNetworkPolicy](params.Lifecycle, lw, resource.WithMetric("CiliumNetworkPolicy"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   214  }
   215  
   216  func CiliumClusterwideNetworkPolicyResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2.CiliumClusterwideNetworkPolicy], error) {
   217  	if !params.ClientSet.IsEnabled() {
   218  		return nil, nil
   219  	}
   220  	lw := utils.ListerWatcherWithModifiers(
   221  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumClusterwideNetworkPolicyList](params.ClientSet.CiliumV2().CiliumClusterwideNetworkPolicies()),
   222  		opts...,
   223  	)
   224  	return resource.New[*cilium_api_v2.CiliumClusterwideNetworkPolicy](params.Lifecycle, lw, resource.WithMetric("CiliumClusterwideNetworkPolicy"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   225  }
   226  
   227  func CiliumCIDRGroupResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumCIDRGroup], error) {
   228  	if !params.ClientSet.IsEnabled() {
   229  		return nil, nil
   230  	}
   231  	lw := utils.ListerWatcherWithModifiers(
   232  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumCIDRGroupList](params.ClientSet.CiliumV2alpha1().CiliumCIDRGroups()),
   233  		opts...,
   234  	)
   235  	return resource.New[*cilium_api_v2alpha1.CiliumCIDRGroup](params.Lifecycle, lw, resource.WithMetric("CiliumCIDRGroup"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   236  }
   237  
   238  func CiliumPodIPPoolResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumPodIPPool], error) {
   239  	if !params.ClientSet.IsEnabled() {
   240  		return nil, nil
   241  	}
   242  	lw := utils.ListerWatcherWithModifiers(
   243  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumPodIPPoolList](params.ClientSet.CiliumV2alpha1().CiliumPodIPPools()),
   244  		opts...,
   245  	)
   246  	return resource.New[*cilium_api_v2alpha1.CiliumPodIPPool](params.Lifecycle, lw, resource.WithMetric("CiliumPodIPPool"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   247  }
   248  
   249  func CiliumBGPPeeringPolicyResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumBGPPeeringPolicy], error) {
   250  	if !params.ClientSet.IsEnabled() {
   251  		return nil, nil
   252  	}
   253  
   254  	lw := utils.ListerWatcherWithModifiers(
   255  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumBGPPeeringPolicyList](params.ClientSet.CiliumV2alpha1().CiliumBGPPeeringPolicies()),
   256  		opts...,
   257  	)
   258  	return resource.New[*cilium_api_v2alpha1.CiliumBGPPeeringPolicy](params.Lifecycle, lw, resource.WithMetric("CiliumBGPPeeringPolicies"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   259  }
   260  
   261  func CiliumBGPNodeConfigResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumBGPNodeConfig], error) {
   262  	if !params.ClientSet.IsEnabled() {
   263  		return nil, nil
   264  	}
   265  
   266  	lw := utils.ListerWatcherWithModifiers(
   267  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumBGPNodeConfigList](params.ClientSet.CiliumV2alpha1().CiliumBGPNodeConfigs()),
   268  		opts...,
   269  	)
   270  	return resource.New[*cilium_api_v2alpha1.CiliumBGPNodeConfig](params.Lifecycle, lw, resource.WithMetric("CiliumBGPNodeConfig"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   271  }
   272  
   273  func CiliumBGPAdvertisementResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumBGPAdvertisement], error) {
   274  	if !params.ClientSet.IsEnabled() {
   275  		return nil, nil
   276  	}
   277  
   278  	lw := utils.ListerWatcherWithModifiers(
   279  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumBGPAdvertisementList](params.ClientSet.CiliumV2alpha1().CiliumBGPAdvertisements()),
   280  		opts...,
   281  	)
   282  	return resource.New[*cilium_api_v2alpha1.CiliumBGPAdvertisement](params.Lifecycle, lw, resource.WithMetric("CiliumBGPAdvertisement"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   283  }
   284  
   285  func CiliumBGPPeerConfigResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumBGPPeerConfig], error) {
   286  	if !params.ClientSet.IsEnabled() {
   287  		return nil, nil
   288  	}
   289  
   290  	lw := utils.ListerWatcherWithModifiers(
   291  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumBGPPeerConfigList](params.ClientSet.CiliumV2alpha1().CiliumBGPPeerConfigs()),
   292  		opts...,
   293  	)
   294  	return resource.New[*cilium_api_v2alpha1.CiliumBGPPeerConfig](params.Lifecycle, lw, resource.WithMetric("CiliumBGPPeerConfig"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   295  }
   296  
   297  func EndpointsResource(lc cell.Lifecycle, cfg Config, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*Endpoints], error) {
   298  	if !cs.IsEnabled() {
   299  		return nil, nil
   300  	}
   301  	endpointsOptsModifier, err := utils.GetServiceAndEndpointListOptionsModifier(cfg.K8sServiceProxyName)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	endpointSliceOptsModifier, err := utils.GetEndpointSliceListOptionsModifier()
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	lw := &endpointsListerWatcher{
   312  		cs:                          cs,
   313  		enableK8sEndpointSlice:      cfg.EnableK8sEndpointSlice,
   314  		endpointsOptsModifiers:      append(opts, endpointsOptsModifier),
   315  		endpointSlicesOptsModifiers: append(opts, endpointSliceOptsModifier),
   316  	}
   317  	return resource.New[*Endpoints](
   318  		lc,
   319  		lw,
   320  		resource.WithLazyTransform(lw.getSourceObj, transformEndpoint),
   321  		resource.WithMetric("Endpoint"),
   322  		resource.WithName("endpoints"),
   323  	), nil
   324  }
   325  
   326  // endpointsListerWatcher implements List and Watch for endpoints/endpointslices. It
   327  // performs the capability check on first call to List/Watch. This allows constructing
   328  // the resource before the client has been started and capabilities have been probed.
   329  type endpointsListerWatcher struct {
   330  	cs                          client.Clientset
   331  	enableK8sEndpointSlice      bool
   332  	endpointsOptsModifiers      []func(*metav1.ListOptions)
   333  	endpointSlicesOptsModifiers []func(*metav1.ListOptions)
   334  	sourceObj                   k8sRuntime.Object
   335  
   336  	once                sync.Once
   337  	cachedListerWatcher cache.ListerWatcher
   338  }
   339  
   340  func (lw *endpointsListerWatcher) getSourceObj() k8sRuntime.Object {
   341  	lw.getListerWatcher() // force the construction
   342  	return lw.sourceObj
   343  }
   344  
   345  func (lw *endpointsListerWatcher) getListerWatcher() cache.ListerWatcher {
   346  	lw.once.Do(func() {
   347  		if lw.enableK8sEndpointSlice && version.Capabilities().EndpointSlice {
   348  			if version.Capabilities().EndpointSliceV1 {
   349  				log.Info("Using discoveryv1.EndpointSlice")
   350  				lw.cachedListerWatcher = utils.ListerWatcherFromTyped[*slim_discoveryv1.EndpointSliceList](
   351  					lw.cs.Slim().DiscoveryV1().EndpointSlices(""),
   352  				)
   353  				lw.sourceObj = &slim_discoveryv1.EndpointSlice{}
   354  			} else {
   355  				log.Info("Using discoveryv1beta1.EndpointSlice")
   356  				lw.cachedListerWatcher = utils.ListerWatcherFromTyped[*slim_discoveryv1beta1.EndpointSliceList](
   357  					lw.cs.Slim().DiscoveryV1beta1().EndpointSlices(""),
   358  				)
   359  				lw.sourceObj = &slim_discoveryv1beta1.EndpointSlice{}
   360  			}
   361  			lw.cachedListerWatcher = utils.ListerWatcherWithModifiers(lw.cachedListerWatcher, lw.endpointSlicesOptsModifiers...)
   362  		} else {
   363  			log.Info("Using v1.Endpoints")
   364  			lw.cachedListerWatcher = utils.ListerWatcherFromTyped[*slim_corev1.EndpointsList](
   365  				lw.cs.Slim().CoreV1().Endpoints(""),
   366  			)
   367  			lw.sourceObj = &slim_corev1.Endpoints{}
   368  			lw.cachedListerWatcher = utils.ListerWatcherWithModifiers(lw.cachedListerWatcher, lw.endpointsOptsModifiers...)
   369  		}
   370  	})
   371  	return lw.cachedListerWatcher
   372  }
   373  
   374  func (lw *endpointsListerWatcher) List(opts metav1.ListOptions) (k8sRuntime.Object, error) {
   375  	return lw.getListerWatcher().List(opts)
   376  }
   377  
   378  func (lw *endpointsListerWatcher) Watch(opts metav1.ListOptions) (watch.Interface, error) {
   379  	return lw.getListerWatcher().Watch(opts)
   380  }
   381  
   382  func transformEndpoint(obj any) (any, error) {
   383  	switch obj := obj.(type) {
   384  	case *slim_corev1.Endpoints:
   385  		return ParseEndpoints(obj), nil
   386  	case *slim_discoveryv1.EndpointSlice:
   387  		return ParseEndpointSliceV1(obj), nil
   388  	case *slim_discoveryv1beta1.EndpointSlice:
   389  		return ParseEndpointSliceV1Beta1(obj), nil
   390  	default:
   391  		return nil, fmt.Errorf("%T not a known endpoint or endpoint slice object", obj)
   392  	}
   393  }
   394  
   395  // CiliumSlimEndpointResource uses the "localNode" IndexFunc to build the resource indexer.
   396  // The IndexFunc accesses the local node info to get its IP, so it depends on the local node store
   397  // to initialize it before the first access.
   398  // To reflect this, the node.LocalNodeStore dependency is explicitly requested in the function
   399  // signature.
   400  func CiliumSlimEndpointResource(params CiliumResourceParams, _ *node.LocalNodeStore, opts ...func(*metav1.ListOptions)) (resource.Resource[*types.CiliumEndpoint], error) {
   401  	if !params.ClientSet.IsEnabled() {
   402  		return nil, nil
   403  	}
   404  	lw := utils.ListerWatcherWithModifiers(
   405  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumEndpointList](params.ClientSet.CiliumV2().CiliumEndpoints(slim_corev1.NamespaceAll)),
   406  		opts...,
   407  	)
   408  	indexers := cache.Indexers{
   409  		"localNode": ciliumEndpointLocalPodIndexFunc,
   410  	}
   411  	return resource.New[*types.CiliumEndpoint](params.Lifecycle, lw,
   412  		resource.WithLazyTransform(func() k8sRuntime.Object {
   413  			return &cilium_api_v2.CiliumEndpoint{}
   414  		}, TransformToCiliumEndpoint),
   415  		resource.WithMetric("CiliumEndpoint"),
   416  		resource.WithIndexers(indexers),
   417  		resource.WithStoppableInformer(),
   418  		resource.WithCRDSync(params.CRDSyncPromise),
   419  	), nil
   420  }
   421  
   422  // ciliumEndpointLocalPodIndexFunc is an IndexFunc that indexes only local
   423  // CiliumEndpoints, by their local Node IP.
   424  func ciliumEndpointLocalPodIndexFunc(obj any) ([]string, error) {
   425  	cep, ok := obj.(*types.CiliumEndpoint)
   426  	if !ok {
   427  		return nil, fmt.Errorf("unexpected object type: %T", obj)
   428  	}
   429  	indices := []string{}
   430  	if cep.Networking == nil {
   431  		log.WithField("ciliumendpoint", cep.GetNamespace()+"/"+cep.GetName()).
   432  			Debug("cannot index CiliumEndpoint by node without network status")
   433  		return nil, nil
   434  	}
   435  	if cep.Networking.NodeIP == node.GetCiliumEndpointNodeIP() {
   436  		indices = append(indices, cep.Networking.NodeIP)
   437  	}
   438  	return indices, nil
   439  }
   440  
   441  // CiliumEndpointSliceResource uses the "localNode" IndexFunc to build the resource indexer.
   442  // The IndexFunc accesses the local node info to get its IP, so it depends on the local node store
   443  // to initialize it before the first access.
   444  // To reflect this, the node.LocalNodeStore dependency is explicitly requested in the function
   445  // signature.
   446  func CiliumEndpointSliceResource(params CiliumResourceParams, _ *node.LocalNodeStore, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2alpha1.CiliumEndpointSlice], error) {
   447  	if !params.ClientSet.IsEnabled() {
   448  		return nil, nil
   449  	}
   450  	lw := utils.ListerWatcherWithModifiers(
   451  		utils.ListerWatcherFromTyped[*cilium_api_v2alpha1.CiliumEndpointSliceList](params.ClientSet.CiliumV2alpha1().CiliumEndpointSlices()),
   452  		opts...,
   453  	)
   454  	indexers := cache.Indexers{
   455  		"localNode": ciliumEndpointSliceLocalPodIndexFunc,
   456  	}
   457  	return resource.New[*cilium_api_v2alpha1.CiliumEndpointSlice](params.Lifecycle, lw,
   458  		resource.WithMetric("CiliumEndpointSlice"),
   459  		resource.WithIndexers(indexers),
   460  		resource.WithStoppableInformer(),
   461  		resource.WithCRDSync(params.CRDSyncPromise),
   462  	), nil
   463  }
   464  
   465  // ciliumEndpointSliceLocalPodIndexFunc is an IndexFunc that indexes CiliumEndpointSlices
   466  // by their corresponding Pod, which are running locally on this Node.
   467  func ciliumEndpointSliceLocalPodIndexFunc(obj any) ([]string, error) {
   468  	ces, ok := obj.(*cilium_api_v2alpha1.CiliumEndpointSlice)
   469  	if !ok {
   470  		return nil, fmt.Errorf("unexpected object type: %T", obj)
   471  	}
   472  	indices := []string{}
   473  	for _, ep := range ces.Endpoints {
   474  		if ep.Networking.NodeIP == node.GetCiliumEndpointNodeIP() {
   475  			indices = append(indices, ep.Networking.NodeIP)
   476  			break
   477  		}
   478  	}
   479  	return indices, nil
   480  }
   481  
   482  func CiliumExternalWorkloads(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2.CiliumExternalWorkload], error) {
   483  	if !params.ClientSet.IsEnabled() {
   484  		return nil, nil
   485  	}
   486  	lw := utils.ListerWatcherWithModifiers(
   487  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumExternalWorkloadList](params.ClientSet.CiliumV2().CiliumExternalWorkloads()),
   488  		opts...,
   489  	)
   490  	return resource.New[*cilium_api_v2.CiliumExternalWorkload](params.Lifecycle, lw, resource.WithMetric("CiliumExternalWorkloads"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   491  }
   492  
   493  func CiliumEnvoyConfigResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2.CiliumEnvoyConfig], error) {
   494  	if !params.ClientSet.IsEnabled() {
   495  		return nil, nil
   496  	}
   497  	lw := utils.ListerWatcherWithModifiers(
   498  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumEnvoyConfigList](params.ClientSet.CiliumV2().CiliumEnvoyConfigs("")),
   499  		opts...,
   500  	)
   501  	return resource.New[*cilium_api_v2.CiliumEnvoyConfig](params.Lifecycle, lw, resource.WithMetric("CiliumEnvoyConfig"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   502  }
   503  
   504  func CiliumClusterwideEnvoyConfigResource(params CiliumResourceParams, opts ...func(*metav1.ListOptions)) (resource.Resource[*cilium_api_v2.CiliumClusterwideEnvoyConfig], error) {
   505  	if !params.ClientSet.IsEnabled() {
   506  		return nil, nil
   507  	}
   508  	lw := utils.ListerWatcherWithModifiers(
   509  		utils.ListerWatcherFromTyped[*cilium_api_v2.CiliumClusterwideEnvoyConfigList](params.ClientSet.CiliumV2().CiliumClusterwideEnvoyConfigs()),
   510  		opts...,
   511  	)
   512  	return resource.New[*cilium_api_v2.CiliumClusterwideEnvoyConfig](params.Lifecycle, lw, resource.WithMetric("CiliumClusterwideEnvoyConfig"), resource.WithCRDSync(params.CRDSyncPromise)), nil
   513  }