istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/proxystatus/proxystatus_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 proxystatus
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"net/http"
    22  	"strings"
    23  	"testing"
    24  
    25  	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    26  	"github.com/spf13/cobra"
    27  	"google.golang.org/grpc"
    28  	corev1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/cli-runtime/pkg/resource"
    31  	"k8s.io/client-go/rest/fake"
    32  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    33  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    34  
    35  	"istio.io/api/label"
    36  	"istio.io/istio/istioctl/pkg/cli"
    37  	"istio.io/istio/istioctl/pkg/clioptions"
    38  	"istio.io/istio/istioctl/pkg/multixds"
    39  	"istio.io/istio/istioctl/pkg/xds"
    40  	"istio.io/istio/pkg/kube"
    41  	"istio.io/istio/pkg/test/util/assert"
    42  )
    43  
    44  type execTestCase struct {
    45  	args     []string
    46  	revision string
    47  	noIstiod bool
    48  
    49  	// Typically use one of the three
    50  	expectedOutput string // Expected constant output
    51  	expectedString string // String output is expected to contain
    52  
    53  	wantException bool
    54  }
    55  
    56  func TestProxyStatus(t *testing.T) {
    57  	cases := []execTestCase{
    58  		{ // case 0, with no Isitod instance
    59  			args:           []string{},
    60  			noIstiod:       true,
    61  			expectedOutput: "Error: no running Istio pods in \"istio-system\"\n",
    62  			wantException:  true,
    63  		},
    64  		{ // case 1, with Istiod instance
    65  			args:           []string{},
    66  			expectedString: "NAME     CLUSTER     CDS     LDS     EDS     RDS     ECDS     ISTIOD",
    67  		},
    68  		{ // case 2: supplying nonexistent pod name should result in error with flag
    69  			args:          strings.Split("deployment/random-gibberish", " "),
    70  			wantException: true,
    71  		},
    72  		{ // case 3: supplying nonexistent deployment name
    73  			args:          strings.Split("deployment/random-gibberish.default", " "),
    74  			wantException: true,
    75  		},
    76  		{ // case 4: supplying nonexistent deployment name in nonexistent namespace
    77  			args:          strings.Split("deployment/random-gibberish.bogus", " "),
    78  			wantException: true,
    79  		},
    80  		{ // case 5: supplying nonexistent pod name should result in error
    81  			args:          strings.Split("random-gibberish-podname-61789237418234", " "),
    82  			wantException: true,
    83  		},
    84  		{ // case 6: new --revision argument
    85  			args:           strings.Split("--revision canary", " "),
    86  			expectedString: "NAME     CLUSTER     CDS     LDS     EDS     RDS     ECDS     ISTIOD",
    87  			revision:       "canary",
    88  		},
    89  		{ // case 7: supplying type that doesn't select pods should fail
    90  			args:          strings.Split("serviceaccount/sleep", " "),
    91  			wantException: true,
    92  		},
    93  	}
    94  	multixds.GetXdsResponse = func(_ *discovery.DiscoveryRequest, _ string, _ string, _ clioptions.CentralControlPlaneOptions, _ []grpc.DialOption,
    95  	) (*discovery.DiscoveryResponse, error) {
    96  		return &discovery.DiscoveryResponse{}, nil
    97  	}
    98  	t.Cleanup(func() {
    99  		multixds.GetXdsResponse = xds.GetXdsResponse
   100  	})
   101  
   102  	for i, c := range cases {
   103  		t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) {
   104  			ctx := cli.NewFakeContext(&cli.NewFakeContextOption{
   105  				IstioNamespace: "istio-system",
   106  			})
   107  			if !c.noIstiod {
   108  				client, err := ctx.CLIClientWithRevision(c.revision)
   109  				assert.NoError(t, err)
   110  				_, err = client.Kube().CoreV1().Pods("istio-system").Create(context.TODO(), &corev1.Pod{
   111  					ObjectMeta: metav1.ObjectMeta{
   112  						Name:      "istiod-test",
   113  						Namespace: "istio-system",
   114  						Labels: map[string]string{
   115  							"app":                 "istiod",
   116  							label.IoIstioRev.Name: c.revision,
   117  						},
   118  					},
   119  					Status: corev1.PodStatus{
   120  						Phase: corev1.PodRunning,
   121  					},
   122  				}, metav1.CreateOptions{})
   123  				assert.NoError(t, err)
   124  			}
   125  			verifyExecTestOutput(t, XdsStatusCommand(ctx), c)
   126  		})
   127  	}
   128  }
   129  
   130  func verifyExecTestOutput(t *testing.T, cmd *cobra.Command, c execTestCase) {
   131  	t.Helper()
   132  
   133  	var out bytes.Buffer
   134  	cmd.SetArgs(c.args)
   135  	cmd.SilenceUsage = true
   136  	cmd.SetOut(&out)
   137  	cmd.SetErr(&out)
   138  
   139  	fErr := cmd.Execute()
   140  	output := out.String()
   141  
   142  	if c.expectedOutput != "" && c.expectedOutput != output {
   143  		t.Fatalf("Unexpected output for 'istioctl %s'\n got: %q\nwant: %q", strings.Join(c.args, " "), output, c.expectedOutput)
   144  	}
   145  
   146  	if c.expectedString != "" && !strings.Contains(output, c.expectedString) {
   147  		t.Fatalf("Output didn't match for '%s %s'\n got %v\nwant: %v", cmd.Name(), strings.Join(c.args, " "), output, c.expectedString)
   148  	}
   149  
   150  	if c.wantException {
   151  		if fErr == nil {
   152  			t.Fatalf("Wanted an exception for 'istioctl %s', didn't get one, output was %q",
   153  				strings.Join(c.args, " "), output)
   154  		}
   155  	} else {
   156  		if fErr != nil {
   157  			t.Fatalf("Unwanted exception for 'istioctl %s': %v", strings.Join(c.args, " "), fErr)
   158  		}
   159  	}
   160  }
   161  
   162  func init() {
   163  	cli.MakeKubeFactory = func(k kube.CLIClient) cmdutil.Factory {
   164  		tf := cmdtesting.NewTestFactory()
   165  		_, _, codec := cmdtesting.NewExternalScheme()
   166  		tf.UnstructuredClient = &fake.RESTClient{
   167  			NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
   168  			Resp: &http.Response{
   169  				StatusCode: http.StatusOK,
   170  				Header:     cmdtesting.DefaultHeader(),
   171  				Body: cmdtesting.ObjBody(codec,
   172  					cmdtesting.NewInternalType("", "", "foo")),
   173  			},
   174  		}
   175  		return tf
   176  	}
   177  }