master
go 137 lines 3.11 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package k8ssd
4
5 import (
6 "context"
7 "sort"
8 "testing"
9 "time"
10
11 "github.com/netdata/netdata/go/plugins/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 failed 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 }