github.com/argoproj/argo-events@v1.9.1/test/stress/main.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "context" 6 "encoding/json" 7 "fmt" 8 "os" 9 "path/filepath" 10 "regexp" 11 "runtime" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/spf13/cobra" 17 corev1 "k8s.io/api/core/v1" 18 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/apimachinery/pkg/runtime/schema" 20 "k8s.io/client-go/dynamic" 21 "k8s.io/client-go/kubernetes" 22 _ "k8s.io/client-go/plugin/pkg/client/auth" 23 "k8s.io/client-go/rest" 24 "k8s.io/client-go/tools/clientcmd" 25 "sigs.k8s.io/controller-runtime/pkg/manager/signals" 26 "sigs.k8s.io/yaml" 27 28 "github.com/argoproj/argo-events/pkg/apis/eventbus" 29 eventbusv1alpha1 "github.com/argoproj/argo-events/pkg/apis/eventbus/v1alpha1" 30 "github.com/argoproj/argo-events/pkg/apis/eventsource" 31 eventsourcev1alpha1 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1" 32 "github.com/argoproj/argo-events/pkg/apis/sensor" 33 sensorv1alpha1 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1" 34 eventbusversiond "github.com/argoproj/argo-events/pkg/client/eventbus/clientset/versioned" 35 eventbuspkg "github.com/argoproj/argo-events/pkg/client/eventbus/clientset/versioned/typed/eventbus/v1alpha1" 36 eventsourceversiond "github.com/argoproj/argo-events/pkg/client/eventsource/clientset/versioned" 37 eventsourcepkg "github.com/argoproj/argo-events/pkg/client/eventsource/clientset/versioned/typed/eventsource/v1alpha1" 38 sensorversiond "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned" 39 sensorpkg "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned/typed/sensor/v1alpha1" 40 testutil "github.com/argoproj/argo-events/test/util" 41 ) 42 43 const ( 44 eventBusName = "stress-testing" 45 defaultTimeout = 60 * time.Second 46 47 Success = "success" 48 Failure = "failure" 49 50 First = "first" 51 Last = "last" 52 53 EventNameKey = "eventName" 54 TriggerNameKey = "triggerName" 55 56 StressTestingLabel = "argo-events-stress" 57 StressTestingLabelValue = "true" 58 59 logEventSourceStarted = "Eventing server started." 60 logSensorStarted = "Sensor started." 61 logTriggerActionSuccessful = "Successfully processed trigger" 62 logTriggerActionFailed = "Failed to execute a trigger" 63 logEventSuccessful = "Succeeded to publish an event" 64 logEventFailed = "Failed to publish an event" 65 ) 66 67 type TestingEventSource string 68 69 // possible values of TestingEventSource 70 const ( 71 UnsupportedEventsource TestingEventSource = "unsupported" 72 WebhookEventSource TestingEventSource = "webhook" 73 SQSEventSource TestingEventSource = "sqs" 74 SNSEventSource TestingEventSource = "sns" 75 KafkaEventSource TestingEventSource = "kafka" 76 NATSEventSource TestingEventSource = "nats" 77 RedisEventSource TestingEventSource = "redis" 78 ) 79 80 type EventBusType string 81 82 // possible value of EventBus type 83 const ( 84 UnsupportedEventBusType EventBusType = "unsupported" 85 STANEventBus EventBusType = "stan" 86 JetstreamEventBus EventBusType = "jetstream" 87 ) 88 89 type TestingTrigger string 90 91 // possible values of TestingTrigger 92 const ( 93 UnsupportedTrigger TestingTrigger = "unsupported" 94 WorkflowTrigger TestingTrigger = "workflow" 95 LogTrigger TestingTrigger = "log" 96 ) 97 98 var ( 99 background = metav1.DeletePropagationBackground 100 ) 101 102 type options struct { 103 namespace string 104 testingEventSource TestingEventSource 105 testingTrigger TestingTrigger 106 eventBusType EventBusType 107 esName string 108 sensorName string 109 // Inactive time before exiting 110 idleTimeout time.Duration 111 hardTimeout *time.Duration 112 noCleanUp bool 113 114 kubeClient kubernetes.Interface 115 eventBusClient eventbuspkg.EventBusInterface 116 eventSourceClient eventsourcepkg.EventSourceInterface 117 sensorClient sensorpkg.SensorInterface 118 restConfig *rest.Config 119 } 120 121 func NewOptions(testingEventSource TestingEventSource, testingTrigger TestingTrigger, eventBusType EventBusType, esName, sensorName string, idleTimeout time.Duration, noCleanUp bool) (*options, error) { 122 loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 123 configOverrides := &clientcmd.ConfigOverrides{} 124 kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) 125 config, err := kubeConfig.ClientConfig() 126 if err != nil { 127 return nil, err 128 } 129 namespace, _, _ := kubeConfig.Namespace() 130 kubeClient, err := kubernetes.NewForConfig(config) 131 if err != nil { 132 return nil, err 133 } 134 eventBusClient := eventbusversiond.NewForConfigOrDie(config).ArgoprojV1alpha1().EventBus(namespace) 135 eventSourceClient := eventsourceversiond.NewForConfigOrDie(config).ArgoprojV1alpha1().EventSources(namespace) 136 sensorClient := sensorversiond.NewForConfigOrDie(config).ArgoprojV1alpha1().Sensors(namespace) 137 return &options{ 138 namespace: namespace, 139 testingEventSource: testingEventSource, 140 testingTrigger: testingTrigger, 141 eventBusType: eventBusType, 142 esName: esName, 143 sensorName: sensorName, 144 kubeClient: kubeClient, 145 eventBusClient: eventBusClient, 146 eventSourceClient: eventSourceClient, 147 restConfig: config, 148 sensorClient: sensorClient, 149 idleTimeout: idleTimeout, 150 noCleanUp: noCleanUp, 151 }, nil 152 } 153 154 func (o *options) createEventBus(ctx context.Context) (*eventbusv1alpha1.EventBus, error) { 155 fmt.Printf("------- Creating %v EventBus -------\n", o.eventBusType) 156 eb := &eventbusv1alpha1.EventBus{} 157 if err := readResource(fmt.Sprintf("@testdata/eventbus/%v.yaml", o.eventBusType), eb); err != nil { 158 return nil, fmt.Errorf("failed to read %v event bus yaml file: %w", o.eventBusType, err) 159 } 160 l := eb.GetLabels() 161 if l == nil { 162 l = map[string]string{} 163 } 164 l[StressTestingLabel] = StressTestingLabelValue 165 eb.SetLabels(l) 166 eb.Name = eventBusName 167 result, err := o.eventBusClient.Create(ctx, eb, metav1.CreateOptions{}) 168 if err != nil { 169 return nil, fmt.Errorf("failed to create event bus: %w", err) 170 } 171 if err := testutil.WaitForEventBusReady(ctx, o.eventBusClient, eb.Name, defaultTimeout); err != nil { 172 return nil, fmt.Errorf("expected to see event bus ready: %w", err) 173 } 174 if err := testutil.WaitForEventBusStatefulSetReady(ctx, o.kubeClient, o.namespace, eb.Name, defaultTimeout); err != nil { 175 return nil, fmt.Errorf("expected to see event bus statefulset ready: %w", err) 176 } 177 return result, nil 178 } 179 180 func (o *options) createEventSource(ctx context.Context) (*eventsourcev1alpha1.EventSource, error) { 181 fmt.Printf("\n------- Creating %v EventSource -------\n", o.testingEventSource) 182 es := &eventsourcev1alpha1.EventSource{} 183 file := fmt.Sprintf("@testdata/eventsources/%v.yaml", o.testingEventSource) 184 if err := readResource(file, es); err != nil { 185 return nil, fmt.Errorf("failed to read %v event source yaml file: %w", o.testingEventSource, err) 186 } 187 l := es.GetLabels() 188 if l == nil { 189 l = map[string]string{} 190 } 191 l[StressTestingLabel] = StressTestingLabelValue 192 es.SetLabels(l) 193 es.Spec.EventBusName = eventBusName 194 result, err := o.eventSourceClient.Create(ctx, es, metav1.CreateOptions{}) 195 if err != nil { 196 return nil, fmt.Errorf("failed to create event source: %w", err) 197 } 198 if err := testutil.WaitForEventSourceReady(ctx, o.eventSourceClient, es.Name, defaultTimeout); err != nil { 199 return nil, fmt.Errorf("expected to see event source ready: %w", err) 200 } 201 if err := testutil.WaitForEventSourceDeploymentReady(ctx, o.kubeClient, o.namespace, es.Name, defaultTimeout); err != nil { 202 return nil, fmt.Errorf("expected to see event source deployment and pod ready: %w", err) 203 } 204 contains, err := testutil.EventSourcePodLogContains(ctx, o.kubeClient, o.namespace, es.Name, logEventSourceStarted) 205 if err != nil { 206 return nil, fmt.Errorf("expected to see event source pod contains something: %w", err) 207 } 208 if !contains { 209 return nil, fmt.Errorf("EventSource Pod does look good, it might have started failed") 210 } 211 return result, nil 212 } 213 214 func (o *options) createSensor(ctx context.Context) (*sensorv1alpha1.Sensor, error) { 215 fmt.Printf("\n------- Creating %v Sensor -------\n", o.testingTrigger) 216 sensor := &sensorv1alpha1.Sensor{} 217 file := fmt.Sprintf("@testdata/sensors/%v.yaml", o.testingTrigger) 218 if err := readResource(file, sensor); err != nil { 219 return nil, fmt.Errorf("failed to read %v sensor yaml file: %w", o.testingTrigger, err) 220 } 221 l := sensor.GetLabels() 222 if l == nil { 223 l = map[string]string{} 224 } 225 l[StressTestingLabel] = StressTestingLabelValue 226 sensor.SetLabels(l) 227 sensor.Spec.EventBusName = eventBusName 228 result, err := o.sensorClient.Create(ctx, sensor, metav1.CreateOptions{}) 229 if err != nil { 230 return nil, fmt.Errorf("failed to create sensor: %w", err) 231 } 232 if err := testutil.WaitForSensorReady(ctx, o.sensorClient, sensor.Name, defaultTimeout); err != nil { 233 return nil, fmt.Errorf("expected to see sensor ready: %w", err) 234 } 235 if err := testutil.WaitForSensorDeploymentReady(ctx, o.kubeClient, o.namespace, sensor.Name, defaultTimeout); err != nil { 236 return nil, fmt.Errorf("expected to see sensor deployment and pod ready: %w", err) 237 } 238 contains, err := testutil.SensorPodLogContains(ctx, o.kubeClient, o.namespace, sensor.Name, logSensorStarted) 239 if err != nil { 240 return nil, fmt.Errorf("expected to see sensor pod contains something: %w", err) 241 } 242 if !contains { 243 return nil, fmt.Errorf("Sensor Pod does look good, it might have started failed") 244 } 245 return result, nil 246 } 247 248 func (o *options) getEventSourcePodNames(ctx context.Context, eventSourceName string) ([]string, error) { 249 labelSelector := fmt.Sprintf("controller=eventsource-controller,eventsource-name=%s", eventSourceName) 250 esPodList, err := o.kubeClient.CoreV1().Pods(o.namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"}) 251 if err != nil { 252 return nil, err 253 } 254 results := []string{} 255 for _, i := range esPodList.Items { 256 results = append(results, i.Name) 257 } 258 return results, nil 259 } 260 261 func (o *options) getSensorPodNames(ctx context.Context, sensorName string) ([]string, error) { 262 labelSelector := fmt.Sprintf("controller=sensor-controller,sensor-name=%s", sensorName) 263 sPodList, err := o.kubeClient.CoreV1().Pods(o.namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"}) 264 if err != nil { 265 return nil, err 266 } 267 results := []string{} 268 for _, i := range sPodList.Items { 269 results = append(results, i.Name) 270 } 271 return results, nil 272 } 273 274 func (o *options) runTesting(ctx context.Context, eventSourceName, sensorName string) error { 275 esPodNames, err := o.getEventSourcePodNames(ctx, eventSourceName) 276 if err != nil { 277 return fmt.Errorf("failed to get event source pod names: %v", err) 278 } 279 if len(esPodNames) == 0 { 280 return fmt.Errorf("no pod found for event source %s", eventSourceName) 281 } 282 sensorPodNames, err := o.getSensorPodNames(ctx, sensorName) 283 if err != nil { 284 return fmt.Errorf("failed to get sensor pod names: %v", err) 285 } 286 if len(sensorPodNames) == 0 { 287 return fmt.Errorf("no pod found for sensor %s", sensorName) 288 } 289 290 successActionReg := regexp.MustCompile(logTriggerActionSuccessful) 291 failureActionReg := regexp.MustCompile(logTriggerActionFailed) 292 successEventReg := regexp.MustCompile(logEventSuccessful) 293 failureEventReg := regexp.MustCompile(logEventFailed) 294 295 fmt.Printf(` 296 ********************************************************* 297 The application will automatically exit: 298 - If there's no active events and actions in %v. 299 `, o.idleTimeout) 300 if o.hardTimeout != nil { 301 fmt.Printf(" - In %v after it starts.\n", *o.hardTimeout) 302 } 303 fmt.Printf(` 304 Or you can terminate it any time by Ctrl + C. 305 ********************************************************* 306 307 `) 308 309 esMap := map[string]int64{} 310 esTimeMap := map[string]time.Time{} 311 312 sensorMap := map[string]int64{} 313 sensorTimeMap := map[string]time.Time{} 314 315 var esLock = &sync.RWMutex{} 316 var sensorLock = &sync.RWMutex{} 317 318 startTime := time.Now() 319 320 wg := &sync.WaitGroup{} 321 for _, sensorPodName := range sensorPodNames { 322 wg.Add(1) 323 go func(podName string) { 324 defer wg.Done() 325 fmt.Printf("Started watching Sensor Pod %s ...\n", podName) 326 stream, err := o.kubeClient.CoreV1().Pods(o.namespace).GetLogs(podName, &corev1.PodLogOptions{Follow: true}).Stream(ctx) 327 if err != nil { 328 fmt.Printf("failed to acquire sensor pod %s log stream: %v", podName, err) 329 return 330 } 331 defer func() { _ = stream.Close() }() 332 333 sCh := make(chan string) 334 go func(dataCh chan string) { 335 s := bufio.NewScanner(stream) 336 for { 337 if !s.Scan() { 338 fmt.Printf("Can not read: %v\n", s.Err()) 339 close(dataCh) 340 return 341 } 342 data := s.Bytes() 343 triggerName := getLogValue(data, startTime, TriggerNameKey) 344 if triggerName == "" { 345 continue 346 } 347 if successActionReg.Match(data) { 348 dataCh <- triggerName + "/" + Success 349 } else if failureActionReg.Match(data) { 350 dataCh <- triggerName + "/" + Failure 351 } 352 } 353 }(sCh) 354 355 for { 356 if o.hardTimeout != nil && time.Since(startTime).Seconds() > o.hardTimeout.Seconds() { 357 fmt.Printf("Exited Sensor Pod %s due to the hard timeout %v\n", podName, *o.hardTimeout) 358 return 359 } 360 timeout := 5 * 60 * time.Second 361 lastActionTime := startTime 362 sensorLock.RLock() 363 if len(sensorMap) > 0 && len(sensorTimeMap) > 0 { 364 timeout = o.idleTimeout 365 for _, v := range sensorTimeMap { 366 if v.After(lastActionTime) { 367 lastActionTime = v 368 } 369 } 370 } 371 sensorLock.RUnlock() 372 373 if time.Since(lastActionTime).Seconds() > timeout.Seconds() { 374 fmt.Printf("Exited Sensor Pod %s due to no actions in the last %v\n", podName, o.idleTimeout) 375 return 376 } 377 select { 378 case <-ctx.Done(): 379 return 380 case data, ok := <-sCh: 381 if !ok { 382 return 383 } 384 // e.g. triggerName/success 385 t := strings.Split(data, "/") 386 sensorLock.Lock() 387 if _, ok := sensorMap[data]; !ok { 388 sensorMap[t[0]+"/"+Success] = 0 389 sensorMap[t[0]+"/"+Failure] = 0 390 } 391 if t[1] == Success { 392 sensorMap[t[0]+"/"+Success]++ 393 } else { 394 sensorMap[t[0]+"/"+Failure]++ 395 } 396 if sensorMap[t[0]+"/"+Success]+sensorMap[t[0]+"/"+Failure] == 1 { 397 sensorTimeMap[t[0]+"/"+First] = time.Now() 398 } 399 sensorTimeMap[t[0]+"/"+Last] = time.Now() 400 sensorLock.Unlock() 401 default: 402 } 403 } 404 }(sensorPodName) 405 } 406 407 for _, esPodName := range esPodNames { 408 wg.Add(1) 409 go func(podName string) { 410 defer wg.Done() 411 fmt.Printf("Started watching EventSource Pod %s ...\n", podName) 412 stream, err := o.kubeClient.CoreV1().Pods(o.namespace).GetLogs(podName, &corev1.PodLogOptions{Follow: true}).Stream(ctx) 413 if err != nil { 414 fmt.Printf("failed to acquire event source pod %s log stream: %v", podName, err) 415 return 416 } 417 defer func() { _ = stream.Close() }() 418 419 sCh := make(chan string) 420 go func(dataCh chan string) { 421 s := bufio.NewScanner(stream) 422 for { 423 if !s.Scan() { 424 fmt.Printf("Can not read: %v\n", s.Err()) 425 close(dataCh) 426 return 427 } 428 data := s.Bytes() 429 eventName := getLogValue(data, startTime, EventNameKey) 430 if eventName == "" { 431 continue 432 } 433 if successEventReg.Match(data) { 434 dataCh <- eventName + "/" + Success 435 } else if failureEventReg.Match(data) { 436 dataCh <- eventName + "/" + Failure 437 } 438 } 439 }(sCh) 440 441 for { 442 if o.hardTimeout != nil && time.Since(startTime).Seconds() > o.hardTimeout.Seconds() { 443 fmt.Printf("Exited EventSource Pod %s due to the hard timeout %v\n", podName, *o.hardTimeout) 444 return 445 } 446 timeout := 5 * 60 * time.Second 447 lastEventTime := startTime 448 449 esLock.RLock() 450 if len(esMap) > 0 && len(esTimeMap) > 0 { 451 timeout = o.idleTimeout 452 for _, v := range esTimeMap { 453 if v.After(lastEventTime) { 454 lastEventTime = v 455 } 456 } 457 } 458 esLock.RUnlock() 459 if time.Since(lastEventTime).Seconds() > timeout.Seconds() { 460 fmt.Printf("Exited EventSource Pod %s due to no active events in the last %v\n", podName, o.idleTimeout) 461 return 462 } 463 select { 464 case <-ctx.Done(): 465 return 466 case data, ok := <-sCh: 467 if !ok { 468 return 469 } 470 // e.g. eventName/success 471 t := strings.Split(data, "/") 472 esLock.Lock() 473 if _, ok := esMap[data]; !ok { 474 esMap[t[0]+"/"+Success] = 0 475 esMap[t[0]+"/"+Failure] = 0 476 } 477 if t[1] == Success { 478 esMap[t[0]+"/"+Success]++ 479 } else { 480 esMap[t[0]+"/"+Failure]++ 481 } 482 if esMap[t[0]+"/"+Success]+esMap[t[0]+"/"+Failure] == 1 { 483 esTimeMap[t[0]+"/"+First] = time.Now() 484 } 485 esTimeMap[t[0]+"/"+Last] = time.Now() 486 esLock.Unlock() 487 default: 488 } 489 } 490 }(esPodName) 491 } 492 493 wg.Wait() 494 495 time.Sleep(3 * time.Second) 496 eventNames := []string{} 497 for k := range esMap { 498 t := strings.Split(k, "/") 499 if t[1] == Success { 500 eventNames = append(eventNames, t[0]) 501 } 502 } 503 triggerNames := []string{} 504 for k := range sensorMap { 505 t := strings.Split(k, "/") 506 if t[1] == Success { 507 triggerNames = append(triggerNames, t[0]) 508 } 509 } 510 fmt.Printf("\n++++++++++++++++++++++++ Events Summary +++++++++++++++++++++++\n") 511 if len(eventNames) == 0 { 512 fmt.Println("No events.") 513 } else { 514 for _, eventName := range eventNames { 515 fmt.Printf("Event Name : %s\n", eventName) 516 fmt.Printf("Total processed events : %d\n", esMap[eventName+"/"+Success]+esMap[eventName+"/"+Failure]) 517 fmt.Printf("Events sent successful : %d\n", esMap[eventName+"/"+Success]) 518 fmt.Printf("Events sent failed : %d\n", esMap[eventName+"/"+Failure]) 519 fmt.Printf("First event sent at : %v\n", esTimeMap[eventName+"/"+First]) 520 fmt.Printf("Last event sent at : %v\n", esTimeMap[eventName+"/"+Last]) 521 fmt.Printf("Total time taken : %v\n", esTimeMap[eventName+"/"+Last].Sub(esTimeMap[eventName+"/"+First])) 522 fmt.Println("--") 523 } 524 } 525 526 fmt.Printf("\n+++++++++++++++++++++++ Actions Summary +++++++++++++++++++++++\n") 527 if len(triggerNames) == 0 { 528 fmt.Println("No actions.") 529 } else { 530 for _, triggerName := range triggerNames { 531 fmt.Printf("Trigger Name : %s\n", triggerName) 532 fmt.Printf("Total triggered actions : %d\n", sensorMap[triggerName+"/"+Success]+sensorMap[triggerName+"/"+Failure]) 533 fmt.Printf("Action triggered successfully : %d\n", sensorMap[triggerName+"/"+Success]) 534 fmt.Printf("Action triggered failed : %d\n", sensorMap[triggerName+"/"+Failure]) 535 fmt.Printf("First action triggered at : %v\n", sensorTimeMap[triggerName+"/"+First]) 536 fmt.Printf("Last action triggered at : %v\n", sensorTimeMap[triggerName+"/"+Last]) 537 fmt.Printf("Total time taken : %v\n", sensorTimeMap[triggerName+"/"+Last].Sub(sensorTimeMap[triggerName+"/"+First])) 538 fmt.Println("--") 539 } 540 } 541 return nil 542 } 543 544 // Check if it a valid log in JSON format, which contains something 545 // like `"ts":1616093369.2583323`, and if it's later than start, 546 // return the value of a log key 547 func getLogValue(log []byte, start time.Time, key string) string { 548 t := make(map[string]interface{}) 549 if err := json.Unmarshal(log, &t); err != nil { 550 // invalid json format log 551 return "" 552 } 553 ts, ok := t["ts"] 554 if !ok { 555 return "" 556 } 557 s, ok := ts.(float64) 558 if !ok { 559 return "" 560 } 561 if float64(start.Unix()) > s { 562 // old log 563 return "" 564 } 565 v, ok := t[key] 566 if !ok { 567 return "" 568 } 569 return fmt.Sprintf("%v", v) 570 } 571 572 func (o *options) dynamicFor(r schema.GroupVersionResource) dynamic.ResourceInterface { 573 resourceInterface := dynamic.NewForConfigOrDie(o.restConfig).Resource(r) 574 return resourceInterface.Namespace(o.namespace) 575 } 576 577 func (o *options) cleanUpResources(ctx context.Context) error { 578 hasTestLabel := metav1.ListOptions{LabelSelector: StressTestingLabel} 579 resources := []schema.GroupVersionResource{ 580 {Group: eventsource.Group, Version: "v1alpha1", Resource: eventsource.Plural}, 581 {Group: sensor.Group, Version: "v1alpha1", Resource: sensor.Plural}, 582 {Group: eventbus.Group, Version: "v1alpha1", Resource: eventbus.Plural}, 583 } 584 for _, r := range resources { 585 if err := o.dynamicFor(r).DeleteCollection(ctx, metav1.DeleteOptions{PropagationPolicy: &background}, hasTestLabel); err != nil { 586 return err 587 } 588 } 589 590 for _, r := range resources { 591 for { 592 list, err := o.dynamicFor(r).List(ctx, hasTestLabel) 593 if err != nil { 594 return err 595 } 596 if len(list.Items) == 0 { 597 break 598 } 599 time.Sleep(100 * time.Millisecond) 600 } 601 } 602 return nil 603 } 604 605 func (o *options) Start(ctx context.Context) error { 606 fmt.Println("#################### Preparing ####################") 607 608 esName := o.esName 609 sensorName := o.sensorName 610 611 // Need to create 612 if esName == "" && sensorName == "" { 613 // Clean up resources if any 614 if err := o.cleanUpResources(ctx); err != nil { 615 return err 616 } 617 time.Sleep(10 * time.Second) 618 619 // Create EventBus 620 eb, err := o.createEventBus(ctx) 621 if err != nil { 622 return err 623 } 624 625 defer func() { 626 if !o.noCleanUp { 627 _ = o.eventBusClient.Delete(ctx, eb.Name, metav1.DeleteOptions{}) 628 } 629 }() 630 631 time.Sleep(5 * time.Second) 632 633 // Create Sensor 634 sensor, err := o.createSensor(ctx) 635 if err != nil { 636 return err 637 } 638 defer func() { 639 if !o.noCleanUp { 640 _ = o.sensorClient.Delete(ctx, sensor.Name, metav1.DeleteOptions{}) 641 } 642 }() 643 644 // Create Event Source 645 es, err := o.createEventSource(ctx) 646 if err != nil { 647 return err 648 } 649 defer func() { 650 if !o.noCleanUp { 651 _ = o.eventSourceClient.Delete(ctx, es.Name, metav1.DeleteOptions{}) 652 } 653 }() 654 655 esName = es.Name 656 sensorName = sensor.Name 657 } else { 658 fmt.Printf("------- Use existing EventSource and Sensor -------\n") 659 fmt.Printf("EventSource name : %s\n", esName) 660 fmt.Printf("Sensor name : %s\n", sensorName) 661 } 662 663 // Run testing 664 fmt.Println("") 665 fmt.Println("################# Started Testing #################") 666 667 return o.runTesting(ctx, esName, sensorName) 668 } 669 670 func readResource(text string, v metav1.Object) error { 671 var data []byte 672 var err error 673 if strings.HasPrefix(text, "@") { 674 file := strings.TrimPrefix(text, "@") 675 _, fileName, _, _ := runtime.Caller(0) 676 data, err = os.ReadFile(filepath.Dir(fileName) + "/" + file) 677 if err != nil { 678 return fmt.Errorf("failed to read a file: %w", err) 679 } 680 } else { 681 data = []byte(text) 682 } 683 if err = yaml.Unmarshal(data, v); err != nil { 684 return fmt.Errorf("failed to unmarshal the yaml: %w", err) 685 } 686 return nil 687 } 688 689 func getTestingEventSource(str string) TestingEventSource { 690 switch str { 691 case "webhook": 692 return WebhookEventSource 693 case "sqs": 694 return SQSEventSource 695 case "sns": 696 return SNSEventSource 697 case "kafka": 698 return KafkaEventSource 699 case "redis": 700 return RedisEventSource 701 case "nats": 702 return NATSEventSource 703 default: 704 return UnsupportedEventsource 705 } 706 } 707 708 func getEventBusType(str string) EventBusType { 709 switch str { 710 case "jetstream": 711 return JetstreamEventBus 712 case "stan": 713 return STANEventBus 714 default: 715 return UnsupportedEventBusType 716 } 717 } 718 719 func getTestingTrigger(str string) TestingTrigger { 720 switch str { 721 case "log": 722 return LogTrigger 723 case "workflow": 724 return WorkflowTrigger 725 default: 726 return UnsupportedTrigger 727 } 728 } 729 730 func main() { 731 var ( 732 ebTypeStr string 733 esTypeStr string 734 triggerTypeStr string 735 esName string 736 sensorName string 737 idleTimeoutStr string 738 hardTimeoutStr string 739 noCleanUp bool 740 ) 741 var rootCmd = &cobra.Command{ 742 Use: "go run ./test/stress/main.go", 743 Short: "Argo Events stress testing.", 744 Long: ``, 745 Run: func(cmd *cobra.Command, args []string) { 746 esType := getTestingEventSource(esTypeStr) 747 triggerType := getTestingTrigger(triggerTypeStr) 748 if esName == "" && sensorName == "" { 749 if esType == UnsupportedEventsource { 750 fmt.Printf("Invalid event source %s\n\n", esTypeStr) 751 cmd.HelpFunc()(cmd, args) 752 os.Exit(1) 753 } 754 755 if triggerType == UnsupportedTrigger { 756 fmt.Printf("Invalid trigger %s\n\n", triggerTypeStr) 757 cmd.HelpFunc()(cmd, args) 758 os.Exit(1) 759 } 760 } else if (esName == "" && sensorName != "") || (esName != "" && sensorName == "") { 761 fmt.Printf("Both event source name and sensor name need to be specified\n\n") 762 cmd.HelpFunc()(cmd, args) 763 os.Exit(1) 764 } 765 eventBusType := getEventBusType(ebTypeStr) 766 if eventBusType == UnsupportedEventBusType { 767 fmt.Printf("Invalid event bus type %s\n\n", ebTypeStr) 768 cmd.HelpFunc()(cmd, args) 769 os.Exit(1) 770 } 771 772 idleTimeout, err := time.ParseDuration(idleTimeoutStr) 773 if err != nil { 774 fmt.Printf("Invalid idle timeout %s: %v\n\n", idleTimeoutStr, err) 775 cmd.HelpFunc()(cmd, args) 776 os.Exit(1) 777 } 778 opts, err := NewOptions(esType, triggerType, eventBusType, esName, sensorName, idleTimeout, noCleanUp) 779 if err != nil { 780 fmt.Printf("Failed: %v\n", err) 781 os.Exit(1) 782 } 783 if hardTimeoutStr != "" { 784 hardTimeout, err := time.ParseDuration(hardTimeoutStr) 785 if err != nil { 786 fmt.Printf("Invalid hard timeout %s: %v\n\n", hardTimeoutStr, err) 787 cmd.HelpFunc()(cmd, args) 788 os.Exit(1) 789 } 790 opts.hardTimeout = &hardTimeout 791 } 792 ctx := signals.SetupSignalHandler() 793 if err = opts.Start(ctx); err != nil { 794 panic(err) 795 } 796 }, 797 } 798 rootCmd.Flags().StringVarP(&ebTypeStr, "eb-type", "b", "", "Type of event bus to be tested: stan, jetstream") 799 rootCmd.Flags().StringVarP(&esTypeStr, "es-type", "e", "", "Type of event source to be tested, e.g. webhook, sqs, etc.") 800 rootCmd.Flags().StringVarP(&triggerTypeStr, "trigger-type", "t", string(LogTrigger), "Type of trigger to be tested, e.g. log, workflow.") 801 rootCmd.Flags().StringVar(&esName, "es-name", "", "Name of an existing event source to be tested") 802 rootCmd.Flags().StringVar(&sensorName, "sensor-name", "", "Name of an existing sensor to be tested.") 803 rootCmd.Flags().StringVar(&idleTimeoutStr, "idle-timeout", "60s", "Exit in a period of time without any active events or actions. e.g. 30s, 2m.") 804 rootCmd.Flags().StringVar(&hardTimeoutStr, "hard-timeout", "", "Exit in a period of time after the testing starts. e.g. 120s, 5m. If it's specified, the application will exit at the time either hard-timeout or idle-timeout meets.") 805 rootCmd.Flags().BoolVar(&noCleanUp, "no-clean-up", false, "Whether to clean up the created resources.") 806 807 _ = rootCmd.Execute() 808 }