github.com/argoproj/argo-events@v1.9.1/test/util/util.go (about) 1 package util 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "regexp" 8 "sync" 9 "time" 10 11 corev1 "k8s.io/api/core/v1" 12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 "k8s.io/client-go/kubernetes" 14 15 eventbuspkg "github.com/argoproj/argo-events/pkg/client/eventbus/clientset/versioned/typed/eventbus/v1alpha1" 16 eventsourcepkg "github.com/argoproj/argo-events/pkg/client/eventsource/clientset/versioned/typed/eventsource/v1alpha1" 17 sensorpkg "github.com/argoproj/argo-events/pkg/client/sensor/clientset/versioned/typed/sensor/v1alpha1" 18 ) 19 20 func WaitForEventBusReady(ctx context.Context, eventBusClient eventbuspkg.EventBusInterface, eventBusName string, timeout time.Duration) error { 21 fieldSelector := "metadata.name=" + eventBusName 22 opts := metav1.ListOptions{FieldSelector: fieldSelector} 23 ctx, cancel := context.WithTimeout(ctx, timeout) 24 defer cancel() 25 for { 26 select { 27 case <-ctx.Done(): 28 return fmt.Errorf("timeout after %v waiting for EventBus ready", timeout) 29 default: 30 } 31 ebList, err := eventBusClient.List(ctx, opts) 32 if err != nil { 33 return fmt.Errorf("error getting EventBus list: %w", err) 34 } 35 if len(ebList.Items) > 0 && ebList.Items[0].Status.IsReady() { 36 return nil 37 } 38 time.Sleep(1 * time.Second) 39 } 40 } 41 42 func WaitForEventBusStatefulSetReady(ctx context.Context, kubeClient kubernetes.Interface, namespace, eventBusName string, timeout time.Duration) error { 43 labelSelector := fmt.Sprintf("controller=eventbus-controller,eventbus-name=%s", eventBusName) 44 opts := metav1.ListOptions{LabelSelector: labelSelector} 45 ctx, cancel := context.WithTimeout(ctx, timeout) 46 defer cancel() 47 for { 48 select { 49 case <-ctx.Done(): 50 return fmt.Errorf("timeout after %v waiting for EventBus StatefulSet ready", timeout) 51 default: 52 } 53 stsList, err := kubeClient.AppsV1().StatefulSets(namespace).List(ctx, opts) 54 if err != nil { 55 return fmt.Errorf("error getting EventBus StatefulSet list: %w", err) 56 } 57 if len(stsList.Items) > 0 && stsList.Items[0].Status.Replicas == stsList.Items[0].Status.ReadyReplicas { 58 return nil 59 } 60 time.Sleep(1 * time.Second) 61 } 62 } 63 64 func WaitForEventSourceReady(ctx context.Context, eventSourceClient eventsourcepkg.EventSourceInterface, eventSourceName string, timeout time.Duration) error { 65 fieldSelector := "metadata.name=" + eventSourceName 66 opts := metav1.ListOptions{FieldSelector: fieldSelector} 67 ctx, cancel := context.WithTimeout(ctx, timeout) 68 defer cancel() 69 for { 70 select { 71 case <-ctx.Done(): 72 return fmt.Errorf("timeout after %v waiting for EventSource ready", timeout) 73 default: 74 } 75 esList, err := eventSourceClient.List(ctx, opts) 76 if err != nil { 77 return fmt.Errorf("error getting EventSource list: %w", err) 78 } 79 if len(esList.Items) > 0 && esList.Items[0].Status.IsReady() { 80 return nil 81 } 82 time.Sleep(1 * time.Second) 83 } 84 } 85 86 func WaitForEventSourceDeploymentReady(ctx context.Context, kubeClient kubernetes.Interface, namespace, eventSourceName string, timeout time.Duration) error { 87 labelSelector := fmt.Sprintf("controller=eventsource-controller,eventsource-name=%s", eventSourceName) 88 return waitForDeploymentAndPodReady(ctx, kubeClient, namespace, "EventSource", labelSelector, timeout) 89 } 90 91 func WaitForSensorReady(ctx context.Context, sensorClient sensorpkg.SensorInterface, sensorName string, timeout time.Duration) error { 92 fieldSelector := "metadata.name=" + sensorName 93 opts := metav1.ListOptions{FieldSelector: fieldSelector} 94 ctx, cancel := context.WithTimeout(ctx, timeout) 95 defer cancel() 96 for { 97 select { 98 case <-ctx.Done(): 99 return fmt.Errorf("timeout after %v waiting for Sensor ready", timeout) 100 default: 101 } 102 sensorList, err := sensorClient.List(ctx, opts) 103 if err != nil { 104 return fmt.Errorf("error getting Sensor list: %w", err) 105 } 106 if len(sensorList.Items) > 0 && sensorList.Items[0].Status.IsReady() { 107 return nil 108 } 109 time.Sleep(1 * time.Second) 110 } 111 } 112 113 func WaitForSensorDeploymentReady(ctx context.Context, kubeClient kubernetes.Interface, namespace, sensorName string, timeout time.Duration) error { 114 labelSelector := fmt.Sprintf("controller=sensor-controller,sensor-name=%s", sensorName) 115 return waitForDeploymentAndPodReady(ctx, kubeClient, namespace, "Sensor", labelSelector, timeout) 116 } 117 118 func waitForDeploymentAndPodReady(ctx context.Context, kubeClient kubernetes.Interface, namespace, objectType, labelSelector string, timeout time.Duration) error { 119 opts := metav1.ListOptions{LabelSelector: labelSelector} 120 ctx, cancel := context.WithTimeout(ctx, timeout) 121 defer cancel() 122 for { 123 select { 124 case <-ctx.Done(): 125 return fmt.Errorf("timeout after %v waiting for deployment ready", timeout) 126 default: 127 } 128 deployList, err := kubeClient.AppsV1().Deployments(namespace).List(ctx, opts) 129 if err != nil { 130 return fmt.Errorf("error getting deployment list: %w", err) 131 } 132 ok := len(deployList.Items) == 1 133 if !ok { 134 continue 135 } 136 ok = ok && deployList.Items[0].Status.Replicas == deployList.Items[0].Status.ReadyReplicas 137 podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"}) 138 if err != nil { 139 return fmt.Errorf("error getting deployment pod list: %w", err) 140 } 141 ok = ok && len(podList.Items) > 0 && len(podList.Items) == int(*deployList.Items[0].Spec.Replicas) 142 for _, p := range podList.Items { 143 ok = ok && p.Status.Phase == corev1.PodRunning 144 } 145 if ok { 146 return nil 147 } 148 time.Sleep(1 * time.Second) 149 } 150 } 151 152 type podLogCheckOptions struct { 153 timeout time.Duration 154 count int 155 } 156 157 func defaultPodLogCheckOptions() *podLogCheckOptions { 158 return &podLogCheckOptions{ 159 timeout: 15 * time.Second, 160 count: -1, 161 } 162 } 163 164 type PodLogCheckOption func(*podLogCheckOptions) 165 166 func PodLogCheckOptionWithTimeout(t time.Duration) PodLogCheckOption { 167 return func(o *podLogCheckOptions) { 168 o.timeout = t 169 } 170 } 171 172 func PodLogCheckOptionWithCount(c int) PodLogCheckOption { 173 return func(o *podLogCheckOptions) { 174 o.count = c 175 } 176 } 177 178 func EventSourcePodLogContains(ctx context.Context, kubeClient kubernetes.Interface, namespace, eventSourceName, regex string, options ...PodLogCheckOption) (bool, error) { 179 labelSelector := fmt.Sprintf("controller=eventsource-controller,eventsource-name=%s", eventSourceName) 180 podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"}) 181 if err != nil { 182 return false, fmt.Errorf("error getting event source pod name: %w", err) 183 } 184 185 return PodsLogContains(ctx, kubeClient, namespace, regex, podList, options...), nil 186 } 187 188 func SensorPodLogContains(ctx context.Context, kubeClient kubernetes.Interface, namespace, sensorName, regex string, options ...PodLogCheckOption) (bool, error) { 189 labelSelector := fmt.Sprintf("controller=sensor-controller,sensor-name=%s", sensorName) 190 podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector, FieldSelector: "status.phase=Running"}) 191 if err != nil { 192 return false, fmt.Errorf("error getting sensor pod name: %w", err) 193 } 194 195 return PodsLogContains(ctx, kubeClient, namespace, regex, podList, options...), nil 196 } 197 198 func PodsLogContains(ctx context.Context, kubeClient kubernetes.Interface, namespace, regex string, podList *corev1.PodList, options ...PodLogCheckOption) bool { 199 // parse options 200 o := defaultPodLogCheckOptions() 201 for _, opt := range options { 202 if opt != nil { 203 opt(o) 204 } 205 } 206 207 cctx, cancel := context.WithTimeout(ctx, o.timeout) 208 defer cancel() 209 errChan := make(chan error) 210 resultChan := make(chan bool) 211 wg := &sync.WaitGroup{} 212 for _, p := range podList.Items { 213 wg.Add(1) 214 go func(podName string) { 215 defer wg.Done() 216 fmt.Printf("Watching POD: %s\n", podName) 217 var contains bool 218 var err error 219 if o.count == -1 { 220 contains, err = podLogContains(cctx, kubeClient, namespace, podName, regex) 221 } else { 222 contains, err = podLogContainsCount(cctx, kubeClient, namespace, podName, regex, o.count) 223 } 224 if err != nil { 225 errChan <- err 226 return 227 } 228 if contains { 229 resultChan <- true 230 } 231 }(p.Name) 232 } 233 allDone := make(chan bool) 234 go func() { 235 wg.Wait() 236 close(allDone) 237 }() 238 for { 239 select { 240 case result := <-resultChan: 241 if result { 242 return true 243 } else { 244 fmt.Println("read resultChan but not true") 245 } 246 case err := <-errChan: 247 fmt.Printf("error: %v", err) 248 case <-allDone: 249 for len(resultChan) > 0 { 250 if x := <-resultChan; x { 251 return true 252 } 253 } 254 return false 255 } 256 } 257 } 258 259 // look for at least one instance of the regex string in the log 260 func podLogContains(ctx context.Context, client kubernetes.Interface, namespace, podName, regex string) (bool, error) { 261 stream, err := client.CoreV1().Pods(namespace).GetLogs(podName, &corev1.PodLogOptions{Follow: true}).Stream(ctx) 262 if err != nil { 263 return false, err 264 } 265 defer func() { _ = stream.Close() }() 266 267 exp, err := regexp.Compile(regex) 268 if err != nil { 269 return false, err 270 } 271 272 s := bufio.NewScanner(stream) 273 for { 274 select { 275 case <-ctx.Done(): 276 return false, nil 277 default: 278 if !s.Scan() { 279 return false, s.Err() 280 } 281 data := s.Bytes() 282 fmt.Println(string(data)) 283 if exp.Match(data) { 284 return true, nil 285 } 286 } 287 } 288 } 289 290 // look for a specific number of instances of the regex string in the log 291 func podLogContainsCount(ctx context.Context, client kubernetes.Interface, namespace, podName, regex string, count int) (bool, error) { 292 stream, err := client.CoreV1().Pods(namespace).GetLogs(podName, &corev1.PodLogOptions{Follow: true}).Stream(ctx) 293 if err != nil { 294 return false, err 295 } 296 defer func() { _ = stream.Close() }() 297 298 exp, err := regexp.Compile(regex) 299 if err != nil { 300 return false, err 301 } 302 303 instancesChan := make(chan struct{}) 304 305 // scan the log looking for matches 306 go func(ctx context.Context, instancesChan chan<- struct{}) { 307 s := bufio.NewScanner(stream) 308 for { 309 select { 310 case <-ctx.Done(): 311 return 312 default: 313 if !s.Scan() { 314 return 315 } 316 data := s.Bytes() 317 fmt.Println(string(data)) 318 if exp.Match(data) { 319 instancesChan <- struct{}{} 320 } 321 } 322 } 323 }(ctx, instancesChan) 324 325 actualCount := 0 326 for { 327 select { 328 case <-instancesChan: 329 actualCount++ 330 case <-ctx.Done(): 331 fmt.Printf("time:%v, count:%d,actualCount:%d\n", time.Now().Unix(), count, actualCount) 332 return count == actualCount, nil 333 } 334 } 335 } 336 337 func WaitForNoPodFound(ctx context.Context, kubeClient kubernetes.Interface, namespace, labelSelector string, timeout time.Duration) error { 338 ctx, cancel := context.WithTimeout(ctx, timeout) 339 defer cancel() 340 for { 341 podList, err := kubeClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: labelSelector}) 342 if err != nil { 343 return fmt.Errorf("error getting pod list: %w", err) 344 } 345 if len(podList.Items) == 0 { 346 return nil 347 } 348 select { 349 case <-ctx.Done(): 350 return fmt.Errorf("timeout waiting for pod disappearing") 351 default: 352 } 353 time.Sleep(2 * time.Second) 354 } 355 }