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 }