istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/operator/uninstall_test.go (about) 1 //go:build integ 2 // +build integ 3 4 // Copyright Istio Authors 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package operator 19 20 import ( 21 "context" 22 "fmt" 23 "os" 24 "path/filepath" 25 "testing" 26 "time" 27 28 corev1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/api/errors" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 32 iopv1alpha1 "istio.io/istio/operator/pkg/apis/istio/v1alpha1" 33 istiokube "istio.io/istio/pkg/kube" 34 "istio.io/istio/pkg/log" 35 "istio.io/istio/pkg/test/framework" 36 "istio.io/istio/pkg/test/framework/components/cluster" 37 "istio.io/istio/pkg/test/framework/components/istioctl" 38 "istio.io/istio/pkg/test/framework/resource" 39 "istio.io/istio/pkg/test/scopes" 40 "istio.io/istio/pkg/test/util/retry" 41 ) 42 43 const deletionTimeout = 5 * time.Minute 44 45 func TestReconcileDelete(t *testing.T) { 46 framework. 47 NewTest(t). 48 Run(func(t framework.TestContext) { 49 // For positive casse, use minimal profile, iop file will be deleted 50 t.NewSubTest("delete-iop-success").Run(func(t framework.TestContext) { 51 istioCtl := istioctl.NewOrFail(t, t, istioctl.Config{}) 52 workDir, err := t.CreateTmpDirectory("operator-controller-test") 53 if err != nil { 54 t.Fatal("failed to create test directory") 55 } 56 cs := t.Clusters().Default() 57 cleanupInClusterCRs(t, cs) 58 t.Cleanup(func() { 59 cleanupIstioResources(t, cs, istioCtl) 60 }) 61 s := t.Settings() 62 // Operator has no --variant flag, hack it with --tag 63 tag := s.Image.Tag 64 if v := s.Image.Variant; v != "" { 65 tag += "-" + v 66 } 67 initCmd := []string{ 68 "operator", "init", 69 "--hub=" + s.Image.Hub, 70 "--tag=" + tag, 71 "--manifests=" + ManifestPath, 72 } 73 // install istio with default config for the first time by running operator init command 74 istioCtl.InvokeOrFail(t, initCmd) 75 t.TrackResource(&operatorDumper{rev: ""}) 76 77 if _, err := cs.Kube().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 78 ObjectMeta: metav1.ObjectMeta{ 79 Name: IstioNamespace, 80 }, 81 }, metav1.CreateOptions{}); err != nil { 82 _, err := cs.Kube().CoreV1().Namespaces().Get(context.TODO(), IstioNamespace, metav1.GetOptions{}) 83 if err == nil { 84 log.Info("istio namespace already exist") 85 } else { 86 t.Errorf("failed to create istio namespace: %v", err) 87 } 88 } 89 iopCRFile = filepath.Join(workDir, "iop_cr.yaml") 90 r := "v2" 91 // later just run `kubectl apply -f newcr.yaml` to apply new installation cr files and verify. 92 applyIop(t, t, cs, "minimal", r) 93 94 if err := checkInstallStatus(cs, r); err != nil { 95 t.Errorf("failed to check install status: %v", err) 96 } 97 98 iopName := revName("test-istiocontrolplane", r) 99 100 log.Infof("delete iop %s", iopName) 101 if err := deleteIop(cs, iopName); err != nil { 102 t.Errorf("failed to delete iopfile: %v", err) 103 } 104 105 retry.UntilSuccessOrFail(t, func() error { 106 exist, checkErr := checkIopExist(cs, iopName) 107 if checkErr != nil { 108 return checkErr 109 } 110 111 if exist { 112 return fmt.Errorf("fail to delete iop") 113 } 114 115 return nil 116 }, retry.Timeout(deletionTimeout), retry.Delay(retryDelay)) 117 }) 118 119 // For negative casse, use default profile, ingressgateway alway connect to istiod 120 // deletion will fail 121 t.NewSubTest("delete-iop-fail").Run(func(t framework.TestContext) { 122 istioCtl := istioctl.NewOrFail(t, t, istioctl.Config{}) 123 workDir, err := t.CreateTmpDirectory("operator-controller-test") 124 if err != nil { 125 t.Fatal("failed to create test directory") 126 } 127 cs := t.Clusters().Default() 128 cleanupInClusterCRs(t, cs) 129 t.Cleanup(func() { 130 cleanupIstioResources(t, cs, istioCtl) 131 }) 132 s := t.Settings() 133 // Operator has no --variant flag, hack it with --tag 134 tag := s.Image.Tag 135 if v := s.Image.Variant; v != "" { 136 tag += "-" + v 137 } 138 initCmd := []string{ 139 "operator", "init", 140 "--hub=" + s.Image.Hub, 141 "--tag=" + tag, 142 "--manifests=" + ManifestPath, 143 } 144 // install istio with default config for the first time by running operator init command 145 istioCtl.InvokeOrFail(t, initCmd) 146 t.TrackResource(&operatorDumper{rev: ""}) 147 148 if _, err := cs.Kube().CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ 149 ObjectMeta: metav1.ObjectMeta{ 150 Name: IstioNamespace, 151 }, 152 }, metav1.CreateOptions{}); err != nil { 153 _, err := cs.Kube().CoreV1().Namespaces().Get(context.TODO(), IstioNamespace, metav1.GetOptions{}) 154 if err == nil { 155 log.Info("istio namespace already exist") 156 } else { 157 t.Errorf("failed to create istio namespace: %v", err) 158 } 159 } 160 iopCRFile = filepath.Join(workDir, "iop_cr.yaml") 161 r := "v2" 162 // later just run `kubectl apply -f newcr.yaml` to apply new installation cr files and verify. 163 applyIop(t, t, cs, "default", r) 164 165 if err := checkInstallStatus(cs, r); err != nil { 166 t.Errorf("failed to check install status: %v", err) 167 } 168 169 iopName := revName("test-istiocontrolplane", r) 170 171 log.Infof("delete iop %s", iopName) 172 if err := deleteIop(cs, iopName); err != nil { 173 t.Errorf("failed to delete iopfile: %v", err) 174 } 175 176 if err := retry.UntilSuccess(func() error { 177 exist, checkErr := checkIopExist(cs, iopName) 178 if checkErr != nil { 179 return checkErr 180 } 181 182 if exist { 183 return fmt.Errorf("fail to delete iop") 184 } 185 186 return nil 187 }, retry.Timeout(deletionTimeout), retry.Delay(retryDelay)); err == nil { 188 t.Fatal("except iop still exists, but got nil") 189 } 190 }) 191 }) 192 } 193 194 func applyIop(t framework.TestContext, ctx resource.Context, cs cluster.Cluster, profileName string, revision string) { 195 scopes.Framework.Infof(fmt.Sprintf("=== install istio with profile: %s===\n", profileName)) 196 metadataYAML := ` 197 apiVersion: install.istio.io/v1alpha1 198 kind: IstioOperator 199 metadata: 200 name: %s 201 namespace: istio-system 202 spec: 203 ` 204 if revision != "" { 205 metadataYAML += " revision: " + revision + "\n" 206 } 207 208 metadataYAML += ` 209 profile: %s 210 installPackagePath: %s 211 hub: %s 212 tag: %s 213 values: 214 global: 215 variant: %q 216 imagePullPolicy: %s 217 ` 218 s := ctx.Settings() 219 overlayYAML := fmt.Sprintf(metadataYAML, revName("test-istiocontrolplane", revision), profileName, ManifestPathContainer, 220 s.Image.Hub, s.Image.Tag, s.Image.Variant, s.Image.PullPolicy) 221 222 scopes.Framework.Infof("=== installing with IOP: ===\n%s\n", overlayYAML) 223 224 if err := os.WriteFile(iopCRFile, []byte(overlayYAML), os.ModePerm); err != nil { 225 t.Fatalf("failed to write iop cr file: %v", err) 226 } 227 228 if err := cs.ApplyYAMLFiles(IstioNamespace, iopCRFile); err != nil { 229 t.Fatalf("failed to apply IstioOperator CR file: %s, %v", iopCRFile, err) 230 } 231 } 232 233 func checkIopExist(cs istiokube.CLIClient, iopName string) (bool, error) { 234 scopes.Framework.Infof("checking IstioOperator CR status") 235 gvr := iopv1alpha1.IstioOperatorGVR 236 237 _, err := cs.Dynamic().Resource(gvr).Namespace(IstioNamespace).Get(context.TODO(), iopName, metav1.GetOptions{}) 238 if errors.IsNotFound(err) { 239 return false, nil 240 } else if err == nil { 241 return true, nil 242 } 243 244 return false, err 245 } 246 247 func deleteIop(cs istiokube.CLIClient, iopName string) error { 248 scopes.Framework.Infof("checking IstioOperator CR status") 249 gvr := iopv1alpha1.IstioOperatorGVR 250 251 return cs.Dynamic().Resource(gvr).Namespace(IstioNamespace).Delete(context.TODO(), iopName, metav1.DeleteOptions{}) 252 }