istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/util/handlers/handlers_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package handlers
    16  
    17  import (
    18  	"fmt"
    19  	"net/http"
    20  	"sort"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	appsv1 "k8s.io/api/apps/v1"
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
    30  	"k8s.io/client-go/rest/fake"
    31  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    32  	"k8s.io/kubectl/pkg/scheme"
    33  
    34  	"istio.io/istio/pkg/test/util/assert"
    35  )
    36  
    37  func TestInferPodInfo(t *testing.T) {
    38  	tests := []struct {
    39  		proxyName     string
    40  		namespace     string
    41  		wantPodName   string
    42  		wantNamespace string
    43  	}{
    44  		{
    45  			proxyName:     "istio-ingressgateway-8d9697654-qdzgh.istio-system",
    46  			namespace:     "kube-system",
    47  			wantPodName:   "istio-ingressgateway-8d9697654-qdzgh",
    48  			wantNamespace: "istio-system",
    49  		},
    50  		{
    51  			proxyName:     "istio-ingressgateway-8d9697654-qdzgh.istio-system",
    52  			namespace:     "",
    53  			wantPodName:   "istio-ingressgateway-8d9697654-qdzgh",
    54  			wantNamespace: "istio-system",
    55  		},
    56  		{
    57  			proxyName:     "istio-ingressgateway-8d9697654-qdzgh",
    58  			namespace:     "kube-system",
    59  			wantPodName:   "istio-ingressgateway-8d9697654-qdzgh",
    60  			wantNamespace: "kube-system",
    61  		},
    62  		{
    63  			proxyName:     "istio-security-post-install-1.2.2-bm9w2.istio-system",
    64  			namespace:     "istio-system",
    65  			wantPodName:   "istio-security-post-install-1.2.2-bm9w2",
    66  			wantNamespace: "istio-system",
    67  		},
    68  		{
    69  			proxyName:     "istio-security-post-install-1.2.2-bm9w2.istio-system",
    70  			namespace:     "",
    71  			wantPodName:   "istio-security-post-install-1.2.2-bm9w2",
    72  			wantNamespace: "istio-system",
    73  		},
    74  		{
    75  			proxyName:     "service/istiod",
    76  			namespace:     "",
    77  			wantPodName:   "service/istiod",
    78  			wantNamespace: "",
    79  		},
    80  		{
    81  			proxyName:     "service/istiod",
    82  			namespace:     "namespace",
    83  			wantPodName:   "service/istiod",
    84  			wantNamespace: "namespace",
    85  		},
    86  		{
    87  			proxyName:     "service/istiod.istio-system",
    88  			namespace:     "namespace",
    89  			wantPodName:   "service/istiod",
    90  			wantNamespace: "istio-system",
    91  		},
    92  		{
    93  			proxyName:     "gateway.gateway.networking.k8s.io/istiod",
    94  			namespace:     "",
    95  			wantPodName:   "gateway.gateway.networking.k8s.io/istiod",
    96  			wantNamespace: "",
    97  		},
    98  		{
    99  			proxyName:     "gateway.gateway.networking.k8s.io/istiod",
   100  			namespace:     "namespace",
   101  			wantPodName:   "gateway.gateway.networking.k8s.io/istiod",
   102  			wantNamespace: "namespace",
   103  		},
   104  		{
   105  			proxyName:     "gateway.gateway.networking.k8s.io/istiod.istio-system",
   106  			namespace:     "namespace",
   107  			wantPodName:   "gateway.gateway.networking.k8s.io/istiod",
   108  			wantNamespace: "istio-system",
   109  		},
   110  	}
   111  	for _, tt := range tests {
   112  		t.Run(strings.Split(tt.proxyName, ".")[0], func(t *testing.T) {
   113  			gotPodName, gotNamespace := InferPodInfo(tt.proxyName, tt.namespace)
   114  			if gotPodName != tt.wantPodName || gotNamespace != tt.wantNamespace {
   115  				t.Errorf("unexpected podName and namespace: wanted %v %v got %v %v", tt.wantPodName, tt.wantNamespace, gotPodName, gotNamespace)
   116  			}
   117  		})
   118  	}
   119  }
   120  
   121  func TestInferPodInfoFromTypedResource(t *testing.T) {
   122  	tests := []struct {
   123  		name          string
   124  		wantPodName   string
   125  		wantNamespace string
   126  	}{
   127  		{
   128  			name:          "foo-abc.istio-system",
   129  			wantPodName:   "foo-abc",
   130  			wantNamespace: "istio-system",
   131  		},
   132  		{
   133  			name:          "foo-abc",
   134  			wantPodName:   "foo-abc",
   135  			wantNamespace: "test",
   136  		},
   137  		{
   138  			name:          "Deployment/foo.istio-system",
   139  			wantPodName:   "foo-abc",
   140  			wantNamespace: "istio-system",
   141  		},
   142  		{
   143  			name:          "Deployment/foo",
   144  			wantPodName:   "foo-abc",
   145  			wantNamespace: "test",
   146  		},
   147  	}
   148  	factory := cmdtesting.NewTestFactory().WithNamespace("test")
   149  	ns := scheme.Codecs.WithoutConversion()
   150  	codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
   151  	factory.Client = &fake.RESTClient{
   152  		GroupVersion:         schema.GroupVersion{Group: "", Version: "v1"},
   153  		NegotiatedSerializer: ns,
   154  		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
   155  			switch p, m := req.URL.Path, req.Method; {
   156  			case p == "/namespaces/istio-system/deployments/foo" && m == "GET":
   157  				body := cmdtesting.ObjBody(codec, attachDeploy("istio-system"))
   158  				return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
   159  			case p == "/namespaces/test/deployments/foo" && m == "GET":
   160  				body := cmdtesting.ObjBody(codec, attachDeploy("test"))
   161  				return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
   162  			default:
   163  				t.Errorf("%s: unexpected request: %s %#v\n%#v", p, req.Method, req.URL, req)
   164  				return nil, fmt.Errorf("unexpected request")
   165  			}
   166  		}),
   167  	}
   168  	getFirstPodFunc = func(client corev1client.PodsGetter, namespace string, selector string, timeout time.Duration, sortBy func([]*corev1.Pod) sort.Interface) (
   169  		*corev1.Pod, int, error,
   170  	) {
   171  		return attachPod(namespace), 1, nil
   172  	}
   173  	for _, tt := range tests {
   174  		t.Run(strings.Split(tt.name, ".")[0], func(t *testing.T) {
   175  			gotPodName, gotNamespace, err := InferPodInfoFromTypedResource(tt.name, "test", factory)
   176  			assert.NoError(t, err)
   177  			if gotPodName != tt.wantPodName || gotNamespace != tt.wantNamespace {
   178  				t.Errorf("unexpected podName and namespace: wanted %v %v got %v %v", tt.wantPodName, tt.wantNamespace, gotPodName, gotNamespace)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func attachPod(ns string) *corev1.Pod {
   185  	return &corev1.Pod{
   186  		ObjectMeta: metav1.ObjectMeta{Name: "foo-abc", Namespace: ns, ResourceVersion: "10"},
   187  		Spec: corev1.PodSpec{
   188  			RestartPolicy: corev1.RestartPolicyAlways,
   189  			DNSPolicy:     corev1.DNSClusterFirst,
   190  			Containers: []corev1.Container{
   191  				{
   192  					Name: "bar",
   193  				},
   194  			},
   195  			InitContainers: []corev1.Container{
   196  				{
   197  					Name: "initfoo",
   198  				},
   199  			},
   200  			EphemeralContainers: []corev1.EphemeralContainer{
   201  				{
   202  					EphemeralContainerCommon: corev1.EphemeralContainerCommon{
   203  						Name: "debugger",
   204  					},
   205  				},
   206  			},
   207  		},
   208  		Status: corev1.PodStatus{
   209  			Phase: corev1.PodRunning,
   210  		},
   211  	}
   212  }
   213  
   214  func attachDeploy(ns string) *appsv1.Deployment {
   215  	return &appsv1.Deployment{
   216  		ObjectMeta: metav1.ObjectMeta{
   217  			Name:      "foo",
   218  			Namespace: ns,
   219  		},
   220  		Spec: appsv1.DeploymentSpec{},
   221  	}
   222  }