diff --git a/Dockerfile b/Dockerfile index 5e6d70d..7898aba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ COPY . . RUN go build -o /bin/exclude-prefixes . FROM build as test -CMD go test -test.v ./... +CMD go test -test.v ./... -count=15000 -timeout 1000m FROM test as debug CMD dlv -l :40000 --headless=true --api-version=2 test -test.v ./... diff --git a/internal/prefixcollector/collector_configmap_output_test.go b/internal/prefixcollector/collector_configmap_output_test.go index 39b58ce..b26a8b5 100644 --- a/internal/prefixcollector/collector_configmap_output_test.go +++ b/internal/prefixcollector/collector_configmap_output_test.go @@ -21,25 +21,17 @@ package prefixcollector_test import ( "cmd-exclude-prefixes-k8s/internal/prefixcollector" "cmd-exclude-prefixes-k8s/internal/prefixcollector/prefixsource" - "cmd-exclude-prefixes-k8s/internal/utils" "context" - "fmt" "os" "path/filepath" "testing" - "time" - - "github.com/pkg/errors" "github.com/ghodss/yaml" "github.com/stretchr/testify/suite" - "go.uber.org/goleak" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" - k8stest "k8s.io/client-go/testing" ) const ( @@ -81,202 +73,6 @@ func (eps *ExcludedPrefixesSuite) TearDownTest() { eps.deleteConfigMap(context.Background(), configMapNamespace, userConfigMapName) } -func (eps *ExcludedPrefixesSuite) TestCollectorWithDummySources() { - defer goleak.VerifyNone(eps.T(), goleak.IgnoreCurrent()) - notifyChan := make(chan struct{}, 1) - ctx, cancel := context.WithCancel(prefixcollector.WithKubernetesInterface(context.Background(), eps.clientSet)) - defer cancel() - - sources := []prefixcollector.PrefixSource{ - newDummyPrefixSource( - []string{ - "127.0.0.1/16", - "127.0.2.1/16", - "168.92.0.1/24", - }, - ), - newDummyPrefixSource( - []string{ - "127.0.3.1/18", - "134.56.0.1/8", - "168.92.0.1/16", - }, - ), - } - - expectedResult := []string{ - "127.0.0.0/16", - "168.92.0.0/16", - "134.0.0.0/8", - } - - eps.testCollectorWithConfigmapOutput(ctx, notifyChan, expectedResult, sources) -} - -func (eps *ExcludedPrefixesSuite) TestConfigMapSource() { - defer goleak.VerifyNone(eps.T(), goleak.IgnoreCurrent()) - expectedResult := []string{ - "168.0.0.0/10", - "1.0.0.0/11", - } - notifyChan := make(chan struct{}, 1) - - ctx, cancel := context.WithCancel(prefixcollector.WithKubernetesInterface(context.Background(), eps.clientSet)) - defer cancel() - eps.createConfigMap(ctx, configMapNamespace, configMapPath) - - sources := []prefixcollector.PrefixSource{ - prefixsource.NewConfigMapPrefixSource( - ctx, - notifyChan, - userConfigMapName, - configMapNamespace, - userConfigMapKey, - ), - } - - eps.testCollectorWithConfigmapOutput(ctx, notifyChan, expectedResult, sources) -} - -func (eps *ExcludedPrefixesSuite) TestConfigMapSourceRefresh() { - defer goleak.VerifyNone(eps.T(), goleak.IgnoreCurrent()) - expectedResult := []string{ - "168.0.0.0/10", - "1.0.0.0/11", - } - notifyChan := make(chan struct{}, 1) - - ctx, cancel := context.WithCancel(prefixcollector.WithKubernetesInterface(context.Background(), eps.clientSet)) - defer cancel() - - clients := eps.clientSet.(*fake.Clientset) - countWatchers, stopAndDisableWatcher := interceptWatcher(clients) - - sources := []prefixcollector.PrefixSource{ - prefixsource.NewConfigMapPrefixSource( - ctx, - notifyChan, - userConfigMapName, - configMapNamespace, - userConfigMapKey, - ), - } - - eps.Eventually(func() bool { return countWatchers() == 1 }, time.Second, 10*time.Millisecond) - stopAndDisableWatcher() - eps.Eventually(func() bool { return countWatchers() == 2 }, time.Second, 10*time.Millisecond) - - eps.createConfigMap(ctx, configMapNamespace, configMapPath) - eps.testCollectorWithConfigmapOutput(ctx, notifyChan, expectedResult, sources) -} - -func (eps *ExcludedPrefixesSuite) TestKubeAdmConfigSource() { - defer goleak.VerifyNone(eps.T(), goleak.IgnoreCurrent()) - expectedResult := []string{ - "10.244.0.0/16", - "10.96.0.0/12", - } - - notifyChan := make(chan struct{}, 1) - ctx, cancel := context.WithCancel(prefixcollector.WithKubernetesInterface(context.Background(), eps.clientSet)) - defer cancel() - - eps.createConfigMap(ctx, prefixsource.KubeNamespace, kubeConfigMapPath) - - sources := []prefixcollector.PrefixSource{ - prefixsource.NewKubeAdmPrefixSource(ctx, notifyChan), - } - - eps.testCollectorWithConfigmapOutput(ctx, notifyChan, expectedResult, sources) -} - -func (eps *ExcludedPrefixesSuite) TestKubeAdmConfigSourceRefresh() { - defer goleak.VerifyNone(eps.T(), goleak.IgnoreCurrent()) - expectedResult := []string{ - "10.244.0.0/16", - "10.96.0.0/12", - } - - notifyChan := make(chan struct{}, 1) - ctx, cancel := context.WithCancel(prefixcollector.WithKubernetesInterface(context.Background(), eps.clientSet)) - defer cancel() - - clients := eps.clientSet.(*fake.Clientset) - countWatchers, stopAndDisableWatcher := interceptWatcher(clients) - - sources := []prefixcollector.PrefixSource{ - prefixsource.NewKubeAdmPrefixSource(ctx, notifyChan), - } - - eps.Eventually(func() bool { return countWatchers() == 1 }, time.Second, 10*time.Millisecond) - stopAndDisableWatcher() - eps.Eventually(func() bool { return countWatchers() == 2 }, time.Second, 10*time.Millisecond) - - eps.createConfigMap(ctx, prefixsource.KubeNamespace, kubeConfigMapPath) - eps.testCollectorWithConfigmapOutput(ctx, notifyChan, expectedResult, sources) -} - -func (eps *ExcludedPrefixesSuite) TestKubeAdmConfigSourceIPv6() { - defer goleak.VerifyNone(eps.T(), goleak.IgnoreCurrent()) - expectedResult := []string{ - "10.244.0.0/16", - "10.96.0.0/16", - "fd00:10:244::/56", - "fd00:10:96::/112", - } - - notifyChan := make(chan struct{}, 1) - ctx, cancel := context.WithCancel(prefixcollector.WithKubernetesInterface(context.Background(), eps.clientSet)) - defer cancel() - - eps.createConfigMap(ctx, prefixsource.KubeNamespace, kubeConfigMapPathIPv6) - - sources := []prefixcollector.PrefixSource{ - prefixsource.NewKubeAdmPrefixSource(ctx, notifyChan), - } - - eps.testCollectorWithConfigmapOutput(ctx, notifyChan, expectedResult, sources) -} - -func (eps *ExcludedPrefixesSuite) TestAllSources() { - defer goleak.VerifyNone(eps.T(), goleak.IgnoreCurrent()) - expectedResult := []string{ - "10.244.0.0/16", - "10.96.0.0/12", - "127.0.0.0/16", - "168.92.0.0/24", - "168.0.0.0/10", - "1.0.0.0/11", - } - - notifyChan := make(chan struct{}, 1) - ctx, cancel := context.WithCancel(prefixcollector.WithKubernetesInterface(context.Background(), eps.clientSet)) - defer cancel() - - eps.createConfigMap(ctx, prefixsource.KubeNamespace, kubeConfigMapPath) - eps.createConfigMap(ctx, configMapNamespace, configMapPath) - - sources := []prefixcollector.PrefixSource{ - newDummyPrefixSource( - []string{ - "127.0.0.1/16", - "127.0.2.1/16", - "168.92.0.1/24", - }, - ), - prefixsource.NewKubeAdmPrefixSource(ctx, notifyChan), - prefixsource.NewConfigMapPrefixSource( - ctx, - notifyChan, - userConfigMapName, - configMapNamespace, - userConfigMapKey, - ), - } - - eps.testCollectorWithConfigmapOutput(ctx, notifyChan, expectedResult, sources) -} - func TestExcludedPrefixesSuite(t *testing.T) { suite.Run(t, &ExcludedPrefixesSuite{}) } @@ -299,78 +95,6 @@ func (eps *ExcludedPrefixesSuite) createConfigMap(ctx context.Context, namespace } } -func (eps *ExcludedPrefixesSuite) testCollectorWithConfigmapOutput(ctx context.Context, notifyChan chan struct{}, - expectedResult []string, sources []prefixcollector.PrefixSource) { - collector := prefixcollector.NewExcludePrefixCollector( - prefixcollector.WithNotifyChan(notifyChan), - prefixcollector.WithConfigMapOutput(nsmConfigMapName, configMapNamespace, excludedPrefixesKey), - prefixcollector.WithSources(sources...), - ) - - ctx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - - errCh := eps.watchConfigMap(ctx, len(sources)+2) - - go collector.Serve(ctx) - - if err := <-errCh; err != nil { - eps.T().Fatal("Error watching config map: ", err) - } - - configMap, err := eps.clientSet.CoreV1().ConfigMaps(configMapNamespace).Get(ctx, nsmConfigMapName, metav1.GetOptions{}) - if err != nil { - eps.T().Fatal("Error getting nsm config map: ", err) - } - - prefixes, err := utils.YamlToPrefixes([]byte(configMap.Data[excludedPrefixesKey])) - if err != nil { - eps.T().Fatal("Error transforming yaml to prefixes: ", err) - } - - eps.Require().ElementsMatch(expectedResult, prefixes) -} - -func (eps *ExcludedPrefixesSuite) watchConfigMap(ctx context.Context, maxModifyCount int) <-chan error { - watcher, err := eps.clientSet.CoreV1().ConfigMaps(configMapNamespace).Watch(ctx, metav1.ListOptions{}) - if err != nil { - eps.T().Fatalf("Error watching configmap: %v", err) - } - - errorCh := make(chan error) - modifyCount := 0 - go func() { - for { - select { - case event, ok := <-watcher.ResultChan(): - if !ok { - errorCh <- errors.New("error watching configmap") - return - } - configMap := event.Object.(*v1.ConfigMap) - if event.Type == watch.Error { - errorCh <- errors.New("error watching configmap") - return - } - - if configMap.Name == nsmConfigMapName && (event.Type == watch.Added || event.Type == watch.Modified) { - modifyCount++ - fmt.Printf("event count : %v\n", modifyCount) - if modifyCount == maxModifyCount { - close(errorCh) - return - } - } - case <-ctx.Done(): - close(errorCh) - return - } - } - }() - - return errorCh -} - func getConfigMap(t *testing.T, filePath string) *v1.ConfigMap { destination := v1.ConfigMap{} bytes, err := os.ReadFile(filepath.Clean(filePath)) @@ -383,22 +107,3 @@ func getConfigMap(t *testing.T, filePath string) *v1.ConfigMap { return &destination } - -func interceptWatcher(clients *fake.Clientset) (getCount func() int, stopAndDisable func()) { - enable := true - count := 0 - watcher := watch.NewFake() - - var reactionFunc k8stest.WatchReactionFunc = func(action k8stest.Action) (bool, watch.Interface, error) { - count++ - return enable, watcher, nil - } - - clients.PrependWatchReactor("configmaps", reactionFunc) - - return func() int { return count }, - func() { - enable = false - watcher.Stop() - } -} diff --git a/internal/prefixcollector/collector_file_output_test.go b/internal/prefixcollector/collector_file_output_test.go index 4dff331..2360e04 100644 --- a/internal/prefixcollector/collector_file_output_test.go +++ b/internal/prefixcollector/collector_file_output_test.go @@ -90,7 +90,7 @@ func (eps *ExcludedPrefixesSuite) testCollectorWithFileOutput(ctx context.Contex ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - watcher, errCh := eps.watchFile(ctx, prefixesFilePath, len(sources)) + watcher, errCh := eps.watchFile(ctx, prefixesFilePath, len(sources)*2) go collector.Serve(ctx) @@ -140,6 +140,7 @@ func (eps *ExcludedPrefixesSuite) watchFile(ctx context.Context, prefixesFilePat close(errorCh) return } + if event.Op&fsnotify.Write == fsnotify.Write { modifyCount++ if modifyCount == maxModifyCount {