istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/internaldebug/internal-debug_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 internaldebug
    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 TestInternalDebug(t *testing.T) {
    57  	cases := []execTestCase{
    58  		{ // case 0, no args
    59  			args:           []string{},
    60  			noIstiod:       true,
    61  			expectedOutput: "Error: debug type is required\n",
    62  			wantException:  true,
    63  		},
    64  		{ // case 1, no istiod
    65  			args:           []string{"adsz"},
    66  			noIstiod:       true,
    67  			expectedOutput: "Error: no running Istio pods in \"istio-system\"\n",
    68  			wantException:  true,
    69  		},
    70  		{ // case 2, with Istiod instance
    71  			args:           []string{"adsz"},
    72  			expectedString: "",
    73  		},
    74  	}
    75  	multixds.GetXdsResponse = func(_ *discovery.DiscoveryRequest, _ string, _ string, _ clioptions.CentralControlPlaneOptions, _ []grpc.DialOption,
    76  	) (*discovery.DiscoveryResponse, error) {
    77  		return &discovery.DiscoveryResponse{}, nil
    78  	}
    79  	t.Cleanup(func() {
    80  		multixds.GetXdsResponse = xds.GetXdsResponse
    81  	})
    82  
    83  	for i, c := range cases {
    84  		t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) {
    85  			ctx := cli.NewFakeContext(&cli.NewFakeContextOption{
    86  				IstioNamespace: "istio-system",
    87  			})
    88  			if !c.noIstiod {
    89  				client, err := ctx.CLIClientWithRevision(c.revision)
    90  				assert.NoError(t, err)
    91  				_, err = client.Kube().CoreV1().Pods("istio-system").Create(context.TODO(), &corev1.Pod{
    92  					ObjectMeta: metav1.ObjectMeta{
    93  						Name:      "istiod-test",
    94  						Namespace: "istio-system",
    95  						Labels: map[string]string{
    96  							"app":                 "istiod",
    97  							label.IoIstioRev.Name: c.revision,
    98  						},
    99  					},
   100  					Status: corev1.PodStatus{
   101  						Phase: corev1.PodRunning,
   102  					},
   103  				}, metav1.CreateOptions{})
   104  				assert.NoError(t, err)
   105  			}
   106  			verifyExecTestOutput(t, DebugCommand(ctx), c)
   107  		})
   108  	}
   109  }
   110  
   111  func verifyExecTestOutput(t *testing.T, cmd *cobra.Command, c execTestCase) {
   112  	t.Helper()
   113  
   114  	var out bytes.Buffer
   115  	cmd.SetArgs(c.args)
   116  	cmd.SilenceUsage = true
   117  	cmd.SetOut(&out)
   118  	cmd.SetErr(&out)
   119  
   120  	fErr := cmd.Execute()
   121  	output := out.String()
   122  
   123  	if c.expectedOutput != "" && c.expectedOutput != output {
   124  		t.Fatalf("Unexpected output for 'istioctl %s'\n got: %q\nwant: %q", strings.Join(c.args, " "), output, c.expectedOutput)
   125  	}
   126  
   127  	if c.expectedString != "" && !strings.Contains(output, c.expectedString) {
   128  		t.Fatalf("Output didn't match for '%s %s'\n got %v\nwant: %v", cmd.Name(), strings.Join(c.args, " "), output, c.expectedString)
   129  	}
   130  
   131  	if c.wantException {
   132  		if fErr == nil {
   133  			t.Fatalf("Wanted an exception for 'istioctl %s', didn't get one, output was %q",
   134  				strings.Join(c.args, " "), output)
   135  		}
   136  	} else {
   137  		if fErr != nil {
   138  			t.Fatalf("Unwanted exception for 'istioctl %s': %v", strings.Join(c.args, " "), fErr)
   139  		}
   140  	}
   141  }
   142  
   143  func init() {
   144  	cli.MakeKubeFactory = func(k kube.CLIClient) cmdutil.Factory {
   145  		tf := cmdtesting.NewTestFactory()
   146  		_, _, codec := cmdtesting.NewExternalScheme()
   147  		tf.UnstructuredClient = &fake.RESTClient{
   148  			NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
   149  			Resp: &http.Response{
   150  				StatusCode: http.StatusOK,
   151  				Header:     cmdtesting.DefaultHeader(),
   152  				Body: cmdtesting.ObjBody(codec,
   153  					cmdtesting.NewInternalType("", "", "foo")),
   154  			},
   155  		}
   156  		return tf
   157  	}
   158  }