github.com/kubeshop/testkube@v1.17.23/pkg/triggers/matcher_test.go (about) 1 package triggers 2 3 import ( 4 "context" 5 "net/http" 6 "net/http/httptest" 7 "net/url" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 13 testtriggersv1 "github.com/kubeshop/testkube-operator/api/testtriggers/v1" 14 "github.com/kubeshop/testkube/pkg/log" 15 ) 16 17 func TestService_matchConditionsRetry(t *testing.T) { 18 t.Parallel() 19 20 retry := 0 21 e := &watcherEvent{ 22 resource: "deployment", 23 name: "test-deployment", 24 namespace: "testkube", 25 labels: nil, 26 object: nil, 27 eventType: "modified", 28 causes: nil, 29 conditionsGetter: func() ([]testtriggersv1.TestTriggerCondition, error) { 30 retry++ 31 status := testtriggersv1.FALSE_TestTriggerConditionStatuses 32 if retry == 1 { 33 status = testtriggersv1.TRUE_TestTriggerConditionStatuses 34 } 35 36 return []testtriggersv1.TestTriggerCondition{ 37 { 38 Type_: "Progressing", 39 Status: &status, 40 Reason: "NewReplicaSetAvailable", 41 Ttl: 60, 42 }, 43 { 44 Type_: "Available", 45 Status: &status, 46 }, 47 }, nil 48 }, 49 } 50 51 var timeout int32 = 1 52 status := testtriggersv1.TRUE_TestTriggerConditionStatuses 53 testTrigger1 := &testtriggersv1.TestTrigger{ 54 ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"}, 55 Spec: testtriggersv1.TestTriggerSpec{ 56 Resource: "deployment", 57 ResourceSelector: testtriggersv1.TestTriggerSelector{Name: "test-deployment"}, 58 Event: "modified", 59 ConditionSpec: &testtriggersv1.TestTriggerConditionSpec{ 60 Timeout: timeout, 61 Conditions: []testtriggersv1.TestTriggerCondition{ 62 { 63 Type_: "Progressing", 64 Status: &status, 65 Reason: "NewReplicaSetAvailable", 66 Ttl: 60, 67 }, 68 { 69 Type_: "Available", 70 Status: &status, 71 }, 72 }, 73 }, 74 Action: "run", 75 Execution: "test", 76 ConcurrencyPolicy: "allow", 77 TestSelector: testtriggersv1.TestTriggerSelector{Name: "some-test"}, 78 }, 79 } 80 statusKey1 := newStatusKey(testTrigger1.Namespace, testTrigger1.Name) 81 triggerStatus1 := &triggerStatus{testTrigger: testTrigger1} 82 s := &Service{ 83 defaultConditionsCheckBackoff: defaultConditionsCheckBackoff, 84 defaultConditionsCheckTimeout: defaultConditionsCheckTimeout, 85 triggerExecutor: func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error { 86 assert.Equal(t, "testkube", trigger.Namespace) 87 assert.Equal(t, "test-trigger-1", trigger.Name) 88 return nil 89 }, 90 triggerStatus: map[statusKey]*triggerStatus{statusKey1: triggerStatus1}, 91 logger: log.DefaultLogger, 92 } 93 94 err := s.match(context.Background(), e) 95 assert.NoError(t, err) 96 assert.Equal(t, 1, retry) 97 } 98 99 func TestService_matchConditionsTimeout(t *testing.T) { 100 t.Parallel() 101 102 e := &watcherEvent{ 103 resource: "deployment", 104 name: "test-deployment", 105 namespace: "testkube", 106 labels: nil, 107 object: nil, 108 eventType: "modified", 109 causes: nil, 110 conditionsGetter: func() ([]testtriggersv1.TestTriggerCondition, error) { 111 status := testtriggersv1.FALSE_TestTriggerConditionStatuses 112 return []testtriggersv1.TestTriggerCondition{ 113 { 114 Type_: "Progressing", 115 Status: &status, 116 Reason: "NewReplicaSetAvailable", 117 Ttl: 60, 118 }, 119 { 120 Type_: "Available", 121 Status: &status, 122 }, 123 }, nil 124 }, 125 } 126 127 var timeout int32 = 1 128 status := testtriggersv1.TRUE_TestTriggerConditionStatuses 129 testTrigger1 := &testtriggersv1.TestTrigger{ 130 ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"}, 131 Spec: testtriggersv1.TestTriggerSpec{ 132 Resource: "deployment", 133 ResourceSelector: testtriggersv1.TestTriggerSelector{Name: "test-deployment"}, 134 Event: "modified", 135 ConditionSpec: &testtriggersv1.TestTriggerConditionSpec{ 136 Timeout: timeout, 137 Conditions: []testtriggersv1.TestTriggerCondition{ 138 { 139 Type_: "Progressing", 140 Status: &status, 141 Reason: "NewReplicaSetAvailable", 142 Ttl: 60, 143 }, 144 { 145 Type_: "Available", 146 Status: &status, 147 }, 148 }, 149 }, 150 Action: "run", 151 Execution: "test", 152 ConcurrencyPolicy: "allow", 153 TestSelector: testtriggersv1.TestTriggerSelector{Name: "some-test"}, 154 }, 155 } 156 statusKey1 := newStatusKey(testTrigger1.Namespace, testTrigger1.Name) 157 triggerStatus1 := &triggerStatus{testTrigger: testTrigger1} 158 s := &Service{ 159 defaultConditionsCheckBackoff: defaultConditionsCheckBackoff, 160 defaultConditionsCheckTimeout: defaultConditionsCheckTimeout, 161 triggerExecutor: func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error { 162 assert.Equal(t, "testkube", trigger.Namespace) 163 assert.Equal(t, "test-trigger-1", trigger.Name) 164 return nil 165 }, 166 triggerStatus: map[statusKey]*triggerStatus{statusKey1: triggerStatus1}, 167 logger: log.DefaultLogger, 168 } 169 170 err := s.match(context.Background(), e) 171 assert.ErrorIs(t, err, ErrConditionTimeout) 172 } 173 174 func TestService_matchProbesMultiple(t *testing.T) { 175 t.Parallel() 176 177 e := &watcherEvent{ 178 resource: "deployment", 179 name: "test-deployment", 180 namespace: "testkube", 181 labels: nil, 182 object: nil, 183 eventType: "modified", 184 causes: nil, 185 } 186 187 srv1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 188 })) 189 defer srv1.Close() 190 191 url1, err := url.Parse(srv1.URL) 192 assert.NoError(t, err) 193 194 srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 195 })) 196 defer srv2.Close() 197 198 url2, err := url.Parse(srv2.URL) 199 assert.NoError(t, err) 200 201 testTrigger1 := &testtriggersv1.TestTrigger{ 202 ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"}, 203 Spec: testtriggersv1.TestTriggerSpec{ 204 Resource: "deployment", 205 ResourceSelector: testtriggersv1.TestTriggerSelector{Name: "test-deployment"}, 206 Event: "modified", 207 Action: "run", 208 Execution: "test", 209 ConcurrencyPolicy: "allow", 210 TestSelector: testtriggersv1.TestTriggerSelector{Name: "some-test"}, 211 ProbeSpec: &testtriggersv1.TestTriggerProbeSpec{ 212 Probes: []testtriggersv1.TestTriggerProbe{ 213 { 214 Scheme: url1.Scheme, 215 Host: url1.Host, 216 Path: url1.Path, 217 }, 218 { 219 Scheme: url2.Scheme, 220 Host: url2.Host, 221 Path: url2.Path, 222 }, 223 }, 224 }, 225 }, 226 } 227 228 statusKey1 := newStatusKey(testTrigger1.Namespace, testTrigger1.Name) 229 triggerStatus1 := &triggerStatus{testTrigger: testTrigger1} 230 s := &Service{ 231 defaultProbesCheckBackoff: defaultProbesCheckBackoff, 232 defaultProbesCheckTimeout: defaultProbesCheckTimeout, 233 triggerExecutor: func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error { 234 assert.Equal(t, "testkube", trigger.Namespace) 235 assert.Equal(t, "test-trigger-1", trigger.Name) 236 return nil 237 }, 238 triggerStatus: map[statusKey]*triggerStatus{statusKey1: triggerStatus1}, 239 logger: log.DefaultLogger, 240 httpClient: http.DefaultClient, 241 } 242 243 err = s.match(context.Background(), e) 244 assert.NoError(t, err) 245 } 246 247 func TestService_matchProbesTimeout(t *testing.T) { 248 t.Parallel() 249 250 e := &watcherEvent{ 251 resource: "deployment", 252 name: "test-deployment", 253 namespace: "testkube", 254 labels: nil, 255 object: nil, 256 eventType: "modified", 257 causes: nil, 258 } 259 260 srv1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 261 })) 262 defer srv1.Close() 263 264 url1, err := url.Parse(srv1.URL) 265 assert.NoError(t, err) 266 267 testTrigger1 := &testtriggersv1.TestTrigger{ 268 ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"}, 269 Spec: testtriggersv1.TestTriggerSpec{ 270 Resource: "deployment", 271 ResourceSelector: testtriggersv1.TestTriggerSelector{Name: "test-deployment"}, 272 Event: "modified", 273 Action: "run", 274 Execution: "test", 275 ConcurrencyPolicy: "allow", 276 TestSelector: testtriggersv1.TestTriggerSelector{Name: "some-test"}, 277 ProbeSpec: &testtriggersv1.TestTriggerProbeSpec{ 278 Timeout: 2, 279 Delay: 1, 280 Probes: []testtriggersv1.TestTriggerProbe{ 281 { 282 Scheme: url1.Scheme, 283 Host: url1.Host, 284 Path: url1.Path, 285 }, 286 { 287 Host: "fakehost", 288 }, 289 }, 290 }, 291 }, 292 } 293 294 statusKey1 := newStatusKey(testTrigger1.Namespace, testTrigger1.Name) 295 triggerStatus1 := &triggerStatus{testTrigger: testTrigger1} 296 s := &Service{ 297 defaultProbesCheckBackoff: defaultProbesCheckBackoff, 298 defaultProbesCheckTimeout: defaultProbesCheckTimeout, 299 triggerExecutor: func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error { 300 assert.Equal(t, "testkube", trigger.Namespace) 301 assert.Equal(t, "test-trigger-1", trigger.Name) 302 return nil 303 }, 304 triggerStatus: map[statusKey]*triggerStatus{statusKey1: triggerStatus1}, 305 logger: log.DefaultLogger, 306 httpClient: http.DefaultClient, 307 } 308 309 err = s.match(context.Background(), e) 310 assert.ErrorIs(t, err, ErrProbeTimeout) 311 312 } 313 314 func TestService_match(t *testing.T) { 315 t.Parallel() 316 317 e := &watcherEvent{ 318 resource: "deployment", 319 name: "test-deployment", 320 namespace: "testkube", 321 labels: nil, 322 object: nil, 323 eventType: "modified", 324 causes: nil, 325 conditionsGetter: func() ([]testtriggersv1.TestTriggerCondition, error) { 326 status := testtriggersv1.TRUE_TestTriggerConditionStatuses 327 return []testtriggersv1.TestTriggerCondition{ 328 { 329 Type_: "Progressing", 330 Status: &status, 331 Reason: "NewReplicaSetAvailable", 332 Ttl: 60, 333 }, 334 { 335 Type_: "Available", 336 Status: &status, 337 }, 338 }, nil 339 }, 340 } 341 342 srv1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 343 })) 344 defer srv1.Close() 345 346 url1, err := url.Parse(srv1.URL) 347 assert.NoError(t, err) 348 349 srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 350 })) 351 defer srv2.Close() 352 353 url2, err := url.Parse(srv2.URL) 354 assert.NoError(t, err) 355 356 status := testtriggersv1.TRUE_TestTriggerConditionStatuses 357 testTrigger1 := &testtriggersv1.TestTrigger{ 358 ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"}, 359 Spec: testtriggersv1.TestTriggerSpec{ 360 Resource: "deployment", 361 ResourceSelector: testtriggersv1.TestTriggerSelector{Name: "test-deployment"}, 362 Event: "modified", 363 ConditionSpec: &testtriggersv1.TestTriggerConditionSpec{ 364 Conditions: []testtriggersv1.TestTriggerCondition{ 365 { 366 Type_: "Progressing", 367 Status: &status, 368 Reason: "NewReplicaSetAvailable", 369 Ttl: 60, 370 }, 371 { 372 Type_: "Available", 373 Status: &status, 374 }, 375 }, 376 }, 377 ProbeSpec: &testtriggersv1.TestTriggerProbeSpec{ 378 Probes: []testtriggersv1.TestTriggerProbe{ 379 { 380 Scheme: url1.Scheme, 381 Host: url1.Host, 382 Path: url1.Path, 383 }, 384 { 385 Scheme: url2.Scheme, 386 Host: url2.Host, 387 Path: url2.Path, 388 }, 389 }, 390 }, 391 Action: "run", 392 Execution: "test", 393 ConcurrencyPolicy: "allow", 394 TestSelector: testtriggersv1.TestTriggerSelector{Name: "some-test"}, 395 }, 396 } 397 statusKey1 := newStatusKey(testTrigger1.Namespace, testTrigger1.Name) 398 triggerStatus1 := &triggerStatus{testTrigger: testTrigger1} 399 s := &Service{ 400 defaultConditionsCheckBackoff: defaultConditionsCheckBackoff, 401 defaultConditionsCheckTimeout: defaultConditionsCheckTimeout, 402 defaultProbesCheckBackoff: defaultProbesCheckBackoff, 403 defaultProbesCheckTimeout: defaultProbesCheckTimeout, 404 triggerExecutor: func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error { 405 assert.Equal(t, "testkube", trigger.Namespace) 406 assert.Equal(t, "test-trigger-1", trigger.Name) 407 return nil 408 }, 409 triggerStatus: map[statusKey]*triggerStatus{statusKey1: triggerStatus1}, 410 logger: log.DefaultLogger, 411 httpClient: http.DefaultClient, 412 } 413 414 err = s.match(context.Background(), e) 415 assert.NoError(t, err) 416 } 417 418 func TestService_matchRegex(t *testing.T) { 419 t.Parallel() 420 421 e := &watcherEvent{ 422 resource: "deployment", 423 name: "test-deployment", 424 namespace: "testkube", 425 labels: nil, 426 object: nil, 427 eventType: "modified", 428 causes: nil, 429 } 430 431 srv1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 432 })) 433 defer srv1.Close() 434 435 testTrigger1 := &testtriggersv1.TestTrigger{ 436 ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"}, 437 Spec: testtriggersv1.TestTriggerSpec{ 438 Resource: "deployment", 439 ResourceSelector: testtriggersv1.TestTriggerSelector{NameRegex: "test.*"}, 440 Event: "modified", 441 Action: "run", 442 Execution: "test", 443 ConcurrencyPolicy: "allow", 444 TestSelector: testtriggersv1.TestTriggerSelector{NameRegex: "some.*"}, 445 }, 446 } 447 statusKey1 := newStatusKey(testTrigger1.Namespace, testTrigger1.Name) 448 triggerStatus1 := &triggerStatus{testTrigger: testTrigger1} 449 s := &Service{ 450 defaultConditionsCheckBackoff: defaultConditionsCheckBackoff, 451 defaultConditionsCheckTimeout: defaultConditionsCheckTimeout, 452 defaultProbesCheckBackoff: defaultProbesCheckBackoff, 453 defaultProbesCheckTimeout: defaultProbesCheckTimeout, 454 triggerExecutor: func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error { 455 assert.Equal(t, "testkube", trigger.Namespace) 456 assert.Equal(t, "test-trigger-1", trigger.Name) 457 return nil 458 }, 459 triggerStatus: map[statusKey]*triggerStatus{statusKey1: triggerStatus1}, 460 logger: log.DefaultLogger, 461 httpClient: http.DefaultClient, 462 } 463 464 err := s.match(context.Background(), e) 465 assert.NoError(t, err) 466 } 467 468 func TestService_noMatch(t *testing.T) { 469 t.Parallel() 470 471 e := &watcherEvent{ 472 resource: "deployment", 473 name: "test-deployment", 474 namespace: "testkube", 475 labels: nil, 476 object: nil, 477 eventType: "modified", 478 causes: nil, 479 } 480 481 testTrigger1 := &testtriggersv1.TestTrigger{ 482 ObjectMeta: metav1.ObjectMeta{Namespace: "testkube", Name: "test-trigger-1"}, 483 Spec: testtriggersv1.TestTriggerSpec{ 484 Resource: "pod", 485 ResourceSelector: testtriggersv1.TestTriggerSelector{Name: "test-pod"}, 486 Event: "modified", 487 Action: "run", 488 Execution: "test", 489 ConcurrencyPolicy: "allow", 490 TestSelector: testtriggersv1.TestTriggerSelector{Name: "some-test"}, 491 }, 492 } 493 statusKey1 := newStatusKey(testTrigger1.Namespace, testTrigger1.Name) 494 triggerStatus1 := &triggerStatus{testTrigger: testTrigger1} 495 testExecutorF := func(ctx context.Context, e *watcherEvent, trigger *testtriggersv1.TestTrigger) error { 496 assert.Fail(t, "should not match event") 497 return nil 498 } 499 s := &Service{ 500 triggerExecutor: testExecutorF, 501 triggerStatus: map[statusKey]*triggerStatus{statusKey1: triggerStatus1}, 502 logger: log.DefaultLogger, 503 } 504 505 err := s.match(context.Background(), e) 506 assert.NoError(t, err) 507 }