istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/helm/upgrade/util.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 helmupgrade
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"path/filepath"
    24  	"strings"
    25  
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  
    28  	"istio.io/api/label"
    29  	"istio.io/istio/pkg/test/framework"
    30  	"istio.io/istio/pkg/test/framework/components/cluster"
    31  	kubecluster "istio.io/istio/pkg/test/framework/components/cluster/kube"
    32  	"istio.io/istio/pkg/test/helm"
    33  	kubetest "istio.io/istio/pkg/test/kube"
    34  	"istio.io/istio/pkg/test/scopes"
    35  	"istio.io/istio/pkg/test/shell"
    36  	"istio.io/istio/pkg/test/util/retry"
    37  	helmtest "istio.io/istio/tests/integration/helm"
    38  	"istio.io/istio/tests/util/sanitycheck"
    39  )
    40  
    41  const (
    42  	gcrHub            = "gcr.io/istio-release"
    43  	prodTag           = "prod"
    44  	canaryTag         = "canary"
    45  	latestRevisionTag = "latest"
    46  )
    47  
    48  // upgradeCharts upgrades Istio using Helm charts with the provided
    49  // override values file to the latest charts in $ISTIO_SRC/manifests
    50  func upgradeCharts(ctx framework.TestContext, h *helm.Helm, overrideValuesFile string, nsConfig helmtest.NamespaceConfig, isAmbient bool) {
    51  	execCmd := fmt.Sprintf(
    52  		"kubectl apply -n %v -f %v",
    53  		helmtest.IstioNamespace,
    54  		filepath.Join(helmtest.ManifestsChartPath, helmtest.BaseChart, helmtest.CRDsFolder))
    55  	_, err := shell.Execute(false, execCmd)
    56  	if err != nil {
    57  		ctx.Fatalf("couldn't run kubectl apply on crds folder: %v", err)
    58  	}
    59  
    60  	// Upgrade base chart
    61  	err = h.UpgradeChart(helmtest.BaseReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.BaseChart),
    62  		nsConfig.Get(helmtest.BaseReleaseName), overrideValuesFile, helmtest.Timeout, "--skip-crds")
    63  	if err != nil {
    64  		ctx.Fatalf("failed to upgrade istio %s chart", helmtest.BaseReleaseName)
    65  	}
    66  
    67  	// Upgrade discovery chart
    68  	err = h.UpgradeChart(helmtest.IstiodReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.ControlChartsDir, helmtest.DiscoveryChartsDir),
    69  		nsConfig.Get(helmtest.IstiodReleaseName), overrideValuesFile, helmtest.Timeout)
    70  	if err != nil {
    71  		ctx.Fatalf("failed to upgrade istio %s chart", helmtest.IstiodReleaseName)
    72  	}
    73  
    74  	if isAmbient || ctx.Settings().OpenShift {
    75  		// Upgrade istio-cni chart
    76  		err = h.UpgradeChart(helmtest.CniReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.CniChartsDir),
    77  			nsConfig.Get(helmtest.CniReleaseName), overrideValuesFile, helmtest.Timeout)
    78  		if err != nil {
    79  			ctx.Fatalf("failed to upgrade istio %s chart", helmtest.CniReleaseName)
    80  		}
    81  	}
    82  
    83  	if isAmbient {
    84  		// Upgrade ztunnel chart
    85  		err = h.UpgradeChart(helmtest.ZtunnelReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.ZtunnelChartsDir),
    86  			nsConfig.Get(helmtest.ZtunnelReleaseName), overrideValuesFile, helmtest.Timeout)
    87  		if err != nil {
    88  			ctx.Fatalf("failed to upgrade istio %s chart", helmtest.ZtunnelReleaseName)
    89  		}
    90  	}
    91  
    92  	// Upgrade ingress gateway chart
    93  	err = h.UpgradeChart(helmtest.IngressReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.GatewayChartsDir),
    94  		nsConfig.Get(helmtest.IngressReleaseName), overrideValuesFile, helmtest.Timeout)
    95  	if err != nil {
    96  		ctx.Fatalf("failed to upgrade istio %s chart", helmtest.IngressReleaseName)
    97  	}
    98  }
    99  
   100  // deleteIstio deletes installed Istio Helm charts and resources
   101  func deleteIstio(cs cluster.Cluster, h *helm.Helm, nsConfig helmtest.NamespaceConfig, gatewayChartInstalled bool) error {
   102  	scopes.Framework.Infof("cleaning up resources")
   103  	if gatewayChartInstalled {
   104  		if err := h.DeleteChart(helmtest.IngressReleaseName, nsConfig.Get(helmtest.IngressReleaseName)); err != nil {
   105  			return fmt.Errorf("failed to delete %s release", helmtest.IngressReleaseName)
   106  		}
   107  	}
   108  
   109  	if err := h.DeleteChart(helmtest.IstiodReleaseName, helmtest.IstioNamespace); err != nil {
   110  		return fmt.Errorf("failed to delete %s release", helmtest.IstiodReleaseName)
   111  	}
   112  
   113  	return cleanupIstio(cs, h)
   114  }
   115  
   116  func cleanupIstio(cs cluster.Cluster, h *helm.Helm) error {
   117  	if err := h.DeleteChart(helmtest.BaseReleaseName, helmtest.IstioNamespace); err != nil {
   118  		return fmt.Errorf("failed to delete %s release", helmtest.BaseReleaseName)
   119  	}
   120  	if err := cs.Kube().CoreV1().Namespaces().Delete(context.TODO(), helmtest.IstioNamespace, metav1.DeleteOptions{}); err != nil {
   121  		return fmt.Errorf("failed to delete istio namespace: %v", err)
   122  	}
   123  	if err := kubetest.WaitForNamespaceDeletion(cs.Kube(), helmtest.IstioNamespace, retry.Timeout(helmtest.RetryTimeOut)); err != nil {
   124  		return fmt.Errorf("waiting for istio namespace to be deleted: %v", err)
   125  	}
   126  	return nil
   127  }
   128  
   129  // deleteIstioCanary deletes installed Istio Helm charts and resources
   130  func deleteIstioRevision(h *helm.Helm, revision string) error {
   131  	scopes.Framework.Infof("cleaning up revision resources (%s)", revision)
   132  	name := helmtest.IstiodReleaseName + "-" + strings.ReplaceAll(revision, ".", "-")
   133  	if err := h.DeleteChart(name, helmtest.IstioNamespace); err != nil {
   134  		return fmt.Errorf("failed to delete revision (%s)", name)
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  // performInPlaceUpgradeFunc returns the provided function necessary to run inside an integration test
   141  // for upgrade capability
   142  func performInPlaceUpgradeFunc(previousVersion string, isAmbient bool) func(framework.TestContext) {
   143  	return func(t framework.TestContext) {
   144  		cs := t.Clusters().Default().(*kubecluster.Cluster)
   145  		h := helm.New(cs.Filename())
   146  		nsConfig := helmtest.DefaultNamespaceConfig
   147  		t.CleanupConditionally(func() {
   148  			// only need to do call this once as helm doesn't need to remove
   149  			// all versions
   150  			helmtest.DeleteIstio(t, h, cs, nsConfig, isAmbient)
   151  		})
   152  		s := t.Settings()
   153  		prevVariant := s.Image.Variant
   154  		// Istio 1.21 ambient did not support distroless, always use debug.
   155  		// TODO(https://github.com/istio/istio/issues/50387) remove this, always use s.Image.Variant
   156  		if isAmbient {
   157  			prevVariant = "debug"
   158  		}
   159  		overrideValuesFile := helmtest.GetValuesOverrides(t, gcrHub, "", prevVariant, "", isAmbient)
   160  		helmtest.InstallIstio(t, cs, h, overrideValuesFile, previousVersion, true, isAmbient, nsConfig)
   161  		helmtest.VerifyInstallation(t, cs, nsConfig, true, isAmbient, "")
   162  
   163  		_, oldClient, oldServer := sanitycheck.SetupTrafficTest(t, t, "")
   164  		sanitycheck.RunTrafficTestClientServer(t, oldClient, oldServer)
   165  
   166  		overrideValuesFile = helmtest.GetValuesOverrides(t, s.Image.Hub, s.Image.Tag, s.Image.Variant, "", isAmbient)
   167  		upgradeCharts(t, h, overrideValuesFile, nsConfig, isAmbient)
   168  		helmtest.VerifyInstallation(t, cs, nsConfig, true, isAmbient, "")
   169  
   170  		_, newClient, newServer := sanitycheck.SetupTrafficTest(t, t, "")
   171  		sanitycheck.RunTrafficTestClientServer(t, newClient, newServer)
   172  
   173  		// now check that we are compatible with N-1 proxy with N proxy
   174  		sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
   175  	}
   176  }
   177  
   178  // performCanaryUpgradeFunc returns the provided function necessary to run inside an integration test
   179  // for upgrade capability with revisions
   180  func performCanaryUpgradeFunc(nsConfig helmtest.NamespaceConfig, previousVersion string) func(framework.TestContext) {
   181  	return func(t framework.TestContext) {
   182  		cs := t.Clusters().Default().(*kubecluster.Cluster)
   183  		h := helm.New(cs.Filename())
   184  		t.CleanupConditionally(func() {
   185  			err := deleteIstioRevision(h, canaryTag)
   186  			if err != nil {
   187  				t.Fatalf("could not delete istio: %v", err)
   188  			}
   189  			err = deleteIstio(cs, h, nsConfig, false)
   190  			if err != nil {
   191  				t.Fatalf("could not delete istio: %v", err)
   192  			}
   193  		})
   194  
   195  		s := t.Settings()
   196  		overrideValuesFile := helmtest.GetValuesOverrides(t, gcrHub, "", s.Image.Variant, "", false)
   197  		helmtest.InstallIstio(t, cs, h, overrideValuesFile, previousVersion, false, false, helmtest.DefaultNamespaceConfig)
   198  		helmtest.VerifyInstallation(t, cs, helmtest.DefaultNamespaceConfig, false, false, "")
   199  
   200  		_, oldClient, oldServer := sanitycheck.SetupTrafficTest(t, t, "")
   201  		sanitycheck.RunTrafficTestClientServer(t, oldClient, oldServer)
   202  
   203  		overrideValuesFile = helmtest.GetValuesOverrides(t, s.Image.Hub, s.Image.Tag, s.Image.Variant, canaryTag, false)
   204  		helmtest.InstallIstioWithRevision(t, cs, h, "", canaryTag, overrideValuesFile, true, false)
   205  		helmtest.VerifyInstallation(t, cs, helmtest.DefaultNamespaceConfig, false, false, "")
   206  
   207  		// now that we've installed with a revision we have a new mutating webhook
   208  		helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
   209  			"istio-sidecar-injector",
   210  			"istio-sidecar-injector-canary",
   211  		})
   212  
   213  		_, newClient, newServer := sanitycheck.SetupTrafficTest(t, t, canaryTag)
   214  		sanitycheck.RunTrafficTestClientServer(t, newClient, newServer)
   215  
   216  		// now check that we are compatible with N-1 proxy with N proxy
   217  		sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
   218  	}
   219  }
   220  
   221  // performRevisionTagsUpgradeFunc returns the provided function necessary to run inside an integration test
   222  // for upgrade capability with stable label revision upgrades
   223  func performRevisionTagsUpgradeFunc(previousVersion string) func(framework.TestContext) {
   224  	return func(t framework.TestContext) {
   225  		cs := t.Clusters().Default().(*kubecluster.Cluster)
   226  		h := helm.New(cs.Filename())
   227  		t.CleanupConditionally(func() {
   228  			err := deleteIstioRevision(h, latestRevisionTag)
   229  			if err != nil {
   230  				t.Fatalf("could not delete istio revision (%v): %v", latestRevisionTag, err)
   231  			}
   232  			err = deleteIstioRevision(h, previousVersion)
   233  			if err != nil {
   234  				t.Fatalf("could not delete istio revision (%v): %v", previousVersion, err)
   235  			}
   236  
   237  			err = cleanupIstio(cs, h)
   238  			if err != nil {
   239  				t.Fatalf("could not cleanup istio: %v", err)
   240  			}
   241  		})
   242  		s := t.Settings()
   243  		// install MAJOR.MINOR.PATCH charts with revision set to "MAJOR-MINOR-PATCH" name. For example,
   244  		// helm install istio-base istio/base --version 1.15.0 --namespace istio-system -f values.yaml
   245  		// helm install istiod-1-15 istio/istiod --version 1.15.0 -f values.yaml
   246  		previousRevision := strings.ReplaceAll(previousVersion, ".", "-")
   247  		overrideValuesFile := helmtest.GetValuesOverrides(t, gcrHub, "", s.Image.Variant, previousRevision, false)
   248  		helmtest.InstallIstioWithRevision(t, cs, h, previousVersion, previousRevision, overrideValuesFile, false, true)
   249  		helmtest.VerifyInstallation(t, cs, helmtest.DefaultNamespaceConfig, false, false, "")
   250  
   251  		// helm template istiod-1-15-0 istio/istiod --version 1.15.0 -s templates/revision-tags.yaml --set revision=1-15-0 --set revisionTags={prod}
   252  		helmtest.SetRevisionTagWithVersion(t, h, previousRevision, prodTag, previousVersion)
   253  		helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
   254  			"istio-revision-tag-prod",
   255  			fmt.Sprintf("istio-sidecar-injector-%s", previousRevision),
   256  		})
   257  
   258  		// setup istio.io/rev=1-15-0 for the default-1 namespace
   259  		oldNs, oldClient, oldServer := sanitycheck.SetupTrafficTest(t, t, previousRevision)
   260  		sanitycheck.RunTrafficTestClientServer(t, oldClient, oldServer)
   261  
   262  		// install the charts from this branch with revision set to "latest"
   263  		// helm upgrade istio-base ../manifests/charts/base --namespace istio-system -f values.yaml
   264  		// helm install istiod-latest ../manifests/charts/istio-control/istio-discovery -f values.yaml
   265  		overrideValuesFile = helmtest.GetValuesOverrides(t, s.Image.Hub, s.Image.Tag, s.Image.Variant, latestRevisionTag, false)
   266  		helmtest.InstallIstioWithRevision(t, cs, h, "", latestRevisionTag, overrideValuesFile, true, false)
   267  		helmtest.VerifyInstallation(t, cs, helmtest.DefaultNamespaceConfig, false, false, "")
   268  
   269  		// helm template istiod-latest ../manifests/charts/istio-control/istio-discovery --namespace istio-system
   270  		//    -s templates/revision-tags.yaml --set revision=latest --set revisionTags={canary}
   271  		helmtest.SetRevisionTag(t, h, "", latestRevisionTag, canaryTag, helmtest.ManifestsChartPath, "")
   272  		helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
   273  			"istio-revision-tag-prod",
   274  			fmt.Sprintf("istio-sidecar-injector-%v", previousRevision),
   275  			"istio-revision-tag-canary",
   276  			"istio-sidecar-injector-latest",
   277  		})
   278  
   279  		// setup istio.io/rev=latest for the default-2 namespace
   280  		_, newClient, newServer := sanitycheck.SetupTrafficTest(t, t, latestRevisionTag)
   281  		sanitycheck.RunTrafficTestClientServer(t, newClient, newServer)
   282  
   283  		// now check that we are compatible with N-1 proxy with N proxy between a client
   284  		// in default-1 namespace and a server in the default-2 namespace, respectively
   285  		sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
   286  
   287  		// change the mutating webhook configuration to use the latest revision (istiod-latest service in istio-system)
   288  		// helm template istiod-latest ../manifests/charts/istio-control/istio-discovery --namespace istio-system
   289  		//    -s templates/revision-tags.yaml --set revision=latest --set revisionTags={prod}
   290  		helmtest.SetRevisionTag(t, h, "", latestRevisionTag, prodTag, helmtest.ManifestsChartPath, "")
   291  
   292  		// change the old namespace that was pointing to the old prod (1-15-0) to point to the
   293  		// 'latest' revision by setting the `istio.io/rev=prod` label on the namespace
   294  		err := oldNs.SetLabel(label.IoIstioRev.Name, prodTag)
   295  		if err != nil {
   296  			t.Fatal("could not remove istio.io/rev from old namespace")
   297  		}
   298  
   299  		err = oldClient.Restart()
   300  		if err != nil {
   301  			t.Fatal("could not restart old client")
   302  		}
   303  		err = oldServer.Restart()
   304  		if err != nil {
   305  			t.Fatal("could not restart old server")
   306  		}
   307  
   308  		// make sure the restarted pods in default-1 namespace do not use
   309  		// the previous version (check for the previousVersion in the image string)
   310  		err = checkVersion(t, oldNs.Name(), previousVersion)
   311  		if err != nil {
   312  			t.Fatalf("found a pod in namespace (%s) with the previous version: %v", oldNs.Name(), err)
   313  		}
   314  
   315  		// now check traffic still works between the proxies
   316  		sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
   317  	}
   318  }
   319  
   320  func checkVersion(t framework.TestContext, namespace, version string) error {
   321  	// func NewPodFetch(a istioKube.CLIClient, namespace string, selectors ...string) PodFetchFunc {
   322  	fetch := kubetest.NewPodFetch(t.Clusters().Default(), namespace)
   323  	pods, err := kubetest.CheckPodsAreReady(fetch)
   324  	if err != nil {
   325  		return fmt.Errorf("failed to retrieve pods: %v", err)
   326  	}
   327  	for _, p := range pods {
   328  		for _, c := range p.Spec.Containers {
   329  			if strings.Contains(c.Image, version) {
   330  				return fmt.Errorf("expected container image to not include version %q, got %q", version, c.Image)
   331  			}
   332  		}
   333  	}
   334  
   335  	return nil
   336  }