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 }