Files
kubectl-unready/cmd/cmd.go
2026-04-07 15:52:35 +02:00

121 lines
2.7 KiB
Go

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
}