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

     1  package triggers
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	appsv1 "k8s.io/api/apps/v1"
    10  	corev1 "k8s.io/api/core/v1"
    11  	networkingv1 "k8s.io/api/networking/v1"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/client-go/informers"
    14  	appsinformerv1 "k8s.io/client-go/informers/apps/v1"
    15  	coreinformerv1 "k8s.io/client-go/informers/core/v1"
    16  	networkinginformerv1 "k8s.io/client-go/informers/networking/v1"
    17  	"k8s.io/client-go/kubernetes"
    18  	"k8s.io/client-go/tools/cache"
    19  
    20  	executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1"
    21  	testsourcev1 "github.com/kubeshop/testkube-operator/api/testsource/v1"
    22  	"github.com/kubeshop/testkube/pkg/tcl/testworkflowstcl/testworkflowprocessor/constants"
    23  
    24  	testsv3 "github.com/kubeshop/testkube-operator/api/tests/v3"
    25  
    26  	testsuitev3 "github.com/kubeshop/testkube-operator/api/testsuite/v3"
    27  	testtriggersv1 "github.com/kubeshop/testkube-operator/api/testtriggers/v1"
    28  	"github.com/kubeshop/testkube-operator/pkg/clientset/versioned"
    29  	"github.com/kubeshop/testkube-operator/pkg/informers/externalversions"
    30  	testkubeexecutorinformerv1 "github.com/kubeshop/testkube-operator/pkg/informers/externalversions/executor/v1"
    31  	testkubeinformerv1 "github.com/kubeshop/testkube-operator/pkg/informers/externalversions/tests/v1"
    32  
    33  	testkubeinformerv3 "github.com/kubeshop/testkube-operator/pkg/informers/externalversions/tests/v3"
    34  	"github.com/kubeshop/testkube-operator/pkg/validation/tests/v1/testtrigger"
    35  	"github.com/kubeshop/testkube/pkg/api/v1/testkube"
    36  	"github.com/kubeshop/testkube/pkg/executor"
    37  	cexecutor "github.com/kubeshop/testkube/pkg/executor/containerexecutor"
    38  )
    39  
    40  type k8sInformers struct {
    41  	podInformers          []coreinformerv1.PodInformer
    42  	deploymentInformers   []appsinformerv1.DeploymentInformer
    43  	daemonsetInformers    []appsinformerv1.DaemonSetInformer
    44  	statefulsetInformers  []appsinformerv1.StatefulSetInformer
    45  	serviceInformers      []coreinformerv1.ServiceInformer
    46  	ingressInformers      []networkinginformerv1.IngressInformer
    47  	clusterEventInformers []coreinformerv1.EventInformer
    48  	configMapInformers    []coreinformerv1.ConfigMapInformer
    49  
    50  	testTriggerInformer testkubeinformerv1.TestTriggerInformer
    51  	testSuiteInformer   testkubeinformerv3.TestSuiteInformer
    52  	testInformer        testkubeinformerv3.TestInformer
    53  	executorInformer    testkubeexecutorinformerv1.ExecutorInformer
    54  	webhookInformer     testkubeexecutorinformerv1.WebhookInformer
    55  	testSourceInformer  testkubeinformerv1.TestSourceInformer
    56  }
    57  
    58  func newK8sInformers(clientset kubernetes.Interface, testKubeClientset versioned.Interface,
    59  	testkubeNamespace string, watcherNamespaces []string) *k8sInformers {
    60  	var k8sInformers k8sInformers
    61  	if len(watcherNamespaces) == 0 {
    62  		watcherNamespaces = append(watcherNamespaces, metav1.NamespaceAll)
    63  	}
    64  
    65  	for _, namespace := range watcherNamespaces {
    66  		f := informers.NewSharedInformerFactoryWithOptions(clientset, 0, informers.WithNamespace(namespace))
    67  		k8sInformers.podInformers = append(k8sInformers.podInformers, f.Core().V1().Pods())
    68  		k8sInformers.deploymentInformers = append(k8sInformers.deploymentInformers, f.Apps().V1().Deployments())
    69  		k8sInformers.daemonsetInformers = append(k8sInformers.daemonsetInformers, f.Apps().V1().DaemonSets())
    70  		k8sInformers.statefulsetInformers = append(k8sInformers.statefulsetInformers, f.Apps().V1().StatefulSets())
    71  		k8sInformers.serviceInformers = append(k8sInformers.serviceInformers, f.Core().V1().Services())
    72  		k8sInformers.ingressInformers = append(k8sInformers.ingressInformers, f.Networking().V1().Ingresses())
    73  		k8sInformers.clusterEventInformers = append(k8sInformers.clusterEventInformers, f.Core().V1().Events())
    74  		k8sInformers.configMapInformers = append(k8sInformers.configMapInformers, f.Core().V1().ConfigMaps())
    75  	}
    76  
    77  	testkubeInformerFactory := externalversions.NewSharedInformerFactoryWithOptions(
    78  		testKubeClientset, 0, externalversions.WithNamespace(testkubeNamespace))
    79  	k8sInformers.testTriggerInformer = testkubeInformerFactory.Tests().V1().TestTriggers()
    80  	k8sInformers.testSuiteInformer = testkubeInformerFactory.Tests().V3().TestSuites()
    81  	k8sInformers.testInformer = testkubeInformerFactory.Tests().V3().Tests()
    82  	k8sInformers.executorInformer = testkubeInformerFactory.Executor().V1().Executor()
    83  	k8sInformers.webhookInformer = testkubeInformerFactory.Executor().V1().Webhook()
    84  	k8sInformers.testSourceInformer = testkubeInformerFactory.Tests().V1().TestSource()
    85  
    86  	return &k8sInformers
    87  }
    88  
    89  func (s *Service) runWatcher(ctx context.Context, leaseChan chan bool) {
    90  	running := false
    91  	var stopChan chan struct{}
    92  
    93  	for {
    94  		select {
    95  		case <-ctx.Done():
    96  			s.logger.Infof("trigger service: stopping watcher component: context finished")
    97  			if _, ok := <-stopChan; ok {
    98  				close(stopChan)
    99  			}
   100  			return
   101  		case leased := <-leaseChan:
   102  			if !leased {
   103  				if running {
   104  					s.logger.Infof("trigger service: instance %s in cluster %s lost lease", s.identifier, s.clusterID)
   105  					close(stopChan)
   106  					s.informers = nil
   107  					running = false
   108  				}
   109  			} else {
   110  				if !running {
   111  					s.logger.Infof("trigger service: instance %s in cluster %s acquired lease", s.identifier, s.clusterID)
   112  					s.informers = newK8sInformers(s.clientset, s.testKubeClientset, s.testkubeNamespace, s.watcherNamespaces)
   113  					stopChan = make(chan struct{})
   114  					s.runInformers(ctx, stopChan)
   115  					running = true
   116  				}
   117  			}
   118  		}
   119  	}
   120  }
   121  
   122  func (s *Service) runInformers(ctx context.Context, stop <-chan struct{}) {
   123  	if s.informers == nil {
   124  		s.logger.Errorf("trigger service: error running k8s informers: informers are nil")
   125  		return
   126  	}
   127  
   128  	for i := range s.informers.podInformers {
   129  		s.informers.podInformers[i].Informer().AddEventHandler(s.podEventHandler(ctx))
   130  	}
   131  
   132  	for i := range s.informers.deploymentInformers {
   133  		s.informers.deploymentInformers[i].Informer().AddEventHandler(s.deploymentEventHandler(ctx))
   134  	}
   135  
   136  	for i := range s.informers.daemonsetInformers {
   137  		s.informers.daemonsetInformers[i].Informer().AddEventHandler(s.daemonSetEventHandler(ctx))
   138  	}
   139  
   140  	for i := range s.informers.statefulsetInformers {
   141  		s.informers.statefulsetInformers[i].Informer().AddEventHandler(s.statefulSetEventHandler(ctx))
   142  	}
   143  
   144  	for i := range s.informers.serviceInformers {
   145  		s.informers.serviceInformers[i].Informer().AddEventHandler(s.serviceEventHandler(ctx))
   146  	}
   147  
   148  	for i := range s.informers.ingressInformers {
   149  		s.informers.ingressInformers[i].Informer().AddEventHandler(s.ingressEventHandler(ctx))
   150  	}
   151  
   152  	for i := range s.informers.clusterEventInformers {
   153  		s.informers.clusterEventInformers[i].Informer().AddEventHandler(s.clusterEventEventHandler(ctx))
   154  	}
   155  
   156  	for i := range s.informers.configMapInformers {
   157  		s.informers.configMapInformers[i].Informer().AddEventHandler(s.configMapEventHandler(ctx))
   158  	}
   159  
   160  	s.informers.testTriggerInformer.Informer().AddEventHandler(s.testTriggerEventHandler())
   161  	s.informers.testSuiteInformer.Informer().AddEventHandler(s.testSuiteEventHandler())
   162  	s.informers.testInformer.Informer().AddEventHandler(s.testEventHandler())
   163  	s.informers.executorInformer.Informer().AddEventHandler(s.executorEventHandler())
   164  	s.informers.webhookInformer.Informer().AddEventHandler(s.webhookEventHandler())
   165  	s.informers.testSourceInformer.Informer().AddEventHandler(s.testSourceEventHandler())
   166  
   167  	s.logger.Debugf("trigger service: starting pod informers")
   168  	for i := range s.informers.podInformers {
   169  		go s.informers.podInformers[i].Informer().Run(stop)
   170  	}
   171  
   172  	s.logger.Debugf("trigger service: starting deployment informers")
   173  	for i := range s.informers.deploymentInformers {
   174  		go s.informers.deploymentInformers[i].Informer().Run(stop)
   175  	}
   176  
   177  	s.logger.Debugf("trigger service: starting daemonset informers")
   178  	for i := range s.informers.daemonsetInformers {
   179  		go s.informers.daemonsetInformers[i].Informer().Run(stop)
   180  	}
   181  
   182  	s.logger.Debugf("trigger service: starting statefulset informers")
   183  	for i := range s.informers.statefulsetInformers {
   184  		go s.informers.statefulsetInformers[i].Informer().Run(stop)
   185  	}
   186  
   187  	s.logger.Debugf("trigger service: starting service informers")
   188  	for i := range s.informers.serviceInformers {
   189  		go s.informers.serviceInformers[i].Informer().Run(stop)
   190  	}
   191  
   192  	s.logger.Debugf("trigger service: starting ingress informers")
   193  	for i := range s.informers.ingressInformers {
   194  		go s.informers.ingressInformers[i].Informer().Run(stop)
   195  	}
   196  
   197  	s.logger.Debugf("trigger service: starting cluster event informers")
   198  	for i := range s.informers.clusterEventInformers {
   199  		go s.informers.clusterEventInformers[i].Informer().Run(stop)
   200  	}
   201  
   202  	s.logger.Debugf("trigger service: starting config map informers")
   203  	for i := range s.informers.configMapInformers {
   204  		go s.informers.configMapInformers[i].Informer().Run(stop)
   205  	}
   206  
   207  	s.logger.Debugf("trigger service: starting test trigger informer")
   208  	go s.informers.testTriggerInformer.Informer().Run(stop)
   209  	s.logger.Debugf("trigger service: starting test suite informer")
   210  	go s.informers.testSuiteInformer.Informer().Run(stop)
   211  	s.logger.Debugf("trigger service: starting test informer")
   212  	go s.informers.testInformer.Informer().Run(stop)
   213  	s.logger.Debugf("trigger service: starting executor informer")
   214  	go s.informers.executorInformer.Informer().Run(stop)
   215  	s.logger.Debugf("trigger service: starting webhook informer")
   216  	go s.informers.webhookInformer.Informer().Run(stop)
   217  	s.logger.Debugf("trigger service: starting test source informer")
   218  	go s.informers.testSourceInformer.Informer().Run(stop)
   219  }
   220  
   221  func (s *Service) podEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
   222  	getConditions := func(object metav1.Object) func() ([]testtriggersv1.TestTriggerCondition, error) {
   223  		return func() ([]testtriggersv1.TestTriggerCondition, error) {
   224  			return getPodConditions(ctx, s.clientset, object)
   225  		}
   226  	}
   227  	getAddrress := func(object metav1.Object) func(c context.Context, delay time.Duration) (string, error) {
   228  		return func(c context.Context, delay time.Duration) (string, error) {
   229  			return getPodAdress(c, s.clientset, object, delay)
   230  		}
   231  	}
   232  	return cache.ResourceEventHandlerFuncs{
   233  		AddFunc: func(obj any) {
   234  			pod, ok := obj.(*corev1.Pod)
   235  			if !ok {
   236  				s.logger.Errorf("failed to process create pod event due to it being an unexpected type, received type %+v", obj)
   237  				return
   238  			}
   239  			if inPast(pod.CreationTimestamp.Time, s.watchFromDate) {
   240  				s.logger.Debugf(
   241  					"trigger service: watcher component: no-op create trigger: pod %s/%s was created in the past",
   242  					pod.Namespace, pod.Name,
   243  				)
   244  				return
   245  			}
   246  			s.logger.Debugf("trigger service: watcher component: emiting event: pod %s/%s created", pod.Namespace, pod.Name)
   247  			event := newWatcherEvent(testtrigger.EventCreated, pod, testtrigger.ResourcePod,
   248  				withConditionsGetter(getConditions(pod)), withAddressGetter(getAddrress(pod)))
   249  			if err := s.match(ctx, event); err != nil {
   250  				s.logger.Errorf("event matcher returned an error while matching create pod event: %v", err)
   251  			}
   252  
   253  		},
   254  		UpdateFunc: func(oldObj, newObj any) {
   255  			oldPod, ok := oldObj.(*corev1.Pod)
   256  			if !ok {
   257  				s.logger.Errorf("failed to process update pod event due to it being an unexpected type, received type %+v", oldObj)
   258  				return
   259  			}
   260  			if inPast(oldPod.CreationTimestamp.Time, s.watchFromDate) {
   261  				s.logger.Debugf(
   262  					"trigger service: watcher component: no-op update trigger: pod %s/%s was updated in the past",
   263  					oldPod.Namespace, oldPod.Name,
   264  				)
   265  				return
   266  			}
   267  
   268  			newPod, ok := newObj.(*corev1.Pod)
   269  			if !ok {
   270  				s.logger.Errorf("failed to process update pod event due to it being an unexpected type, received type %+v", newObj)
   271  				return
   272  			}
   273  			if inPast(newPod.CreationTimestamp.Time, s.watchFromDate) {
   274  				s.logger.Debugf(
   275  					"trigger service: watcher component: no-op update trigger: pod %s/%s was updated in the past",
   276  					newPod.Namespace, newPod.Name,
   277  				)
   278  				return
   279  			}
   280  			if oldPod.Namespace == s.testkubeNamespace && oldPod.Labels["job-name"] != "" && oldPod.Labels[testkube.TestLabelTestName] != "" &&
   281  				newPod.Namespace == s.testkubeNamespace && newPod.Labels["job-name"] != "" && newPod.Labels[testkube.TestLabelTestName] != "" &&
   282  				!(strings.HasSuffix(oldPod.Name, cexecutor.ScraperPodSuffix) || strings.HasSuffix(newPod.Name, cexecutor.ScraperPodSuffix)) &&
   283  				oldPod.Labels["job-name"] == newPod.Labels["job-name"] {
   284  				s.checkExecutionPodStatus(ctx, oldPod.Labels["job-name"], []*corev1.Pod{oldPod, newPod})
   285  			}
   286  		},
   287  		DeleteFunc: func(obj interface{}) {
   288  			pod, ok := obj.(*corev1.Pod)
   289  			if !ok {
   290  				s.logger.Errorf("failed to process delete pod event due to it being an unexpected type, received type %+v", obj)
   291  				return
   292  			}
   293  			s.logger.Debugf("trigger service: watcher component: emiting event: pod %s/%s deleted", pod.Namespace, pod.Name)
   294  			if pod.Namespace == s.testkubeNamespace && pod.Labels["job-name"] != "" && !strings.HasSuffix(pod.Name, cexecutor.ScraperPodSuffix) &&
   295  				pod.Labels[testkube.TestLabelTestName] != "" {
   296  				s.checkExecutionPodStatus(ctx, pod.Labels["job-name"], []*corev1.Pod{pod})
   297  			}
   298  			event := newWatcherEvent(testtrigger.EventDeleted, pod, testtrigger.ResourcePod,
   299  				withConditionsGetter(getConditions(pod)), withAddressGetter(getAddrress(pod)))
   300  			if err := s.match(ctx, event); err != nil {
   301  				s.logger.Errorf("event matcher returned an error while matching delete pod event: %v", err)
   302  			}
   303  		},
   304  	}
   305  }
   306  
   307  func (s *Service) checkExecutionPodStatus(ctx context.Context, executionID string, pods []*corev1.Pod) error {
   308  	if len(pods) > 0 && pods[0].Labels[constants.ExecutionIdLabelName] != "" {
   309  		return nil
   310  	}
   311  	execution, err := s.resultRepository.Get(ctx, executionID)
   312  	if err != nil {
   313  		s.logger.Errorf("get execution returned an error %v while looking for execution id: %s", err, executionID)
   314  		return err
   315  	}
   316  
   317  	if execution.ExecutionResult.IsRunning() || execution.ExecutionResult.IsQueued() {
   318  		errorMessage := ""
   319  		for _, pod := range pods {
   320  			if exitCode := executor.GetPodExitCode(pod); pod.Status.Phase == corev1.PodFailed || exitCode != 0 {
   321  				errorMessage = executor.GetPodErrorMessage(ctx, s.clientset, pod)
   322  				break
   323  			}
   324  		}
   325  
   326  		if errorMessage != "" {
   327  			s.logger.Infow("execution pod failed with error message", "executionId", executionID, "message", execution.ExecutionResult.ErrorMessage)
   328  			execution.ExecutionResult.Error()
   329  			if execution.ExecutionResult.ErrorMessage != "" {
   330  				execution.ExecutionResult.ErrorMessage += "\n"
   331  			}
   332  
   333  			execution.ExecutionResult.ErrorMessage += errorMessage
   334  			test, err := s.testsClient.Get(execution.TestName)
   335  			if err != nil {
   336  				s.logger.Errorf("get test returned an error %v while looking for test name: %s", err, execution.TestName)
   337  				return err
   338  			}
   339  
   340  			if test.Spec.ExecutionRequest != nil && test.Spec.ExecutionRequest.NegativeTest {
   341  				s.logger.Debugw("test run was expected to fail, and it failed as expected", "test", execution.TestName)
   342  				execution.ExecutionResult.Status = testkube.ExecutionStatusPassed
   343  				execution.ExecutionResult.ErrorMessage = ""
   344  			}
   345  
   346  			err = s.resultRepository.UpdateResult(ctx, executionID, execution)
   347  			if err != nil {
   348  				s.logger.Errorf("update execution result returned an error %v while storing for execution id: %s", err, executionID)
   349  				return err
   350  			}
   351  		}
   352  	}
   353  
   354  	return nil
   355  }
   356  
   357  func (s *Service) deploymentEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
   358  	getConditions := func(object metav1.Object) func() ([]testtriggersv1.TestTriggerCondition, error) {
   359  		return func() ([]testtriggersv1.TestTriggerCondition, error) {
   360  			return getDeploymentConditions(ctx, s.clientset, object)
   361  		}
   362  	}
   363  	return cache.ResourceEventHandlerFuncs{
   364  		AddFunc: func(obj any) {
   365  			deployment, ok := obj.(*appsv1.Deployment)
   366  			if !ok {
   367  				s.logger.Errorf("failed to process create deployment event due to it being an unexpected type, received type %+v", obj)
   368  				return
   369  			}
   370  			if inPast(deployment.CreationTimestamp.Time, s.watchFromDate) {
   371  				s.logger.Debugf(
   372  					"trigger service: watcher component: no-op create trigger: deployment %s/%s was created in the past",
   373  					deployment.Namespace, deployment.Name,
   374  				)
   375  				return
   376  			}
   377  			s.logger.Debugf("emiting event: deployment %s/%s created", deployment.Namespace, deployment.Name)
   378  			event := newWatcherEvent(testtrigger.EventCreated, deployment, testtrigger.ResourceDeployment, withConditionsGetter(getConditions(deployment)))
   379  			if err := s.match(ctx, event); err != nil {
   380  				s.logger.Errorf("event matcher returned an error while matching create deployment event: %v", err)
   381  			}
   382  		},
   383  		UpdateFunc: func(oldObj, newObj interface{}) {
   384  			oldDeployment, ok := oldObj.(*appsv1.Deployment)
   385  			if !ok {
   386  				s.logger.Errorf(
   387  					"failed to process update deployment event for old object due to it being an unexpected type, received type %+v",
   388  					oldDeployment,
   389  				)
   390  				return
   391  			}
   392  			newDeployment, ok := newObj.(*appsv1.Deployment)
   393  			if !ok {
   394  				s.logger.Errorf(
   395  					"failed to process update deployment event for new object due to it being an unexpected type, received type %+v",
   396  					newDeployment,
   397  				)
   398  				return
   399  			}
   400  			if cmp.Equal(oldDeployment.Spec, newDeployment.Spec) {
   401  				s.logger.Debugf("trigger service: watcher component: no-op update trigger: deployment specs are equal")
   402  				return
   403  			}
   404  			s.logger.Debugf(
   405  				"trigger service: watcher component: emiting event: deployment %s/%s updated",
   406  				newDeployment.Namespace, newDeployment.Name,
   407  			)
   408  			causes := diffDeployments(oldDeployment, newDeployment)
   409  			event := newWatcherEvent(testtrigger.EventModified, newDeployment, testtrigger.ResourceDeployment, withCauses(causes), withConditionsGetter(getConditions(newDeployment)))
   410  			if err := s.match(ctx, event); err != nil {
   411  				s.logger.Errorf("event matcher returned an error while matching update deployment event: %v", err)
   412  			}
   413  		},
   414  		DeleteFunc: func(obj interface{}) {
   415  			deployment, ok := obj.(*appsv1.Deployment)
   416  			if !ok {
   417  				s.logger.Errorf("failed to process create deployment event due to it being an unexpected type, received type %+v", obj)
   418  				return
   419  			}
   420  			s.logger.Debugf("trigger service: watcher component: emiting event: deployment %s/%s deleted", deployment.Namespace, deployment.Name)
   421  			event := newWatcherEvent(testtrigger.EventDeleted, deployment, testtrigger.ResourceDeployment, withConditionsGetter(getConditions(deployment)))
   422  			if err := s.match(ctx, event); err != nil {
   423  				s.logger.Errorf("event matcher returned an error while matching delete deployment event: %v", err)
   424  			}
   425  		},
   426  	}
   427  }
   428  
   429  func (s *Service) statefulSetEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
   430  	getConditions := func(object metav1.Object) func() ([]testtriggersv1.TestTriggerCondition, error) {
   431  		return func() ([]testtriggersv1.TestTriggerCondition, error) {
   432  			return getStatefulSetConditions(ctx, s.clientset, object)
   433  		}
   434  	}
   435  	return cache.ResourceEventHandlerFuncs{
   436  		AddFunc: func(obj any) {
   437  			statefulset, ok := obj.(*appsv1.StatefulSet)
   438  			if !ok {
   439  				s.logger.Errorf("failed to process create statefulset event due to it being an unexpected type, received type %+v", obj)
   440  				return
   441  			}
   442  			if inPast(statefulset.CreationTimestamp.Time, s.watchFromDate) {
   443  				s.logger.Debugf(
   444  					"trigger service: watcher component: no-op create trigger: statefulset %s/%s was created in the past",
   445  					statefulset.Namespace, statefulset.Name,
   446  				)
   447  				return
   448  			}
   449  			s.logger.Debugf("trigger service: watcher component: emiting event: statefulset %s/%s created", statefulset.Namespace, statefulset.Name)
   450  			event := newWatcherEvent(testtrigger.EventCreated, statefulset, testtrigger.ResourceStatefulSet, withConditionsGetter(getConditions(statefulset)))
   451  			if err := s.match(ctx, event); err != nil {
   452  				s.logger.Errorf("event matcher returned an error while matching create statefulset event: %v", err)
   453  			}
   454  		},
   455  		UpdateFunc: func(oldObj, newObj interface{}) {
   456  			oldStatefulSet, ok := oldObj.(*appsv1.StatefulSet)
   457  			if !ok {
   458  				s.logger.Errorf(
   459  					"failed to process update statefulset event for old object due to it being an unexpected type, received type %+v",
   460  					oldStatefulSet,
   461  				)
   462  				return
   463  			}
   464  			newStatefulSet, ok := newObj.(*appsv1.StatefulSet)
   465  			if !ok {
   466  				s.logger.Errorf(
   467  					"failed to process update statefulset event for new object due to it being an unexpected type, received type %+v",
   468  					newStatefulSet,
   469  				)
   470  				return
   471  			}
   472  			if cmp.Equal(oldStatefulSet.Spec, newStatefulSet.Spec) {
   473  				s.logger.Debugf("trigger service: watcher component: no-op update trigger: statefulset specs are equal")
   474  				return
   475  			}
   476  			s.logger.Debugf(
   477  				"trigger service: watcher component: emiting event: statefulset %s/%s updated",
   478  				newStatefulSet.Namespace, newStatefulSet.Name,
   479  			)
   480  			event := newWatcherEvent(testtrigger.EventModified, newStatefulSet, testtrigger.ResourceStatefulSet, withConditionsGetter(getConditions(newStatefulSet)))
   481  			if err := s.match(ctx, event); err != nil {
   482  				s.logger.Errorf("event matcher returned an error while matching update statefulset event: %v", err)
   483  			}
   484  		},
   485  		DeleteFunc: func(obj interface{}) {
   486  			statefulset, ok := obj.(*appsv1.StatefulSet)
   487  			if !ok {
   488  				s.logger.Errorf("failed to process delete statefulset event due to it being an unexpected type, received type %+v", obj)
   489  				return
   490  			}
   491  			s.logger.Debugf("trigger service: watcher component: emiting event: statefulset %s/%s deleted", statefulset.Namespace, statefulset.Name)
   492  			event := newWatcherEvent(testtrigger.EventDeleted, statefulset, testtrigger.ResourceStatefulSet, withConditionsGetter(getConditions(statefulset)))
   493  			if err := s.match(ctx, event); err != nil {
   494  				s.logger.Errorf("event matcher returned an error while matching delete statefulset event: %v", err)
   495  			}
   496  		},
   497  	}
   498  }
   499  
   500  func (s *Service) daemonSetEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
   501  	getConditions := func(object metav1.Object) func() ([]testtriggersv1.TestTriggerCondition, error) {
   502  		return func() ([]testtriggersv1.TestTriggerCondition, error) {
   503  			return getDaemonSetConditions(ctx, s.clientset, object)
   504  		}
   505  	}
   506  	return cache.ResourceEventHandlerFuncs{
   507  		AddFunc: func(obj any) {
   508  			daemonset, ok := obj.(*appsv1.DaemonSet)
   509  			if !ok {
   510  				s.logger.Errorf("failed to process create daemonset event due to it being an unexpected type, received type %+v", obj)
   511  				return
   512  			}
   513  			if inPast(daemonset.CreationTimestamp.Time, s.watchFromDate) {
   514  				s.logger.Debugf(
   515  					"trigger service: watcher component: no-op create trigger: daemonset %s/%s was created in the past",
   516  					daemonset.Namespace, daemonset.Name,
   517  				)
   518  				return
   519  			}
   520  			s.logger.Debugf("trigger service: watcher component: emiting event: daemonset %s/%s created", daemonset.Namespace, daemonset.Name)
   521  			event := newWatcherEvent(testtrigger.EventCreated, daemonset, testtrigger.ResourceDaemonSet, withConditionsGetter(getConditions(daemonset)))
   522  			if err := s.match(ctx, event); err != nil {
   523  				s.logger.Errorf("event matcher returned an error while matching create daemonset event: %v", err)
   524  			}
   525  		},
   526  		UpdateFunc: func(oldObj, newObj interface{}) {
   527  			oldDaemonSet, ok := oldObj.(*appsv1.DaemonSet)
   528  			if !ok {
   529  				s.logger.Errorf(
   530  					"failed to process update daemonset event for old object due to it being an unexpected type, received type %+v",
   531  					oldDaemonSet,
   532  				)
   533  				return
   534  			}
   535  			newDaemonSet, ok := newObj.(*appsv1.DaemonSet)
   536  			if !ok {
   537  				s.logger.Errorf(
   538  					"failed to process update daemonset event for new object due to it being an unexpected type, received type %+v",
   539  					newDaemonSet,
   540  				)
   541  				return
   542  			}
   543  			if cmp.Equal(oldDaemonSet.Spec, newDaemonSet.Spec) {
   544  				s.logger.Debugf("trigger service: watcher component: no-op update trigger: daemonset specs are equal")
   545  				return
   546  			}
   547  			s.logger.Debugf(
   548  				"trigger service: watcher component: emiting event: daemonset %s/%s updated",
   549  				newDaemonSet.Namespace, newDaemonSet.Name,
   550  			)
   551  			event := newWatcherEvent(testtrigger.EventModified, newDaemonSet, testtrigger.ResourceDaemonSet, withConditionsGetter(getConditions(newDaemonSet)))
   552  			if err := s.match(ctx, event); err != nil {
   553  				s.logger.Errorf("event matcher returned an error while matching update daemonset event: %v", err)
   554  			}
   555  		},
   556  		DeleteFunc: func(obj interface{}) {
   557  			daemonset, ok := obj.(*appsv1.DaemonSet)
   558  			if !ok {
   559  				s.logger.Errorf("failed to process delete daemonset event due to it being an unexpected type, received type %+v", obj)
   560  				return
   561  			}
   562  			s.logger.Debugf("trigger service: watcher component: emiting event: daemonset %s/%s deleted", daemonset.Namespace, daemonset.Name)
   563  			event := newWatcherEvent(testtrigger.EventDeleted, daemonset, testtrigger.ResourceDaemonSet, withConditionsGetter(getConditions(daemonset)))
   564  			if err := s.match(ctx, event); err != nil {
   565  				s.logger.Errorf("event matcher returned an error while matching delete daemonset event: %v", err)
   566  			}
   567  		},
   568  	}
   569  }
   570  
   571  func (s *Service) serviceEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
   572  	getConditions := func(object metav1.Object) func() ([]testtriggersv1.TestTriggerCondition, error) {
   573  		return func() ([]testtriggersv1.TestTriggerCondition, error) {
   574  			return getServiceConditions(ctx, s.clientset, object)
   575  		}
   576  	}
   577  	getAddrress := func(object metav1.Object) func(c context.Context, delay time.Duration) (string, error) {
   578  		return func(c context.Context, delay time.Duration) (string, error) {
   579  			return getServiceAdress(ctx, s.clientset, object)
   580  		}
   581  	}
   582  	return cache.ResourceEventHandlerFuncs{
   583  		AddFunc: func(obj any) {
   584  			service, ok := obj.(*corev1.Service)
   585  			if !ok {
   586  				s.logger.Errorf("failed to process create service event due to it being an unexpected type, received type %+v", obj)
   587  				return
   588  			}
   589  			if inPast(service.CreationTimestamp.Time, s.watchFromDate) {
   590  				s.logger.Debugf(
   591  					"trigger service: watcher component: no-op create trigger: service %s/%s was created in the past",
   592  					service.Namespace, service.Name,
   593  				)
   594  				return
   595  			}
   596  			s.logger.Debugf("trigger service: watcher component: emiting event: service %s/%s created", service.Namespace, service.Name)
   597  			event := newWatcherEvent(testtrigger.EventCreated, service, testtrigger.ResourceService,
   598  				withConditionsGetter(getConditions(service)), withAddressGetter(getAddrress(service)))
   599  			if err := s.match(ctx, event); err != nil {
   600  				s.logger.Errorf("event matcher returned an error while matching create service event: %v", err)
   601  			}
   602  		},
   603  		UpdateFunc: func(oldObj, newObj interface{}) {
   604  			oldService, ok := oldObj.(*corev1.Service)
   605  			if !ok {
   606  				s.logger.Errorf(
   607  					"failed to process update service event for old object due to it being an unexpected type, received type %+v",
   608  					oldService,
   609  				)
   610  				return
   611  			}
   612  			newService, ok := newObj.(*corev1.Service)
   613  			if !ok {
   614  				s.logger.Errorf(
   615  					"failed to process update service event for new object due to it being an unexpected type, received type %+v",
   616  					newService,
   617  				)
   618  				return
   619  			}
   620  			if cmp.Equal(oldService.Spec, newService.Spec) {
   621  				s.logger.Debugf("trigger service: watcher component: no-op update trigger: service specs are equal")
   622  				return
   623  			}
   624  			s.logger.Debugf(
   625  				"trigger service: watcher component: emiting event: service %s/%s updated",
   626  				newService.Namespace, newService.Name,
   627  			)
   628  			event := newWatcherEvent(testtrigger.EventModified, newService, testtrigger.ResourceService,
   629  				withConditionsGetter(getConditions(newService)), withAddressGetter(getAddrress(newService)))
   630  			if err := s.match(ctx, event); err != nil {
   631  				s.logger.Errorf("event matcher returned an error while matching update service event: %v", err)
   632  			}
   633  		},
   634  		DeleteFunc: func(obj interface{}) {
   635  			service, ok := obj.(*corev1.Service)
   636  			if !ok {
   637  				s.logger.Errorf("failed to process delete service event due to it being an unexpected type, received type %+v", obj)
   638  				return
   639  			}
   640  			s.logger.Debugf("trigger service: watcher component: emiting event: service %s/%s deleted", service.Namespace, service.Name)
   641  			event := newWatcherEvent(testtrigger.EventDeleted, service, testtrigger.ResourceService,
   642  				withConditionsGetter(getConditions(service)), withAddressGetter(getAddrress(service)))
   643  			if err := s.match(ctx, event); err != nil {
   644  				s.logger.Errorf("event matcher returned an error while matching delete service event: %v", err)
   645  			}
   646  		},
   647  	}
   648  }
   649  
   650  func (s *Service) ingressEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
   651  	return cache.ResourceEventHandlerFuncs{
   652  		AddFunc: func(obj any) {
   653  			ingress, ok := obj.(*networkingv1.Ingress)
   654  			if !ok {
   655  				s.logger.Errorf("failed to process create ingress event due to it being an unexpected type, received type %+v", obj)
   656  				return
   657  			}
   658  			if inPast(ingress.CreationTimestamp.Time, s.watchFromDate) {
   659  				s.logger.Debugf(
   660  					"trigger service: watcher component: no-op create trigger: ingress %s/%s was created in the past",
   661  					ingress.Namespace, ingress.Name,
   662  				)
   663  				return
   664  			}
   665  			s.logger.Debugf("trigger service: watcher component: emiting event: ingress %s/%s created", ingress.Namespace, ingress.Name)
   666  			event := newWatcherEvent(testtrigger.EventCreated, ingress, testtrigger.ResourceIngress)
   667  			if err := s.match(ctx, event); err != nil {
   668  				s.logger.Errorf("event matcher returned an error while matching create ingress event: %v", err)
   669  			}
   670  		},
   671  		UpdateFunc: func(oldObj, newObj interface{}) {
   672  			oldIngress, ok := oldObj.(*networkingv1.Ingress)
   673  			if !ok {
   674  				s.logger.Errorf(
   675  					"failed to process update ingress event for old object due to it being an unexpected type, received type %+v",
   676  					oldIngress,
   677  				)
   678  				return
   679  			}
   680  			newIngress, ok := newObj.(*networkingv1.Ingress)
   681  			if !ok {
   682  				s.logger.Errorf(
   683  					"failed to process update ingress event for new object due to it being an unexpected type, received type %+v",
   684  					newIngress,
   685  				)
   686  				return
   687  			}
   688  			if cmp.Equal(oldIngress.Spec, newIngress.Spec) {
   689  				s.logger.Debugf("trigger service: watcher component: no-op update trigger: ingress specs are equal")
   690  				return
   691  			}
   692  			s.logger.Debugf(
   693  				"trigger service: watcher component: emiting event: ingress %s/%s updated",
   694  				oldIngress.Namespace, newIngress.Name,
   695  			)
   696  			event := newWatcherEvent(testtrigger.EventModified, newIngress, testtrigger.ResourceIngress)
   697  			if err := s.match(ctx, event); err != nil {
   698  				s.logger.Errorf("event matcher returned an error while matching update ingress event: %v", err)
   699  			}
   700  		},
   701  		DeleteFunc: func(obj interface{}) {
   702  			ingress, ok := obj.(*networkingv1.Ingress)
   703  			if !ok {
   704  				s.logger.Errorf("failed to process delete ingress event due to it being an unexpected type, received type %+v", obj)
   705  				return
   706  			}
   707  			s.logger.Debugf("trigger service: watcher component: emiting event: ingress %s/%s deleted", ingress.Namespace, ingress.Name)
   708  			event := newWatcherEvent(testtrigger.EventDeleted, ingress, testtrigger.ResourceIngress)
   709  			if err := s.match(ctx, event); err != nil {
   710  				s.logger.Errorf("event matcher returned an error while matching delete ingress event: %v", err)
   711  			}
   712  		},
   713  	}
   714  }
   715  
   716  func (s *Service) clusterEventEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
   717  	return cache.ResourceEventHandlerFuncs{
   718  		AddFunc: func(obj any) {
   719  			clusterEvent, ok := obj.(*corev1.Event)
   720  			if !ok {
   721  				s.logger.Errorf("failed to process create cluster event event due to it being an unexpected type, received type %+v", obj)
   722  				return
   723  			}
   724  			if inPast(clusterEvent.CreationTimestamp.Time, s.watchFromDate) {
   725  				s.logger.Debugf(
   726  					"trigger service: watcher component: no-op create trigger: cluster event %s/%s was created in the past",
   727  					clusterEvent.Namespace, clusterEvent.Name,
   728  				)
   729  				return
   730  			}
   731  			s.logger.Debugf("trigger service: watcher component: emiting event: cluster event %s/%s created", clusterEvent.Namespace, clusterEvent.Name)
   732  			event := newWatcherEvent(testtrigger.EventCreated, clusterEvent, testtrigger.ResourceEvent)
   733  			if err := s.match(ctx, event); err != nil {
   734  				s.logger.Errorf("event matcher returned an error while matching create cluster event event: %v", err)
   735  			}
   736  		},
   737  	}
   738  }
   739  
   740  func (s *Service) testTriggerEventHandler() cache.ResourceEventHandlerFuncs {
   741  	return cache.ResourceEventHandlerFuncs{
   742  		AddFunc: func(obj interface{}) {
   743  			t, ok := obj.(*testtriggersv1.TestTrigger)
   744  			if !ok {
   745  				s.logger.Errorf("failed to process create testtrigger event due to it being an unexpected type, received type %+v", obj)
   746  				return
   747  			}
   748  			s.logger.Debugf(
   749  				"trigger service: watcher component: adding testtrigger %s/%s for resource %s on event %s",
   750  				t.Namespace, t.Name, t.Spec.Resource, t.Spec.Event,
   751  			)
   752  			s.addTrigger(t)
   753  
   754  			s.logger.Debugf(
   755  				"trigger service: watcher component: emitting event for created testtrigger %s/%s for resource %s on event %s",
   756  				t.Namespace, t.Name, t.Spec.Resource, t.Spec.Event,
   757  			)
   758  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventCreated, testkube.EventResourceTrigger, t.Name))
   759  		},
   760  		UpdateFunc: func(oldObj, newObj interface{}) {
   761  			t, ok := newObj.(*testtriggersv1.TestTrigger)
   762  			if !ok {
   763  				s.logger.Errorf(
   764  					"failed to process update testtrigger event for new testtrigger due to it being an unexpected type, received type %+v",
   765  					newObj,
   766  				)
   767  				return
   768  			}
   769  			s.logger.Debugf(
   770  				"trigger service: watcher component: updating testtrigger %s/%s for resource %s on event %s",
   771  				t.Namespace, t.Name, t.Spec.Resource, t.Spec.Event,
   772  			)
   773  			s.updateTrigger(t)
   774  
   775  			s.logger.Debugf(
   776  				"trigger service: watcher component: emitting event for updated testtrigger %s/%s for resource %s on event %s",
   777  				t.Namespace, t.Name, t.Spec.Resource, t.Spec.Event,
   778  			)
   779  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventUpdated, testkube.EventResourceTrigger, t.Name))
   780  		},
   781  		DeleteFunc: func(obj interface{}) {
   782  			t, ok := obj.(*testtriggersv1.TestTrigger)
   783  			if !ok {
   784  				s.logger.Errorf("failed to process delete testtrigger event due to it being an unexpected type, received type %+v", obj)
   785  				return
   786  			}
   787  			s.logger.Debugf(
   788  				"trigger service: watcher component: deleting testtrigger %s/%s for resource %s on event %s",
   789  				t.Namespace, t.Name, t.Spec.Resource, t.Spec.Event,
   790  			)
   791  			s.removeTrigger(t)
   792  
   793  			s.logger.Debugf(
   794  				"trigger service: watcher component: emitting event for deleted testtrigger %s/%s for resource %s on event %s",
   795  				t.Namespace, t.Name, t.Spec.Resource, t.Spec.Event,
   796  			)
   797  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventDeleted, testkube.EventResourceTrigger, t.Name))
   798  		},
   799  	}
   800  }
   801  
   802  func (s *Service) testSuiteEventHandler() cache.ResourceEventHandlerFuncs {
   803  	return cache.ResourceEventHandlerFuncs{
   804  		AddFunc: func(obj interface{}) {
   805  			testSuite, ok := obj.(*testsuitev3.TestSuite)
   806  			if !ok {
   807  				s.logger.Errorf("failed to process create testsuite event due to it being an unexpected type, received type %+v", obj)
   808  				return
   809  			}
   810  			if inPast(testSuite.CreationTimestamp.Time, s.watchFromDate) {
   811  				s.logger.Debugf(
   812  					"trigger service: watcher component: no-op create test suite: test suite %s/%s was created in the past",
   813  					testSuite.Namespace, testSuite.Name,
   814  				)
   815  				return
   816  			}
   817  			s.logger.Debugf(
   818  				"trigger service: watcher component: adding testsuite %s/%s",
   819  				testSuite.Namespace, testSuite.Name,
   820  			)
   821  			s.addTestSuite(testSuite)
   822  
   823  			s.logger.Debugf(
   824  				"trigger service: watcher component: emitting event for creating testsuite %s/%s",
   825  				testSuite.Namespace, testSuite.Name,
   826  			)
   827  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventCreated, testkube.EventResourceTestsuite, testSuite.Name))
   828  		},
   829  		UpdateFunc: func(oldObj, newObj interface{}) {
   830  			testSuite, ok := newObj.(*testsuitev3.TestSuite)
   831  			if !ok {
   832  				s.logger.Errorf("failed to process update testsuite event due to it being an unexpected type, received type %+v", newObj)
   833  				return
   834  			}
   835  			s.logger.Debugf(
   836  				"trigger service: watcher component: emitting event for updating testsuite %s/%s",
   837  				testSuite.Namespace, testSuite.Name,
   838  			)
   839  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventUpdated, testkube.EventResourceTestsuite, testSuite.Name))
   840  		},
   841  		DeleteFunc: func(obj interface{}) {
   842  			testSuite, ok := obj.(*testsuitev3.TestSuite)
   843  			if !ok {
   844  				s.logger.Errorf("failed to process delete testsuite event due to it being an unexpected type, received type %+v", obj)
   845  				return
   846  			}
   847  			s.logger.Debugf(
   848  				"trigger service: watcher component: emitting event for deleting testsuite %s/%s",
   849  				testSuite.Namespace, testSuite.Name,
   850  			)
   851  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventDeleted, testkube.EventResourceTestsuite, testSuite.Name))
   852  		},
   853  	}
   854  }
   855  
   856  func (s *Service) testEventHandler() cache.ResourceEventHandlerFuncs {
   857  	return cache.ResourceEventHandlerFuncs{
   858  		AddFunc: func(obj interface{}) {
   859  			test, ok := obj.(*testsv3.Test)
   860  			if !ok {
   861  				s.logger.Errorf("failed to process create test event due to it being an unexpected type, received type %+v", obj)
   862  				return
   863  			}
   864  			if inPast(test.CreationTimestamp.Time, s.watchFromDate) {
   865  				s.logger.Debugf(
   866  					"trigger service: watcher component: no-op create test: test %s/%s was created in the past",
   867  					test.Namespace, test.Name,
   868  				)
   869  				return
   870  			}
   871  			s.logger.Debugf(
   872  				"trigger service: watcher component: adding test %s/%s",
   873  				test.Namespace, test.Name,
   874  			)
   875  			s.addTest(test)
   876  
   877  			s.logger.Debugf(
   878  				"trigger service: watcher component: emitting event for test %s/%s",
   879  				test.Namespace, test.Name,
   880  			)
   881  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventCreated, testkube.EventResourceTest, test.Name))
   882  		},
   883  		UpdateFunc: func(oldObj, newObj interface{}) {
   884  			test, ok := newObj.(*testsv3.Test)
   885  			if !ok {
   886  				s.logger.Errorf("failed to process update test event due to it being an unexpected type, received type %+v", newObj)
   887  				return
   888  			}
   889  			s.logger.Debugf(
   890  				"trigger service: watcher component: updating test %s/%s",
   891  				test.Namespace, test.Name,
   892  			)
   893  			s.updateTest(test)
   894  
   895  			s.logger.Debugf(
   896  				"trigger service: watcher component: emitting event for updating test %s/%s",
   897  				test.Namespace, test.Name,
   898  			)
   899  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventUpdated, testkube.EventResourceTest, test.Name))
   900  		},
   901  		DeleteFunc: func(obj interface{}) {
   902  			test, ok := obj.(*testsv3.Test)
   903  			if !ok {
   904  				s.logger.Errorf("failed to process delete test event due to it being an unexpected type, received type %+v", obj)
   905  				return
   906  			}
   907  			s.logger.Debugf(
   908  				"trigger service: watcher component: emitting event for deleting test %s/%s",
   909  				test.Namespace, test.Name,
   910  			)
   911  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventDeleted, testkube.EventResourceTest, test.Name))
   912  		},
   913  	}
   914  }
   915  
   916  func (s *Service) executorEventHandler() cache.ResourceEventHandlerFuncs {
   917  	return cache.ResourceEventHandlerFuncs{
   918  		AddFunc: func(obj interface{}) {
   919  			executor, ok := obj.(*executorv1.Executor)
   920  			if !ok {
   921  				s.logger.Errorf("failed to process create executor event due to it being an unexpected type, received type %+v", obj)
   922  				return
   923  			}
   924  			s.logger.Debugf(
   925  				"trigger service: watcher component: emitting event for executor %s/%s",
   926  				executor.Namespace, executor.Name,
   927  			)
   928  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventCreated, testkube.EventResourceExecutor, executor.Name))
   929  		},
   930  		UpdateFunc: func(oldObj, newObj interface{}) {
   931  			executor, ok := newObj.(*executorv1.Executor)
   932  			if !ok {
   933  				s.logger.Errorf("failed to process update executor event due to it being an unexpected type, received type %+v", newObj)
   934  				return
   935  			}
   936  
   937  			s.logger.Debugf(
   938  				"trigger service: watcher component: emitting event for updating executor %s/%s",
   939  				executor.Namespace, executor.Name,
   940  			)
   941  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventUpdated, testkube.EventResourceExecutor, executor.Name))
   942  		},
   943  		DeleteFunc: func(obj interface{}) {
   944  			executor, ok := obj.(*executorv1.Executor)
   945  			if !ok {
   946  				s.logger.Errorf("failed to process delete executor event due to it being an unexpected type, received type %+v", obj)
   947  				return
   948  			}
   949  			s.logger.Debugf(
   950  				"trigger service: watcher component: emitting event for deleting executor %s/%s",
   951  				executor.Namespace, executor.Name,
   952  			)
   953  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventDeleted, testkube.EventResourceExecutor, executor.Name))
   954  		},
   955  	}
   956  }
   957  
   958  func (s *Service) webhookEventHandler() cache.ResourceEventHandlerFuncs {
   959  	return cache.ResourceEventHandlerFuncs{
   960  		AddFunc: func(obj interface{}) {
   961  			webhook, ok := obj.(*executorv1.Webhook)
   962  			if !ok {
   963  				s.logger.Errorf("failed to process create webhook event due to it being an unexpected type, received type %+v", obj)
   964  				return
   965  			}
   966  			s.logger.Debugf(
   967  				"trigger service: watcher component: emitting event for webhook %s/%s",
   968  				webhook.Namespace, webhook.Name,
   969  			)
   970  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventCreated, testkube.EventResourceWebhook, webhook.Name))
   971  		},
   972  		UpdateFunc: func(oldObj, newObj interface{}) {
   973  			webhook, ok := newObj.(*executorv1.Webhook)
   974  			if !ok {
   975  				s.logger.Errorf("failed to process update webhook event due to it being an unexpected type, received type %+v", newObj)
   976  				return
   977  			}
   978  
   979  			s.logger.Debugf(
   980  				"trigger service: watcher component: emitting event for updating webhook %s/%s",
   981  				webhook.Namespace, webhook.Name,
   982  			)
   983  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventUpdated, testkube.EventResourceWebhook, webhook.Name))
   984  		},
   985  		DeleteFunc: func(obj interface{}) {
   986  			webhook, ok := obj.(*executorv1.Webhook)
   987  			if !ok {
   988  				s.logger.Errorf("failed to process delete webhook event due to it being an unexpected type, received type %+v", obj)
   989  				return
   990  			}
   991  			s.logger.Debugf(
   992  				"trigger service: watcher component: emitting event for deleting webhook %s/%s",
   993  				webhook.Namespace, webhook.Name,
   994  			)
   995  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventDeleted, testkube.EventResourceWebhook, webhook.Name))
   996  		},
   997  	}
   998  }
   999  
  1000  func (s *Service) testSourceEventHandler() cache.ResourceEventHandlerFuncs {
  1001  	return cache.ResourceEventHandlerFuncs{
  1002  		AddFunc: func(obj interface{}) {
  1003  			testSource, ok := obj.(*testsourcev1.TestSource)
  1004  			if !ok {
  1005  				s.logger.Errorf("failed to process create test source event due to it being an unexpected type, received type %+v", obj)
  1006  				return
  1007  			}
  1008  			s.logger.Debugf(
  1009  				"trigger service: watcher component: emitting event for test source %s/%s",
  1010  				testSource.Namespace, testSource.Name,
  1011  			)
  1012  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventCreated, testkube.EventResourceTestsource, testSource.Name))
  1013  		},
  1014  		UpdateFunc: func(oldObj, newObj interface{}) {
  1015  			testSource, ok := newObj.(*testsourcev1.TestSource)
  1016  			if !ok {
  1017  				s.logger.Errorf("failed to process update test source event due to it being an unexpected type, received type %+v", newObj)
  1018  				return
  1019  			}
  1020  
  1021  			s.logger.Debugf(
  1022  				"trigger service: watcher component: emitting event for updating test source %s/%s",
  1023  				testSource.Namespace, testSource.Name,
  1024  			)
  1025  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventUpdated, testkube.EventResourceTestsource, testSource.Name))
  1026  		},
  1027  		DeleteFunc: func(obj interface{}) {
  1028  			testSource, ok := obj.(*testsourcev1.TestSource)
  1029  			if !ok {
  1030  				s.logger.Errorf("failed to process delete test source event due to it being an unexpected type, received type %+v", obj)
  1031  				return
  1032  			}
  1033  			s.logger.Debugf(
  1034  				"trigger service: watcher component: emitting event for deleting test source %s/%s",
  1035  				testSource.Namespace, testSource.Name,
  1036  			)
  1037  			s.eventsBus.Publish(testkube.NewEvent(testkube.EventDeleted, testkube.EventResourceTestsource, testSource.Name))
  1038  		},
  1039  	}
  1040  }
  1041  
  1042  func (s *Service) configMapEventHandler(ctx context.Context) cache.ResourceEventHandlerFuncs {
  1043  	return cache.ResourceEventHandlerFuncs{
  1044  		AddFunc: func(obj any) {
  1045  			configMap, ok := obj.(*corev1.ConfigMap)
  1046  			if !ok {
  1047  				s.logger.Errorf("failed to process create config map event due to it being an unexpected type, received type %+v", obj)
  1048  				return
  1049  			}
  1050  			if inPast(configMap.CreationTimestamp.Time, s.watchFromDate) {
  1051  				s.logger.Debugf(
  1052  					"trigger service: watcher component: no-op create trigger: config map %s/%s was created in the past",
  1053  					configMap.Namespace, configMap.Name,
  1054  				)
  1055  				return
  1056  			}
  1057  			s.logger.Debugf("trigger service: watcher component: emiting event: config map %s/%s created", configMap.Namespace, configMap.Name)
  1058  			event := newWatcherEvent(testtrigger.EventCreated, configMap, testtrigger.ResourceConfigMap)
  1059  			if err := s.match(ctx, event); err != nil {
  1060  				s.logger.Errorf("event matcher returned an error while matching create config map event: %v", err)
  1061  			}
  1062  		},
  1063  		UpdateFunc: func(oldObj, newObj interface{}) {
  1064  			oldConfigMap, ok := oldObj.(*corev1.ConfigMap)
  1065  			if !ok {
  1066  				s.logger.Errorf(
  1067  					"failed to process update config map event for old object due to it being an unexpected type, received type %+v",
  1068  					oldConfigMap,
  1069  				)
  1070  				return
  1071  			}
  1072  			newConfigMap, ok := newObj.(*corev1.ConfigMap)
  1073  			if !ok {
  1074  				s.logger.Errorf(
  1075  					"failed to process update config map event for new object due to it being an unexpected type, received type %+v",
  1076  					newConfigMap,
  1077  				)
  1078  				return
  1079  			}
  1080  			if cmp.Equal(oldConfigMap.Data, newConfigMap.Data) && cmp.Equal(oldConfigMap.BinaryData, newConfigMap.BinaryData) {
  1081  				s.logger.Debugf("trigger service: watcher component: no-op update trigger: config map data and binary data are equal")
  1082  				return
  1083  			}
  1084  			s.logger.Debugf(
  1085  				"trigger service: watcher component: emiting event: config map %s/%s updated",
  1086  				oldConfigMap.Namespace, newConfigMap.Name,
  1087  			)
  1088  			event := newWatcherEvent(testtrigger.EventModified, newConfigMap, testtrigger.ResourceConfigMap)
  1089  			if err := s.match(ctx, event); err != nil {
  1090  				s.logger.Errorf("event matcher returned an error while matching update config map event: %v", err)
  1091  			}
  1092  		},
  1093  		DeleteFunc: func(obj interface{}) {
  1094  			configMap, ok := obj.(*corev1.ConfigMap)
  1095  			if !ok {
  1096  				s.logger.Errorf("failed to process delete config map event due to it being an unexpected type, received type %+v", obj)
  1097  				return
  1098  			}
  1099  			s.logger.Debugf("trigger service: watcher component: emiting event: config map %s/%s deleted", configMap.Namespace, configMap.Name)
  1100  			event := newWatcherEvent(testtrigger.EventDeleted, configMap, testtrigger.ResourceConfigMap)
  1101  			if err := s.match(ctx, event); err != nil {
  1102  				s.logger.Errorf("event matcher returned an error while matching delete config map event: %v", err)
  1103  			}
  1104  		},
  1105  	}
  1106  }