github.com/kubeshop/testkube@v1.17.23/pkg/triggers/executor.go (about) 1 package triggers 2 3 import ( 4 "context" 5 "regexp" 6 "time" 7 8 "github.com/pkg/errors" 9 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 11 testsv3 "github.com/kubeshop/testkube-operator/api/tests/v3" 12 testsuitesv3 "github.com/kubeshop/testkube-operator/api/testsuite/v3" 13 testtriggersv1 "github.com/kubeshop/testkube-operator/api/testtriggers/v1" 14 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 15 "github.com/kubeshop/testkube/pkg/scheduler" 16 "github.com/kubeshop/testkube/pkg/workerpool" 17 ) 18 19 type Execution string 20 21 const ( 22 ExecutionTest = "test" 23 ExecutionTestSuite = "testsuite" 24 ) 25 26 type ExecutorF func(context.Context, *watcherEvent, *testtriggersv1.TestTrigger) error 27 28 func (s *Service) execute(ctx context.Context, e *watcherEvent, t *testtriggersv1.TestTrigger) error { 29 status := s.getStatusForTrigger(t) 30 31 concurrencyLevel := scheduler.DefaultConcurrencyLevel 32 variables := map[string]testkube.Variable{ 33 "WATCHER_EVENT_RESOURCE": { 34 Name: "WATCHER_EVENT_RESOURCE", 35 Value: string(e.resource), 36 Type_: testkube.VariableTypeBasic, 37 }, 38 "WATCHER_EVENT_NAME": { 39 Name: "WATCHER_EVENT_NAME", 40 Value: e.name, 41 Type_: testkube.VariableTypeBasic, 42 }, 43 "WATCHER_EVENT_NAMESPACE": { 44 Name: "WATCHER_EVENT_NAMESPACE", 45 Value: e.namespace, 46 Type_: testkube.VariableTypeBasic, 47 }, 48 "WATCHER_EVENT_EVENT_TYPE": { 49 Name: "WATCHER_EVENT_EVENT_TYPE", 50 Value: string(e.eventType), 51 Type_: testkube.VariableTypeBasic, 52 }, 53 } 54 55 switch t.Spec.Execution { 56 case ExecutionTest: 57 tests, err := s.getTests(t) 58 if err != nil { 59 return err 60 } 61 62 request := testkube.ExecutionRequest{ 63 Variables: variables, 64 RunningContext: &testkube.RunningContext{ 65 Type_: string(testkube.RunningContextTypeTestTrigger), 66 Context: t.Name, 67 }, 68 } 69 70 wp := workerpool.New[testkube.Test, testkube.ExecutionRequest, testkube.Execution](concurrencyLevel) 71 go func() { 72 isDelayDefined := t.Spec.Delay != nil 73 if isDelayDefined { 74 s.logger.Infof( 75 "trigger service: executor component: trigger %s/%s has delayed test execution configured for %f seconds", 76 t.Namespace, t.Name, t.Spec.Delay.Seconds(), 77 ) 78 time.Sleep(t.Spec.Delay.Duration) 79 } 80 s.logger.Infof( 81 "trigger service: executor component: scheduling test executions for trigger %s/%s", 82 t.Namespace, t.Name, 83 ) 84 go wp.SendRequests(s.scheduler.PrepareTestRequests(tests, request)) 85 go wp.Run(ctx) 86 }() 87 88 for r := range wp.GetResponses() { 89 status.addExecutionID(r.Result.Id) 90 } 91 case ExecutionTestSuite: 92 testSuites, err := s.getTestSuites(t) 93 if err != nil { 94 return err 95 } 96 97 request := testkube.TestSuiteExecutionRequest{ 98 Variables: variables, 99 RunningContext: &testkube.RunningContext{ 100 Type_: string(testkube.RunningContextTypeTestTrigger), 101 Context: t.Name, 102 }, 103 } 104 105 wp := workerpool.New[testkube.TestSuite, testkube.TestSuiteExecutionRequest, testkube.TestSuiteExecution](concurrencyLevel) 106 go func() { 107 isDelayDefined := t.Spec.Delay != nil 108 if isDelayDefined { 109 s.logger.Infof( 110 "trigger service: executor component: trigger %s/%s has delayed testsuite execution configured for %f seconds", 111 t.Namespace, t.Name, t.Spec.Delay.Seconds(), 112 ) 113 time.Sleep(t.Spec.Delay.Duration) 114 } 115 s.logger.Infof( 116 "trigger service: executor component: scheduling testsuite executions for trigger %s/%s", 117 t.Namespace, t.Name, 118 ) 119 go wp.SendRequests(s.scheduler.PrepareTestSuiteRequests(testSuites, request)) 120 go wp.Run(ctx) 121 }() 122 123 for r := range wp.GetResponses() { 124 status.addTestSuiteExecutionID(r.Result.Id) 125 } 126 default: 127 return errors.Errorf("invalid execution: %s", t.Spec.Execution) 128 } 129 130 status.start() 131 s.logger.Debugf("trigger service: executor component: started test execution for trigger %s/%s", t.Namespace, t.Name) 132 133 return nil 134 } 135 136 func (s *Service) getTests(t *testtriggersv1.TestTrigger) ([]testsv3.Test, error) { 137 var tests []testsv3.Test 138 if t.Spec.TestSelector.Name != "" { 139 s.logger.Debugf("trigger service: executor component: fetching testsv3.Test with name %s", t.Spec.TestSelector.Name) 140 test, err := s.testsClient.Get(t.Spec.TestSelector.Name) 141 if err != nil { 142 return nil, err 143 } 144 tests = append(tests, *test) 145 } 146 147 if t.Spec.TestSelector.NameRegex != "" { 148 s.logger.Debugf("trigger service: executor component: fetching testsv3.Test with name regex %s", t.Spec.TestSelector.NameRegex) 149 testList, err := s.testsClient.List("") 150 if err != nil { 151 return nil, err 152 } 153 154 re, err := regexp.Compile(t.Spec.TestSelector.NameRegex) 155 if err != nil { 156 return nil, err 157 } 158 159 for i := range testList.Items { 160 if re.MatchString(testList.Items[i].Name) { 161 tests = append(tests, testList.Items[i]) 162 } 163 } 164 } 165 166 if t.Spec.TestSelector.LabelSelector != nil { 167 selector, err := metav1.LabelSelectorAsSelector(t.Spec.TestSelector.LabelSelector) 168 if err != nil { 169 return nil, errors.WithMessagef(err, "error creating selector from test resource label selector") 170 } 171 stringifiedSelector := selector.String() 172 s.logger.Debugf("trigger service: executor component: fetching testsv3.Test with labels %s", stringifiedSelector) 173 testList, err := s.testsClient.List(stringifiedSelector) 174 if err != nil { 175 return nil, err 176 } 177 tests = append(tests, testList.Items...) 178 } 179 return tests, nil 180 } 181 182 func (s *Service) getTestSuites(t *testtriggersv1.TestTrigger) ([]testsuitesv3.TestSuite, error) { 183 var testSuites []testsuitesv3.TestSuite 184 if t.Spec.TestSelector.Name != "" { 185 s.logger.Debugf("trigger service: executor component: fetching testsuitesv3.TestSuite with name %s", t.Spec.TestSelector.Name) 186 testSuite, err := s.testSuitesClient.Get(t.Spec.TestSelector.Name) 187 if err != nil { 188 return nil, err 189 } 190 testSuites = append(testSuites, *testSuite) 191 } 192 193 if t.Spec.TestSelector.NameRegex != "" { 194 s.logger.Debugf("trigger service: executor component: fetching testsuitesv3.TestSuite with name regex %s", t.Spec.TestSelector.NameRegex) 195 testSuitesList, err := s.testSuitesClient.List("") 196 if err != nil { 197 return nil, err 198 } 199 200 re, err := regexp.Compile(t.Spec.TestSelector.NameRegex) 201 if err != nil { 202 return nil, err 203 } 204 205 for i := range testSuitesList.Items { 206 if re.MatchString(testSuitesList.Items[i].Name) { 207 testSuites = append(testSuites, testSuitesList.Items[i]) 208 } 209 } 210 } 211 212 if t.Spec.TestSelector.LabelSelector != nil { 213 selector, err := metav1.LabelSelectorAsSelector(t.Spec.TestSelector.LabelSelector) 214 if err != nil { 215 return nil, errors.WithMessagef(err, "error creating selector from test resource label selector") 216 } 217 stringifiedSelector := selector.String() 218 s.logger.Debugf("trigger service: executor component: fetching testsuitesv3.TestSuite with label %s", stringifiedSelector) 219 testSuitesList, err := s.testSuitesClient.List(stringifiedSelector) 220 if err != nil { 221 return nil, err 222 } 223 testSuites = append(testSuites, testSuitesList.Items...) 224 } 225 return testSuites, nil 226 }