github.com/netdata/go.d.plugin@v0.58.1/modules/k8s_state/discover_kubernetes.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package k8s_state 4 5 import ( 6 "context" 7 "os" 8 "sync" 9 "time" 10 11 "github.com/netdata/go.d.plugin/logger" 12 13 corev1 "k8s.io/api/core/v1" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/runtime" 16 "k8s.io/apimachinery/pkg/watch" 17 "k8s.io/client-go/kubernetes" 18 "k8s.io/client-go/tools/cache" 19 "k8s.io/client-go/util/workqueue" 20 ) 21 22 func newKubeDiscovery(client kubernetes.Interface, l *logger.Logger) *kubeDiscovery { 23 return &kubeDiscovery{ 24 client: client, 25 Logger: l, 26 readyCh: make(chan struct{}), 27 stopCh: make(chan struct{}), 28 } 29 } 30 31 type kubeDiscovery struct { 32 *logger.Logger 33 client kubernetes.Interface 34 discoverers []discoverer 35 readyCh chan struct{} 36 stopCh chan struct{} 37 } 38 39 func (d *kubeDiscovery) run(ctx context.Context, in chan<- resource) { 40 d.Info("kube_discoverer is started") 41 defer func() { close(d.stopCh); d.Info("kube_discoverer is stopped") }() 42 43 d.discoverers = d.setupDiscoverers(ctx) 44 45 var wg sync.WaitGroup 46 updates := make(chan resource) 47 48 for _, dd := range d.discoverers { 49 wg.Add(1) 50 go func(dd discoverer) { defer wg.Done(); dd.run(ctx, updates) }(dd) 51 } 52 53 wg.Add(1) 54 go func() { defer wg.Done(); d.runDiscover(ctx, updates, in) }() 55 56 close(d.readyCh) 57 wg.Wait() 58 <-ctx.Done() 59 } 60 61 func (d *kubeDiscovery) ready() bool { 62 if !isChanClosed(d.readyCh) { 63 return false 64 } 65 for _, dd := range d.discoverers { 66 if !dd.ready() { 67 return false 68 } 69 } 70 return true 71 } 72 73 func (d *kubeDiscovery) stopped() bool { 74 if !isChanClosed(d.stopCh) { 75 return false 76 } 77 for _, dd := range d.discoverers { 78 if !dd.stopped() { 79 return false 80 } 81 } 82 return true 83 } 84 85 func (d *kubeDiscovery) runDiscover(ctx context.Context, updates chan resource, in chan<- resource) { 86 for { 87 select { 88 case <-ctx.Done(): 89 return 90 case r := <-updates: 91 select { 92 case <-ctx.Done(): 93 return 94 case in <- r: 95 } 96 } 97 } 98 } 99 100 const resyncPeriod = 10 * time.Minute 101 102 var ( 103 myNodeName = os.Getenv("MY_NODE_NAME") 104 ) 105 106 func (d *kubeDiscovery) setupDiscoverers(ctx context.Context) []discoverer { 107 node := d.client.CoreV1().Nodes() 108 nodeWatcher := &cache.ListWatch{ 109 ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { return node.List(ctx, options) }, 110 WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return node.Watch(ctx, options) }, 111 } 112 113 pod := d.client.CoreV1().Pods(corev1.NamespaceAll) 114 podWatcher := &cache.ListWatch{ 115 ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { 116 if myNodeName != "" { 117 options.FieldSelector = "spec.nodeName=" + myNodeName 118 } 119 return pod.List(ctx, options) 120 }, 121 WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { 122 if myNodeName != "" { 123 options.FieldSelector = "spec.nodeName=" + myNodeName 124 } 125 return pod.Watch(ctx, options) 126 }, 127 } 128 129 return []discoverer{ 130 newNodeDiscoverer(cache.NewSharedInformer(nodeWatcher, &corev1.Node{}, resyncPeriod), d.Logger), 131 newPodDiscoverer(cache.NewSharedInformer(podWatcher, &corev1.Pod{}, resyncPeriod), d.Logger), 132 } 133 } 134 135 func enqueue(queue *workqueue.Type, obj interface{}) { 136 key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) 137 if err != nil { 138 return 139 } 140 queue.Add(key) 141 } 142 143 func send(ctx context.Context, in chan<- resource, r resource) { 144 if r == nil { 145 return 146 } 147 select { 148 case <-ctx.Done(): 149 case in <- r: 150 } 151 } 152 153 func isChanClosed(ch chan struct{}) bool { 154 select { 155 case <-ch: 156 return true 157 default: 158 return false 159 } 160 }