github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/k8smeta/k8s_meta_cache.go (about)

     1  package k8smeta
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	app "k8s.io/api/apps/v1"
     9  	batch "k8s.io/api/batch/v1"
    10  	v1 "k8s.io/api/core/v1"
    11  	networking "k8s.io/api/networking/v1"
    12  	storage "k8s.io/api/storage/v1"
    13  	meta "k8s.io/apimachinery/pkg/api/meta"
    14  	"k8s.io/apimachinery/pkg/runtime"
    15  	"k8s.io/client-go/informers"
    16  	"k8s.io/client-go/kubernetes"
    17  	"k8s.io/client-go/tools/cache"
    18  	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
    19  
    20  	"github.com/alibaba/ilogtail/pkg/logger"
    21  )
    22  
    23  const hostIPIndexPrefix = "host/"
    24  
    25  type k8sMetaCache struct {
    26  	metaStore *DeferredDeletionMetaStore
    27  	clientset *kubernetes.Clientset
    28  
    29  	eventCh chan *K8sMetaEvent
    30  	stopCh  chan struct{}
    31  
    32  	resourceType string
    33  	schema       *runtime.Scheme
    34  }
    35  
    36  func newK8sMetaCache(stopCh chan struct{}, resourceType string) *k8sMetaCache {
    37  	idxRules := getIdxRules(resourceType)
    38  	m := &k8sMetaCache{}
    39  	m.eventCh = make(chan *K8sMetaEvent, 100)
    40  	m.stopCh = stopCh
    41  	m.metaStore = NewDeferredDeletionMetaStore(m.eventCh, m.stopCh, 120, cache.MetaNamespaceKeyFunc, idxRules...)
    42  	m.resourceType = resourceType
    43  	m.schema = runtime.NewScheme()
    44  	_ = v1.AddToScheme(m.schema)
    45  	_ = batch.AddToScheme(m.schema)
    46  	_ = app.AddToScheme(m.schema)
    47  	_ = networking.AddToScheme(m.schema)
    48  	_ = storage.AddToScheme(m.schema)
    49  	return m
    50  }
    51  
    52  func (m *k8sMetaCache) init(clientset *kubernetes.Clientset) {
    53  	m.clientset = clientset
    54  	m.metaStore.Start()
    55  	m.watch(m.stopCh)
    56  }
    57  
    58  func (m *k8sMetaCache) Get(key []string) map[string][]*ObjectWrapper {
    59  	return m.metaStore.Get(key)
    60  }
    61  
    62  func (m *k8sMetaCache) GetSize() int {
    63  	return len(m.metaStore.Items)
    64  }
    65  
    66  func (m *k8sMetaCache) GetQueueSize() int {
    67  	return len(m.eventCh)
    68  }
    69  
    70  func (m *k8sMetaCache) List() []*ObjectWrapper {
    71  	return m.metaStore.List()
    72  }
    73  
    74  func (m *k8sMetaCache) Filter(filterFunc func(*ObjectWrapper) bool, limit int) []*ObjectWrapper {
    75  	return m.metaStore.Filter(filterFunc, limit)
    76  }
    77  
    78  func (m *k8sMetaCache) RegisterSendFunc(key string, sendFunc SendFunc, interval int) {
    79  	m.metaStore.RegisterSendFunc(key, sendFunc, interval)
    80  	logger.Debug(context.Background(), "register send func", m.resourceType)
    81  }
    82  
    83  func (m *k8sMetaCache) UnRegisterSendFunc(key string) {
    84  	m.metaStore.UnRegisterSendFunc(key)
    85  }
    86  
    87  func (m *k8sMetaCache) watch(stopCh <-chan struct{}) {
    88  	factory, informer := m.getFactoryInformer()
    89  	if informer == nil {
    90  		return
    91  	}
    92  	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
    93  		AddFunc: func(obj interface{}) {
    94  			nowTime := time.Now().Unix()
    95  			m.eventCh <- &K8sMetaEvent{
    96  				EventType: EventTypeAdd,
    97  				Object: &ObjectWrapper{
    98  					ResourceType:      m.resourceType,
    99  					Raw:               m.preProcess(obj),
   100  					FirstObservedTime: nowTime,
   101  					LastObservedTime:  nowTime,
   102  				},
   103  			}
   104  			metaManager.addEventCount.Add(1)
   105  		},
   106  		UpdateFunc: func(oldObj interface{}, obj interface{}) {
   107  			nowTime := time.Now().Unix()
   108  			m.eventCh <- &K8sMetaEvent{
   109  				EventType: EventTypeUpdate,
   110  				Object: &ObjectWrapper{
   111  					ResourceType:      m.resourceType,
   112  					Raw:               m.preProcess(obj),
   113  					FirstObservedTime: nowTime,
   114  					LastObservedTime:  nowTime,
   115  				},
   116  			}
   117  			metaManager.updateEventCount.Add(1)
   118  		},
   119  		DeleteFunc: func(obj interface{}) {
   120  			m.eventCh <- &K8sMetaEvent{
   121  				EventType: EventTypeDelete,
   122  				Object: &ObjectWrapper{
   123  					ResourceType:     m.resourceType,
   124  					Raw:              m.preProcess(obj),
   125  					LastObservedTime: time.Now().Unix(),
   126  				},
   127  			}
   128  			metaManager.deleteEventCount.Add(1)
   129  		},
   130  	})
   131  	go factory.Start(stopCh)
   132  	// wait infinite for first cache sync success
   133  	for {
   134  		if !cache.WaitForCacheSync(stopCh, informer.HasSynced) {
   135  			logger.Error(context.Background(), "K8S_META_CACHE_SYNC_TIMEOUT", "service cache sync timeout")
   136  			time.Sleep(1 * time.Second)
   137  		} else {
   138  			break
   139  		}
   140  	}
   141  }
   142  
   143  func (m *k8sMetaCache) getFactoryInformer() (informers.SharedInformerFactory, cache.SharedIndexInformer) {
   144  	var factory informers.SharedInformerFactory
   145  	switch m.resourceType {
   146  	case POD:
   147  		factory = informers.NewSharedInformerFactory(m.clientset, time.Hour*24)
   148  	default:
   149  		factory = informers.NewSharedInformerFactory(m.clientset, time.Hour*1)
   150  	}
   151  	var informer cache.SharedIndexInformer
   152  	switch m.resourceType {
   153  	case POD:
   154  		informer = factory.Core().V1().Pods().Informer()
   155  	case SERVICE:
   156  		informer = factory.Core().V1().Services().Informer()
   157  	case DEPLOYMENT:
   158  		informer = factory.Apps().V1().Deployments().Informer()
   159  	case REPLICASET:
   160  		informer = factory.Apps().V1().ReplicaSets().Informer()
   161  	case STATEFULSET:
   162  		informer = factory.Apps().V1().StatefulSets().Informer()
   163  	case DAEMONSET:
   164  		informer = factory.Apps().V1().DaemonSets().Informer()
   165  	case CRONJOB:
   166  		informer = factory.Batch().V1().CronJobs().Informer()
   167  	case JOB:
   168  		informer = factory.Batch().V1().Jobs().Informer()
   169  	case NODE:
   170  		informer = factory.Core().V1().Nodes().Informer()
   171  	case NAMESPACE:
   172  		informer = factory.Core().V1().Namespaces().Informer()
   173  	case CONFIGMAP:
   174  		informer = factory.Core().V1().ConfigMaps().Informer()
   175  	case PERSISTENTVOLUME:
   176  		informer = factory.Core().V1().PersistentVolumes().Informer()
   177  	case PERSISTENTVOLUMECLAIM:
   178  		informer = factory.Core().V1().PersistentVolumeClaims().Informer()
   179  	case STORAGECLASS:
   180  		informer = factory.Storage().V1().StorageClasses().Informer()
   181  	case INGRESS:
   182  		informer = factory.Networking().V1().Ingresses().Informer()
   183  	default:
   184  		logger.Error(context.Background(), "ENTITY_PIPELINE_REGISTER_ERROR", "resourceType not support", m.resourceType)
   185  		return factory, nil
   186  	}
   187  	return factory, informer
   188  }
   189  
   190  func getIdxRules(resourceType string) []IdxFunc {
   191  	switch resourceType {
   192  	case NODE:
   193  		return []IdxFunc{generateNodeKey}
   194  	case POD:
   195  		return []IdxFunc{generateCommonKey, generatePodIPKey, generateContainerIDKey, generateHostIPKey}
   196  	case SERVICE:
   197  		return []IdxFunc{generateCommonKey, generateServiceIPKey}
   198  	default:
   199  		return []IdxFunc{generateCommonKey}
   200  	}
   201  }
   202  
   203  func (m *k8sMetaCache) preProcess(obj interface{}) interface{} {
   204  	switch m.resourceType {
   205  	case POD:
   206  		return m.preProcessPod(obj)
   207  	default:
   208  		return m.preProcessCommon(obj)
   209  	}
   210  }
   211  
   212  func (m *k8sMetaCache) preProcessCommon(obj interface{}) interface{} {
   213  	runtimeObj, ok := obj.(runtime.Object)
   214  	if !ok {
   215  		logger.Error(context.Background(), "K8S_META_PRE_PROCESS_ERROR", "object is not runtime object", obj)
   216  		return obj
   217  	}
   218  	metaObj, err := meta.Accessor(runtimeObj)
   219  	if err != nil {
   220  		logger.Error(context.Background(), "K8S_META_PRE_PROCESS_ERROR", "object is not meta object", err)
   221  		return obj
   222  	}
   223  	// fill empty kind
   224  	if runtimeObj.GetObjectKind().GroupVersionKind().Empty() {
   225  		gvk, err := apiutil.GVKForObject(runtimeObj, m.schema)
   226  		if err != nil {
   227  			logger.Error(context.Background(), "K8S_META_PRE_PROCESS_ERROR", "get GVK for object error", err)
   228  			return obj
   229  		}
   230  		runtimeObj.GetObjectKind().SetGroupVersionKind(gvk)
   231  	}
   232  	// remove unnecessary annotations
   233  	if metaObj.GetAnnotations() != nil {
   234  		if _, ok := metaObj.GetAnnotations()["kubectl.kubernetes.io/last-applied-configuration"]; ok {
   235  			metaObj.GetAnnotations()["kubectl.kubernetes.io/last-applied-configuration"] = ""
   236  		}
   237  	}
   238  	return runtimeObj
   239  }
   240  
   241  func (m *k8sMetaCache) preProcessPod(obj interface{}) interface{} {
   242  	m.preProcessCommon(obj)
   243  	pod, ok := obj.(*v1.Pod)
   244  	if !ok {
   245  		logger.Error(context.Background(), "K8S_META_PRE_PROCESS_ERROR", "object is not pod", obj)
   246  		return obj
   247  	}
   248  	pod.ManagedFields = nil
   249  	pod.Status.Conditions = nil
   250  	pod.Spec.Tolerations = nil
   251  	return pod
   252  }
   253  
   254  func generateCommonKey(obj interface{}) ([]string, error) {
   255  	meta, err := meta.Accessor(obj)
   256  	if err != nil {
   257  		return []string{}, err
   258  	}
   259  	return []string{generateNameWithNamespaceKey(meta.GetNamespace(), meta.GetName())}, nil
   260  }
   261  
   262  func generateNodeKey(obj interface{}) ([]string, error) {
   263  	node, err := meta.Accessor(obj)
   264  	if err != nil {
   265  		return []string{}, err
   266  	}
   267  	return []string{node.GetName()}, nil
   268  }
   269  
   270  func generateNameWithNamespaceKey(namespace, name string) string {
   271  	return fmt.Sprintf("%s/%s", namespace, name)
   272  }
   273  
   274  func generatePodIPKey(obj interface{}) ([]string, error) {
   275  	pod, ok := obj.(*v1.Pod)
   276  	if !ok {
   277  		return []string{}, fmt.Errorf("object is not a pod")
   278  	}
   279  	return []string{pod.Status.PodIP}, nil
   280  }
   281  
   282  func generateContainerIDKey(obj interface{}) ([]string, error) {
   283  	pod, ok := obj.(*v1.Pod)
   284  	if !ok {
   285  		return []string{}, fmt.Errorf("object is not a pod")
   286  	}
   287  	result := make([]string, len(pod.Status.ContainerStatuses))
   288  	for i, containerStatus := range pod.Status.ContainerStatuses {
   289  		result[i] = truncateContainerID(containerStatus.ContainerID)
   290  	}
   291  	return result, nil
   292  }
   293  
   294  func generateHostIPKey(obj interface{}) ([]string, error) {
   295  	pod, ok := obj.(*v1.Pod)
   296  	if !ok {
   297  		return []string{}, fmt.Errorf("object is not a pod")
   298  	}
   299  	return []string{addHostIPIndexPrefex(pod.Status.HostIP)}, nil
   300  }
   301  
   302  func addHostIPIndexPrefex(ip string) string {
   303  	return hostIPIndexPrefix + ip
   304  }
   305  
   306  func generateServiceIPKey(obj interface{}) ([]string, error) {
   307  	svc, ok := obj.(*v1.Service)
   308  	if !ok {
   309  		return []string{}, fmt.Errorf("object is not a service")
   310  	}
   311  	results := make([]string, 0)
   312  	for _, ip := range svc.Spec.ClusterIPs {
   313  		if ip != "" {
   314  			results = append(results, ip)
   315  		}
   316  	}
   317  	for _, ip := range svc.Spec.ExternalIPs {
   318  		if ip != "" {
   319  			results = append(results, ip)
   320  		}
   321  	}
   322  	if svc.Spec.LoadBalancerIP != "" {
   323  		results = append(results, svc.Spec.LoadBalancerIP)
   324  	}
   325  	return results, nil
   326  }