package cmd import ( "context" "fmt" "os" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" "k8s.io/client-go/kubernetes" ) func NewUnreadyCmd(ctx context.Context) *cobra.Command { var kubernetesConfigFlags *genericclioptions.ConfigFlags var allNamespacesFlag bool cmd := &cobra.Command{ Use: "unready", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { if kubernetesConfigFlags.Namespace == nil || *kubernetesConfigFlags.Namespace == "" { kubernetesConfigFlags.Namespace = new("default") } return run(ctx, kubernetesConfigFlags, allNamespacesFlag) }, } kubernetesConfigFlags = genericclioptions.NewConfigFlags(false) kubernetesConfigFlags.AddFlags(cmd.Flags()) cmd.Flags().BoolVarP(&allNamespacesFlag, "all-namespaces", "A", false, "If present, list unready pods across all namespaces.") return cmd } func run(ctx context.Context, configFlags *genericclioptions.ConfigFlags, allNamespaces bool) error { config, err := configFlags.ToRESTConfig() if err != nil { return fmt.Errorf("failed to read kubeconfig: %w", err) } clientset, err := kubernetes.NewForConfig(config) if err != nil { return fmt.Errorf("failed to create clientset: %w", err) } namespace := *configFlags.Namespace if allNamespaces { namespace = "" } pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{}) if err != nil { return fmt.Errorf("failed listing Pods: %w", err) } filtered := make([]corev1.Pod, 0) for _, pod := range pods.Items { if isNotReady(pod) { filtered = append(filtered, pod) } } return print(filtered) } func print(pods []corev1.Pod) error { table := metav1.Table{ ColumnDefinitions: []metav1.TableColumnDefinition{ {Name: "Namespace"}, {Name: "Name"}, {Name: "Ready"}, {Name: "Status"}, }, } for _, pod := range pods { table.Rows = append(table.Rows, metav1.TableRow{ Cells: []any{ pod.Namespace, pod.Name, containerStatuses(pod), pod.Status.Phase, }, }) } printer := printers.NewTablePrinter(printers.PrintOptions{}) return printer.PrintObj(&table, os.Stdout) } func containerStatuses(pod corev1.Pod) string { ready := 0 unready := 0 for _, status := range pod.Status.ContainerStatuses { if status.Ready { ready++ } else { unready++ } } return fmt.Sprintf("%d/%d", ready, ready+unready) } func isNotReady(pod corev1.Pod) bool { return hasUnreadyContainers(pod) && pod.Status.Phase != corev1.PodSucceeded } func hasUnreadyContainers(pod corev1.Pod) bool { for _, ctr := range pod.Status.ContainerStatuses { if !ctr.Ready { return true } } return false }