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

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package kubernetes
     4  
     5  import (
     6  	"context"
     7  	"sort"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/netdata/go.d.plugin/agent/discovery/sd/model"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	"k8s.io/client-go/tools/cache"
    16  )
    17  
    18  const (
    19  	startWaitTimeout  = time.Second * 3
    20  	finishWaitTimeout = time.Second * 5
    21  )
    22  
    23  type discoverySim struct {
    24  	td               *KubeDiscoverer
    25  	runAfterSync     func(ctx context.Context)
    26  	sortBeforeVerify bool
    27  	wantTargetGroups []model.TargetGroup
    28  }
    29  
    30  func (sim discoverySim) run(t *testing.T) []model.TargetGroup {
    31  	t.Helper()
    32  	require.NotNil(t, sim.td)
    33  	require.NotEmpty(t, sim.wantTargetGroups)
    34  
    35  	in, out := make(chan []model.TargetGroup), make(chan []model.TargetGroup)
    36  	go sim.collectTargetGroups(t, in, out)
    37  
    38  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
    39  	defer cancel()
    40  	go sim.td.Discover(ctx, in)
    41  
    42  	select {
    43  	case <-sim.td.started:
    44  	case <-time.After(startWaitTimeout):
    45  		t.Fatalf("td %s filed to start in %s", sim.td.discoverers, startWaitTimeout)
    46  	}
    47  
    48  	synced := cache.WaitForCacheSync(ctx.Done(), sim.td.hasSynced)
    49  	require.Truef(t, synced, "td %s failed to sync", sim.td.discoverers)
    50  
    51  	if sim.runAfterSync != nil {
    52  		sim.runAfterSync(ctx)
    53  	}
    54  
    55  	groups := <-out
    56  
    57  	if sim.sortBeforeVerify {
    58  		sortTargetGroups(groups)
    59  	}
    60  
    61  	sim.verifyResult(t, groups)
    62  	return groups
    63  }
    64  
    65  func (sim discoverySim) collectTargetGroups(t *testing.T, in, out chan []model.TargetGroup) {
    66  	var tggs []model.TargetGroup
    67  loop:
    68  	for {
    69  		select {
    70  		case inGroups := <-in:
    71  			if tggs = append(tggs, inGroups...); len(tggs) >= len(sim.wantTargetGroups) {
    72  				break loop
    73  			}
    74  		case <-time.After(finishWaitTimeout):
    75  			t.Logf("td %s timed out after %s, got %d groups, expected %d, some events are skipped",
    76  				sim.td.discoverers, finishWaitTimeout, len(tggs), len(sim.wantTargetGroups))
    77  			break loop
    78  		}
    79  	}
    80  	out <- tggs
    81  }
    82  
    83  func (sim discoverySim) verifyResult(t *testing.T, result []model.TargetGroup) {
    84  	var expected, actual any
    85  
    86  	if len(sim.wantTargetGroups) == len(result) {
    87  		expected = sim.wantTargetGroups
    88  		actual = result
    89  	} else {
    90  		want := make(map[string]model.TargetGroup)
    91  		for _, group := range sim.wantTargetGroups {
    92  			want[group.Source()] = group
    93  		}
    94  		got := make(map[string]model.TargetGroup)
    95  		for _, group := range result {
    96  			got[group.Source()] = group
    97  		}
    98  		expected, actual = want, got
    99  	}
   100  
   101  	assert.Equal(t, expected, actual)
   102  }
   103  
   104  type hasSynced interface {
   105  	hasSynced() bool
   106  }
   107  
   108  var (
   109  	_ hasSynced = &KubeDiscoverer{}
   110  	_ hasSynced = &podDiscoverer{}
   111  	_ hasSynced = &serviceDiscoverer{}
   112  )
   113  
   114  func (d *KubeDiscoverer) hasSynced() bool {
   115  	for _, disc := range d.discoverers {
   116  		v, ok := disc.(hasSynced)
   117  		if !ok || !v.hasSynced() {
   118  			return false
   119  		}
   120  	}
   121  	return true
   122  }
   123  
   124  func (p *podDiscoverer) hasSynced() bool {
   125  	return p.podInformer.HasSynced() && p.cmapInformer.HasSynced() && p.secretInformer.HasSynced()
   126  }
   127  
   128  func (s *serviceDiscoverer) hasSynced() bool {
   129  	return s.informer.HasSynced()
   130  }
   131  
   132  func sortTargetGroups(tggs []model.TargetGroup) {
   133  	if len(tggs) == 0 {
   134  		return
   135  	}
   136  	sort.Slice(tggs, func(i, j int) bool { return tggs[i].Source() < tggs[j].Source() })
   137  }