diff --git a/util/kube/portforwarder.go b/util/kube/portforwarder.go index 94875baa99e2e..5b77ff2ce7015 100644 --- a/util/kube/portforwarder.go +++ b/util/kube/portforwarder.go @@ -16,10 +16,29 @@ import ( "k8s.io/client-go/tools/portforward" "k8s.io/client-go/transport/spdy" cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/podutils" "github.com/argoproj/argo-cd/v3/util/io" ) +func selectPodForPortForward(clientSet kubernetes.Interface, namespace string, podSelectors ...string) (*corev1.Pod, error) { + for _, podSelector := range podSelectors { + pods, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: podSelector, + }) + if err != nil { + return nil, err + } + + for _, po := range pods.Items { + if po.Status.Phase == corev1.PodRunning && podutils.IsPodReady(&po) { + return &po, nil + } + } + } + return nil, fmt.Errorf("cannot find ready pod with selector: %v - use the --{component}-name flag in this command or set the environmental variable (Refer to https://argo-cd.readthedocs.io/en/stable/user-guide/environment-variables), to change the Argo CD component name in the CLI", podSelectors) +} + func PortForward(targetPort int, namespace string, overrides *clientcmd.ConfigOverrides, podSelectors ...string) (int, error) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig @@ -41,24 +60,9 @@ func PortForward(targetPort int, namespace string, overrides *clientcmd.ConfigOv return -1, err } - var pod *corev1.Pod - - for _, podSelector := range podSelectors { - pods, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{ - LabelSelector: podSelector, - }) - if err != nil { - return -1, err - } - - if len(pods.Items) > 0 { - pod = &pods.Items[0] - break - } - } - - if pod == nil { - return -1, fmt.Errorf("cannot find pod with selector: %v - use the --{component}-name flag in this command or set the environmental variable (Refer to https://argo-cd.readthedocs.io/en/stable/user-guide/environment-variables), to change the Argo CD component name in the CLI", podSelectors) + pod, err := selectPodForPortForward(clientSet, namespace, podSelectors...) + if err != nil { + return -1, err } url := clientSet.CoreV1().RESTClient().Post(). diff --git a/util/kube/portforwarder_test.go b/util/kube/portforwarder_test.go new file mode 100644 index 0000000000000..4d1142d45ea30 --- /dev/null +++ b/util/kube/portforwarder_test.go @@ -0,0 +1,83 @@ +package kube + +import ( + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" +) + +func Test_selectPodForPortForward(t *testing.T) { + // Mock the Kubernetes client + client := fake.NewSimpleClientset( + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pod", + Namespace: "default", + Labels: map[string]string{ + "app": "test-app", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.Now(), + }, + }, + Phase: corev1.PodRunning, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test2-pod-broken", + Namespace: "default", + Labels: map[string]string{ + "app": "test2", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: corev1.ConditionFalse, + LastTransitionTime: metav1.Now(), + }, + }, + Phase: corev1.PodFailed, + }, + }, + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test2-pod-working", + Namespace: "default", + Labels: map[string]string{ + "app": "test2", + }, + }, + Status: corev1.PodStatus{ + Conditions: []corev1.PodCondition{ + { + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.Now(), + }, + }, + Phase: corev1.PodRunning, + }, + }, + ) + + // Test selecting the pod + selectedPod, err := selectPodForPortForward(client, "default", "app=test-app") + require.NoError(t, err) + require.Equal(t, "test-pod", selectedPod.Name) + + // Test selecting the working pod + selectedPod2, err := selectPodForPortForward(client, "default", "app=test2") + require.NoError(t, err) + require.Equal(t, "test2-pod-working", selectedPod2.Name) +}