diff --git a/cmd/cmd.go b/cmd/cmd.go index 35b39fb..6907b2b 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -11,6 +11,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/duration" + kwatch "k8s.io/apimachinery/pkg/watch" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/client-go/kubernetes" @@ -19,6 +20,7 @@ import ( func NewUnreadyCmd(ctx context.Context) *cobra.Command { var kubernetesConfigFlags *genericclioptions.ConfigFlags var allNamespacesFlag bool + var watchFlag bool cmd := &cobra.Command{ Use: "unready", @@ -27,7 +29,7 @@ func NewUnreadyCmd(ctx context.Context) *cobra.Command { if kubernetesConfigFlags.Namespace == nil || *kubernetesConfigFlags.Namespace == "" { kubernetesConfigFlags.Namespace = new("default") } - return run(ctx, kubernetesConfigFlags, allNamespacesFlag) + return run(ctx, kubernetesConfigFlags, allNamespacesFlag, watchFlag) }, } @@ -35,13 +37,14 @@ func NewUnreadyCmd(ctx context.Context) *cobra.Command { kubernetesConfigFlags.AddFlags(cmd.Flags()) cmd.Flags().BoolVarP(&allNamespacesFlag, "all-namespaces", "A", false, "If present, list unready pods across all namespaces.") + cmd.Flags().BoolVarP(&watchFlag, "watch", "w", false, "Watch for changes to unready pods.") cmd.AddCommand(version.NewVersionCmd(ctx)) return cmd } -func run(ctx context.Context, configFlags *genericclioptions.ConfigFlags, allNamespaces bool) error { +func run(ctx context.Context, configFlags *genericclioptions.ConfigFlags, allNamespaces bool, doWatch bool) error { config, err := configFlags.ToRESTConfig() if err != nil { return fmt.Errorf("failed to read kubeconfig: %w", err) @@ -63,17 +66,52 @@ func run(ctx context.Context, configFlags *genericclioptions.ConfigFlags, allNam } filtered := make([]corev1.Pod, 0) - for _, pod := range pods.Items { if isNotReady(pod) { filtered = append(filtered, pod) } } - return print(filtered) + if err := printPods(filtered, false); err != nil { + return err + } + + if !doWatch { + return nil + } + + watcher, err := clientset.CoreV1().Pods(namespace).Watch(ctx, metav1.ListOptions{ + ResourceVersion: pods.ResourceVersion, + }) + if err != nil { + return fmt.Errorf("failed watching Pods: %w", err) + } + defer watcher.Stop() + + for { + select { + case <-ctx.Done(): + return nil + case event, ok := <-watcher.ResultChan(): + if !ok { + return nil + } + pod, ok := event.Object.(*corev1.Pod) + if !ok { + continue + } + if event.Type == kwatch.Added || event.Type == kwatch.Modified { + if isNotReady(*pod) { + if err := printPods([]corev1.Pod{*pod}, true); err != nil { + return err + } + } + } + } + } } -func print(pods []corev1.Pod) error { +func printPods(pods []corev1.Pod, noHeaders bool) error { table := metav1.Table{ ColumnDefinitions: []metav1.TableColumnDefinition{ {Name: "Namespace"}, @@ -95,7 +133,7 @@ func print(pods []corev1.Pod) error { }) } - printer := printers.NewTablePrinter(printers.PrintOptions{}) + printer := printers.NewTablePrinter(printers.PrintOptions{NoHeaders: noHeaders}) return printer.PrintObj(&table, os.Stdout) }