istio.io/istio@v0.0.0-20240520182934-d79c90f27776/operator/cmd/mesh/operator_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 mesh
    16  
    17  import (
    18  	"os"
    19  	"path/filepath"
    20  	"strings"
    21  	"testing"
    22  
    23  	"k8s.io/client-go/kubernetes/fake"
    24  	"k8s.io/client-go/rest"
    25  	"sigs.k8s.io/controller-runtime/pkg/client"
    26  	"sigs.k8s.io/yaml"
    27  
    28  	"istio.io/istio/istioctl/pkg/cli"
    29  	"istio.io/istio/operator/pkg/util"
    30  	"istio.io/istio/operator/pkg/util/clog"
    31  	"istio.io/istio/pkg/kube"
    32  	"istio.io/istio/pkg/test/env"
    33  	"istio.io/istio/pkg/test/util/assert"
    34  )
    35  
    36  var (
    37  	extendedClient kube.CLIClient
    38  	kubeClient     client.Client
    39  )
    40  
    41  // TODO: rewrite this with running the actual top level command.
    42  func TestOperatorDump(t *testing.T) {
    43  	goldenFilepath := filepath.Join(env.IstioSrc, "operator/cmd/mesh/testdata/operator/output/operator-dump.yaml")
    44  
    45  	odArgs := &operatorDumpArgs{
    46  		common: operatorCommonArgs{
    47  			hub:               "foo.io/istio",
    48  			tag:               "1.2.3",
    49  			imagePullSecrets:  []string{"imagePullSecret1,imagePullSecret2"},
    50  			operatorNamespace: "operator-test-namespace",
    51  			watchedNamespaces: "istio-test-namespace1,istio-test-namespace2",
    52  		},
    53  	}
    54  
    55  	cmd := "operator dump --hub " + odArgs.common.hub
    56  	cmd += " --tag " + odArgs.common.tag
    57  	cmd += " --imagePullSecrets " + strings.Join(odArgs.common.imagePullSecrets, ",")
    58  	cmd += " --operatorNamespace " + odArgs.common.operatorNamespace
    59  	cmd += " --watchedNamespaces " + odArgs.common.watchedNamespaces
    60  	cmd += " --manifests=" + string(snapshotCharts)
    61  
    62  	gotYAML, err := runCommand(cmd)
    63  	if err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	if refreshGoldenFiles() {
    68  		t.Logf("Refreshing golden file for %s", goldenFilepath)
    69  		if err := os.WriteFile(goldenFilepath, []byte(gotYAML), 0o644); err != nil {
    70  			t.Error(err)
    71  		}
    72  	}
    73  
    74  	wantYAML, err := readFile(goldenFilepath)
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  
    79  	if diff := util.YAMLDiff(wantYAML, gotYAML); diff != "" {
    80  		t.Fatalf("diff: %s", diff)
    81  	}
    82  }
    83  
    84  func TestOperatorDumpJSONFormat(t *testing.T) {
    85  	goldenFilepath := filepath.Join(env.IstioSrc, "operator/cmd/mesh/testdata/operator/output/operator-dump.json")
    86  
    87  	odArgs := &operatorDumpArgs{
    88  		common: operatorCommonArgs{
    89  			hub:               "foo.io/istio",
    90  			tag:               "1.2.3",
    91  			imagePullSecrets:  []string{"imagePullSecret1,imagePullSecret2"},
    92  			operatorNamespace: "operator-test-namespace",
    93  			watchedNamespaces: "istio-test-namespace1,istio-test-namespace2",
    94  			outputFormat:      jsonOutput,
    95  		},
    96  	}
    97  
    98  	cmd := "operator dump --hub " + odArgs.common.hub
    99  	cmd += " --tag " + odArgs.common.tag
   100  	cmd += " --imagePullSecrets " + strings.Join(odArgs.common.imagePullSecrets, ",")
   101  	cmd += " --operatorNamespace " + odArgs.common.operatorNamespace
   102  	cmd += " --watchedNamespaces " + odArgs.common.watchedNamespaces
   103  	cmd += " --manifests=" + string(snapshotCharts)
   104  	cmd += " --output " + odArgs.common.outputFormat
   105  
   106  	gotJSON, err := runCommand(cmd)
   107  	if err != nil {
   108  		t.Fatal(err)
   109  	}
   110  
   111  	if refreshGoldenFiles() {
   112  		t.Logf("Refreshing golden file for %s", goldenFilepath)
   113  		if err := os.WriteFile(goldenFilepath, []byte(gotJSON), 0o644); err != nil {
   114  			t.Error(err)
   115  		}
   116  	}
   117  
   118  	wantJSON, err := readFile(goldenFilepath)
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	var want, got []byte
   124  	if got, err = yaml.JSONToYAML([]byte(gotJSON)); err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	if want, err = yaml.JSONToYAML([]byte(wantJSON)); err != nil {
   128  		t.Fatal(err)
   129  	}
   130  
   131  	if diff := util.YAMLDiff(string(want), string(got)); diff != "" {
   132  		t.Fatalf("diff: %s", diff)
   133  	}
   134  }
   135  
   136  // TODO: rewrite this with running the actual top level command.
   137  func TestOperatorInit(t *testing.T) {
   138  	goldenFilepath := filepath.Join(operatorRootDir, "cmd/mesh/testdata/operator/output/operator-init.yaml")
   139  	rootArgs := &RootArgs{}
   140  	oiArgs := &operatorInitArgs{
   141  		common: operatorCommonArgs{
   142  			hub:               "foo.io/istio",
   143  			tag:               "1.2.3",
   144  			operatorNamespace: "operator-test-namespace",
   145  			watchedNamespaces: "istio-test-namespace1,istio-test-namespace2",
   146  			manifestsPath:     string(snapshotCharts),
   147  		},
   148  	}
   149  
   150  	l := clog.NewConsoleLogger(os.Stdout, os.Stderr, installerScope)
   151  	_, gotYAML, err := renderOperatorManifest(rootArgs, &oiArgs.common)
   152  	if err != nil {
   153  		l.LogAndFatal(err)
   154  	}
   155  
   156  	if refreshGoldenFiles() {
   157  		t.Logf("Refreshing golden file for %s", goldenFilepath)
   158  		if err := os.WriteFile(goldenFilepath, []byte(gotYAML), 0o644); err != nil {
   159  			t.Error(err)
   160  		}
   161  	}
   162  
   163  	wantYAML, err := readFile(goldenFilepath)
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  
   168  	if diff := util.YAMLDiff(wantYAML, gotYAML); diff != "" {
   169  		t.Fatalf("diff: %s", diff)
   170  	}
   171  }
   172  
   173  func MockKubernetesClients(_ kube.CLIClient, _ clog.Logger) (kube.CLIClient, client.Client, error) {
   174  	extendedClient = kube.NewFakeClient()
   175  	kubeClient, _ = client.New(&rest.Config{}, client.Options{})
   176  	return extendedClient, kubeClient, nil
   177  }
   178  
   179  func TestOperatorInitDryRun(t *testing.T) {
   180  	tests := []struct {
   181  		operatorNamespace string
   182  		watchedNamespaces string
   183  	}{
   184  		{
   185  			// default nss
   186  			operatorNamespace: "",
   187  			watchedNamespaces: "",
   188  		},
   189  		{
   190  			operatorNamespace: "test",
   191  			watchedNamespaces: "test1",
   192  		},
   193  		{
   194  			operatorNamespace: "",
   195  			watchedNamespaces: "test4, test5",
   196  		},
   197  	}
   198  
   199  	kubeClients = MockKubernetesClients
   200  
   201  	for _, test := range tests {
   202  		t.Run("", func(t *testing.T) {
   203  			args := []string{"operator", "init", "--dry-run"}
   204  			if test.operatorNamespace != "" {
   205  				args = append(args, "--operatorNamespace", test.operatorNamespace)
   206  			}
   207  			if test.watchedNamespaces != "" {
   208  				args = append(args, "--watchedNamespaces", test.watchedNamespaces)
   209  			}
   210  
   211  			kubeClientFunc = func() (kube.CLIClient, error) {
   212  				return nil, nil
   213  			}
   214  			rootCmd := GetRootCmd(cli.NewFakeContext(&cli.NewFakeContextOption{
   215  				Version: "25",
   216  			}), args)
   217  			err := rootCmd.Execute()
   218  			assert.NoError(t, err)
   219  
   220  			readActions := map[string]bool{
   221  				"get":   true,
   222  				"list":  true,
   223  				"watch": true,
   224  			}
   225  
   226  			actions := extendedClient.Kube().(*fake.Clientset).Actions()
   227  			for _, action := range actions {
   228  				if v := readActions[action.GetVerb()]; !v {
   229  					t.Fatalf("unexpected action: %+v, expected %s", action.GetVerb(), "get")
   230  				}
   231  			}
   232  		})
   233  	}
   234  }