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  }