github.com/netdata/go.d.plugin@v0.58.1/agent/discovery/manager.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package discovery
     4  
     5  import (
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"log/slog"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/netdata/go.d.plugin/agent/confgroup"
    14  	"github.com/netdata/go.d.plugin/agent/discovery/dummy"
    15  	"github.com/netdata/go.d.plugin/agent/discovery/file"
    16  	"github.com/netdata/go.d.plugin/logger"
    17  )
    18  
    19  func NewManager(cfg Config) (*Manager, error) {
    20  	if err := validateConfig(cfg); err != nil {
    21  		return nil, fmt.Errorf("discovery manager config validation: %v", err)
    22  	}
    23  
    24  	mgr := &Manager{
    25  		Logger: logger.New().With(
    26  			slog.String("component", "discovery manager"),
    27  		),
    28  		send:        make(chan struct{}, 1),
    29  		sendEvery:   time.Second * 2, // timeout to aggregate changes
    30  		discoverers: make([]discoverer, 0),
    31  		mux:         &sync.RWMutex{},
    32  		cache:       newCache(),
    33  	}
    34  
    35  	if err := mgr.registerDiscoverers(cfg); err != nil {
    36  		return nil, fmt.Errorf("discovery manager initializaion: %v", err)
    37  	}
    38  
    39  	return mgr, nil
    40  }
    41  
    42  type discoverer interface {
    43  	Run(ctx context.Context, in chan<- []*confgroup.Group)
    44  }
    45  
    46  type Manager struct {
    47  	*logger.Logger
    48  	discoverers []discoverer
    49  	send        chan struct{}
    50  	sendEvery   time.Duration
    51  	mux         *sync.RWMutex
    52  	cache       *cache
    53  }
    54  
    55  func (m *Manager) String() string {
    56  	return fmt.Sprintf("discovery manager: %v", m.discoverers)
    57  }
    58  
    59  func (m *Manager) Add(d discoverer) {
    60  	m.discoverers = append(m.discoverers, d)
    61  }
    62  
    63  func (m *Manager) Run(ctx context.Context, in chan<- []*confgroup.Group) {
    64  	m.Info("instance is started")
    65  	defer func() { m.Info("instance is stopped") }()
    66  
    67  	var wg sync.WaitGroup
    68  
    69  	for _, d := range m.discoverers {
    70  		wg.Add(1)
    71  		go func(d discoverer) {
    72  			defer wg.Done()
    73  			m.runDiscoverer(ctx, d)
    74  		}(d)
    75  	}
    76  
    77  	wg.Add(1)
    78  	go func() {
    79  		defer wg.Done()
    80  		m.sendLoop(ctx, in)
    81  	}()
    82  
    83  	wg.Wait()
    84  	<-ctx.Done()
    85  }
    86  
    87  func (m *Manager) registerDiscoverers(cfg Config) error {
    88  	if len(cfg.File.Read) > 0 || len(cfg.File.Watch) > 0 {
    89  		cfg.File.Registry = cfg.Registry
    90  		d, err := file.NewDiscovery(cfg.File)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		m.Add(d)
    95  	}
    96  
    97  	if len(cfg.Dummy.Names) > 0 {
    98  		cfg.Dummy.Registry = cfg.Registry
    99  		d, err := dummy.NewDiscovery(cfg.Dummy)
   100  		if err != nil {
   101  			return err
   102  		}
   103  		m.Add(d)
   104  	}
   105  
   106  	if len(m.discoverers) == 0 {
   107  		return errors.New("zero registered discoverers")
   108  	}
   109  
   110  	m.Infof("registered discoverers: %v", m.discoverers)
   111  	return nil
   112  }
   113  
   114  func (m *Manager) runDiscoverer(ctx context.Context, d discoverer) {
   115  	updates := make(chan []*confgroup.Group)
   116  	go d.Run(ctx, updates)
   117  
   118  	for {
   119  		select {
   120  		case <-ctx.Done():
   121  			return
   122  		case groups, ok := <-updates:
   123  			if !ok {
   124  				return
   125  			}
   126  			func() {
   127  				m.mux.Lock()
   128  				defer m.mux.Unlock()
   129  
   130  				m.cache.update(groups)
   131  				m.triggerSend()
   132  			}()
   133  		}
   134  	}
   135  }
   136  
   137  func (m *Manager) sendLoop(ctx context.Context, in chan<- []*confgroup.Group) {
   138  	m.mustSend(ctx, in)
   139  
   140  	tk := time.NewTicker(m.sendEvery)
   141  	defer tk.Stop()
   142  
   143  	for {
   144  		select {
   145  		case <-ctx.Done():
   146  			return
   147  		case <-tk.C:
   148  			select {
   149  			case <-m.send:
   150  				m.trySend(in)
   151  			default:
   152  			}
   153  		}
   154  	}
   155  }
   156  
   157  func (m *Manager) mustSend(ctx context.Context, in chan<- []*confgroup.Group) {
   158  	select {
   159  	case <-ctx.Done():
   160  		return
   161  	case <-m.send:
   162  		m.mux.Lock()
   163  		groups := m.cache.groups()
   164  		m.cache.reset()
   165  		m.mux.Unlock()
   166  
   167  		select {
   168  		case <-ctx.Done():
   169  		case in <- groups:
   170  		}
   171  		return
   172  	}
   173  }
   174  
   175  func (m *Manager) trySend(in chan<- []*confgroup.Group) {
   176  	m.mux.Lock()
   177  	defer m.mux.Unlock()
   178  
   179  	select {
   180  	case in <- m.cache.groups():
   181  		m.cache.reset()
   182  	default:
   183  		m.triggerSend()
   184  	}
   185  }
   186  
   187  func (m *Manager) triggerSend() {
   188  	select {
   189  	case m.send <- struct{}{}:
   190  	default:
   191  	}
   192  }