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  }