github.com/kubeshop/testkube@v1.17.23/pkg/triggers/watcher_test.go (about)

     1  package triggers
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	corev1 "k8s.io/api/core/v1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/client-go/kubernetes/fake"
    12  
    13  	testtriggersv1 "github.com/kubeshop/testkube-operator/api/testtriggers/v1"
    14  	faketestkube "github.com/kubeshop/testkube-operator/pkg/clientset/versioned/fake"
    15  	"github.com/kubeshop/testkube/pkg/event/bus"
    16  	"github.com/kubeshop/testkube/pkg/log"
    17  )
    18  
    19  func TestService_runWatcher_lease(t *testing.T) {
    20  	t.Parallel()
    21  
    22  	t.Run("create and delete a test trigger", func(t *testing.T) {
    23  		t.Parallel()
    24  
    25  		clientset := fake.NewSimpleClientset()
    26  		testKubeClientset := faketestkube.NewSimpleClientset()
    27  
    28  		ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    29  		defer cancel()
    30  
    31  		s := &Service{
    32  			triggerStatus:     make(map[statusKey]*triggerStatus),
    33  			clientset:         clientset,
    34  			testKubeClientset: testKubeClientset,
    35  			logger:            log.DefaultLogger,
    36  			informers:         newK8sInformers(clientset, testKubeClientset, "", []string{}),
    37  			eventsBus:         &bus.EventBusMock{},
    38  		}
    39  
    40  		leaseChan := make(chan bool)
    41  		go func() { time.Sleep(50 * time.Millisecond); leaseChan <- true }()
    42  		go s.runWatcher(ctx, leaseChan)
    43  
    44  		time.Sleep(100 * time.Millisecond)
    45  
    46  		testNamespace := "testkube"
    47  		testTrigger := testtriggersv1.TestTrigger{
    48  			ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-trigger-1"},
    49  			Spec:       testtriggersv1.TestTriggerSpec{Event: "created"},
    50  		}
    51  		createdTestTrigger, err := testKubeClientset.TestsV1().TestTriggers(testNamespace).Create(ctx, &testTrigger, metav1.CreateOptions{})
    52  		assert.NotNil(t, createdTestTrigger)
    53  		assert.NoError(t, err)
    54  
    55  		time.Sleep(100 * time.Millisecond)
    56  
    57  		assert.Len(t, s.triggerStatus, 1)
    58  		key := newStatusKey(testNamespace, "test-trigger-1")
    59  		assert.Contains(t, s.triggerStatus, key)
    60  
    61  		err = testKubeClientset.TestsV1().TestTriggers(testNamespace).Delete(ctx, "test-trigger-1", metav1.DeleteOptions{})
    62  		assert.NoError(t, err)
    63  
    64  		time.Sleep(100 * time.Millisecond)
    65  
    66  		assert.Len(t, s.triggerStatus, 0)
    67  	})
    68  
    69  	t.Run("create a test trigger for pod created and match event on pod creation", func(t *testing.T) {
    70  		t.Parallel()
    71  
    72  		clientset := fake.NewSimpleClientset()
    73  		testKubeClientset := faketestkube.NewSimpleClientset()
    74  
    75  		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    76  		defer cancel()
    77  
    78  		testNamespace := "testkube"
    79  
    80  		match := false
    81  		testExecutorF := func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error {
    82  			assert.Equal(t, testNamespace, trigger.Namespace)
    83  			assert.Equal(t, "test-trigger-2", trigger.Name)
    84  			match = true
    85  			return nil
    86  		}
    87  		s := &Service{
    88  			triggerExecutor:   testExecutorF,
    89  			identifier:        "testkube-api",
    90  			clusterID:         "testkube",
    91  			triggerStatus:     make(map[statusKey]*triggerStatus),
    92  			clientset:         clientset,
    93  			testKubeClientset: testKubeClientset,
    94  			logger:            log.DefaultLogger,
    95  			informers:         newK8sInformers(clientset, testKubeClientset, "", []string{}),
    96  			eventsBus:         &bus.EventBusMock{},
    97  		}
    98  
    99  		leaseChan := make(chan bool)
   100  		go func() { time.Sleep(50 * time.Millisecond); leaseChan <- true }()
   101  		go s.runWatcher(ctx, leaseChan)
   102  
   103  		time.Sleep(50 * time.Millisecond)
   104  		leaseChan <- true
   105  		time.Sleep(50 * time.Millisecond)
   106  
   107  		testTrigger := testtriggersv1.TestTrigger{
   108  			ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-trigger-2"},
   109  			Spec: testtriggersv1.TestTriggerSpec{
   110  				Resource:          "pod",
   111  				ResourceSelector:  testtriggersv1.TestTriggerSelector{Name: "test-pod"},
   112  				Event:             "created",
   113  				Action:            "run",
   114  				Execution:         "test",
   115  				ConcurrencyPolicy: "allow",
   116  				TestSelector:      testtriggersv1.TestTriggerSelector{Name: "some-test"},
   117  			},
   118  		}
   119  		createdTestTrigger, err := testKubeClientset.TestsV1().TestTriggers(testNamespace).Create(ctx, &testTrigger, metav1.CreateOptions{})
   120  		assert.NotNil(t, createdTestTrigger)
   121  		assert.NoError(t, err)
   122  
   123  		time.Sleep(100 * time.Millisecond)
   124  
   125  		assert.Len(t, s.triggerStatus, 1)
   126  		key := newStatusKey(testNamespace, "test-trigger-2")
   127  		assert.Contains(t, s.triggerStatus, key)
   128  
   129  		testPod := corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-pod"}}
   130  		_, err = clientset.CoreV1().Pods(testNamespace).Create(ctx, &testPod, metav1.CreateOptions{})
   131  		assert.NoError(t, err)
   132  
   133  		time.Sleep(100 * time.Millisecond)
   134  		assert.True(t, match, "pod created event should match the test trigger condition")
   135  	})
   136  }
   137  
   138  func TestService_runWatcher_noLease(t *testing.T) {
   139  	t.Parallel()
   140  
   141  	t.Run("watcher will not start if lease is not acquired", func(t *testing.T) {
   142  		t.Parallel()
   143  
   144  		clientset := fake.NewSimpleClientset()
   145  		testKubeClientset := faketestkube.NewSimpleClientset()
   146  
   147  		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   148  		defer cancel()
   149  
   150  		s := &Service{
   151  			triggerStatus:     make(map[statusKey]*triggerStatus),
   152  			identifier:        "testkube-api",
   153  			clusterID:         "testkube",
   154  			clientset:         clientset,
   155  			testKubeClientset: testKubeClientset,
   156  			logger:            log.DefaultLogger,
   157  			informers:         newK8sInformers(clientset, testKubeClientset, "", []string{}),
   158  			eventsBus:         &bus.EventBusMock{},
   159  		}
   160  
   161  		leaseChan := make(chan bool)
   162  		go s.runWatcher(ctx, leaseChan)
   163  
   164  		time.Sleep(50 * time.Millisecond)
   165  		leaseChan <- false
   166  		time.Sleep(50 * time.Millisecond)
   167  
   168  		testNamespace := "testkube"
   169  		testTrigger := testtriggersv1.TestTrigger{
   170  			ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-trigger-1"},
   171  			Spec:       testtriggersv1.TestTriggerSpec{Event: "created"},
   172  		}
   173  		createdTestTrigger, err := testKubeClientset.TestsV1().TestTriggers(testNamespace).Create(ctx, &testTrigger, metav1.CreateOptions{})
   174  		assert.NotNil(t, createdTestTrigger)
   175  		assert.NoError(t, err)
   176  
   177  		time.Sleep(50 * time.Millisecond)
   178  
   179  		assert.Len(t, s.triggerStatus, 0)
   180  	})
   181  
   182  	t.Run("watcher should stop when lease is lost", func(t *testing.T) {
   183  		t.Parallel()
   184  
   185  		clientset := fake.NewSimpleClientset()
   186  		testKubeClientset := faketestkube.NewSimpleClientset()
   187  
   188  		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   189  		defer cancel()
   190  
   191  		s := &Service{
   192  			triggerStatus:     make(map[statusKey]*triggerStatus),
   193  			identifier:        "testkube-api",
   194  			clusterID:         "testkube",
   195  			clientset:         clientset,
   196  			testKubeClientset: testKubeClientset,
   197  			logger:            log.DefaultLogger,
   198  			informers:         newK8sInformers(clientset, testKubeClientset, "", []string{}),
   199  			eventsBus:         &bus.EventBusMock{},
   200  		}
   201  
   202  		leaseChan := make(chan bool)
   203  		go s.runWatcher(ctx, leaseChan)
   204  
   205  		time.Sleep(50 * time.Millisecond)
   206  		leaseChan <- true
   207  		time.Sleep(50 * time.Millisecond)
   208  		leaseChan <- false
   209  		time.Sleep(50 * time.Millisecond)
   210  
   211  		testNamespace := "testkube"
   212  		testTrigger := testtriggersv1.TestTrigger{
   213  			ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-trigger-1"},
   214  			Spec:       testtriggersv1.TestTriggerSpec{Event: "created"},
   215  		}
   216  		createdTestTrigger, err := testKubeClientset.TestsV1().TestTriggers(testNamespace).Create(ctx, &testTrigger, metav1.CreateOptions{})
   217  		assert.NotNil(t, createdTestTrigger)
   218  		assert.NoError(t, err)
   219  
   220  		time.Sleep(50 * time.Millisecond)
   221  
   222  		assert.Len(t, s.triggerStatus, 0)
   223  	})
   224  
   225  	t.Run("watcher should successfully restart on a newly acquired lease", func(t *testing.T) {
   226  		t.Parallel()
   227  
   228  		clientset := fake.NewSimpleClientset()
   229  		testKubeClientset := faketestkube.NewSimpleClientset()
   230  
   231  		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   232  		defer cancel()
   233  
   234  		s := &Service{
   235  			triggerStatus:     make(map[statusKey]*triggerStatus),
   236  			identifier:        "testkube-api",
   237  			clusterID:         "testkube",
   238  			clientset:         clientset,
   239  			testKubeClientset: testKubeClientset,
   240  			logger:            log.DefaultLogger,
   241  			informers:         newK8sInformers(clientset, testKubeClientset, "", []string{}),
   242  			eventsBus:         &bus.EventBusMock{},
   243  		}
   244  
   245  		leaseChan := make(chan bool)
   246  		go s.runWatcher(ctx, leaseChan)
   247  
   248  		time.Sleep(50 * time.Millisecond)
   249  		leaseChan <- true
   250  		time.Sleep(50 * time.Millisecond)
   251  		leaseChan <- false
   252  		time.Sleep(50 * time.Millisecond)
   253  		leaseChan <- true
   254  		time.Sleep(50 * time.Millisecond)
   255  
   256  		testNamespace := "testkube"
   257  		testTrigger := testtriggersv1.TestTrigger{
   258  			ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "test-trigger-1"},
   259  			Spec:       testtriggersv1.TestTriggerSpec{Event: "created"},
   260  		}
   261  		createdTestTrigger, err := testKubeClientset.TestsV1().TestTriggers(testNamespace).Create(ctx, &testTrigger, metav1.CreateOptions{})
   262  		assert.NotNil(t, createdTestTrigger)
   263  		assert.NoError(t, err)
   264  
   265  		time.Sleep(50 * time.Millisecond)
   266  
   267  		assert.Len(t, s.triggerStatus, 1)
   268  		key := newStatusKey(testNamespace, "test-trigger-1")
   269  		assert.Contains(t, s.triggerStatus, key)
   270  	})
   271  }