istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/helm/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 helm
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  	"time"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  	kerrors "k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/types"
    32  
    33  	networking "istio.io/api/networking/v1alpha3"
    34  	"istio.io/client-go/pkg/apis/networking/v1alpha3"
    35  	"istio.io/istio/pkg/config/constants"
    36  	"istio.io/istio/pkg/log"
    37  	"istio.io/istio/pkg/maps"
    38  	"istio.io/istio/pkg/test"
    39  	"istio.io/istio/pkg/test/env"
    40  	"istio.io/istio/pkg/test/framework"
    41  	"istio.io/istio/pkg/test/framework/components/cluster"
    42  	"istio.io/istio/pkg/test/framework/components/cluster/kube"
    43  	"istio.io/istio/pkg/test/helm"
    44  	kubetest "istio.io/istio/pkg/test/kube"
    45  	"istio.io/istio/pkg/test/scopes"
    46  	"istio.io/istio/pkg/test/util/retry"
    47  )
    48  
    49  const (
    50  	IstioNamespace         = "istio-system"
    51  	ReleasePrefix          = "istio-"
    52  	BaseChart              = "base"
    53  	CRDsFolder             = "crds"
    54  	DiscoveryChartsDir     = "istio-discovery"
    55  	BaseReleaseName        = ReleasePrefix + BaseChart
    56  	RepoBaseChartPath      = BaseChart
    57  	RepoDiscoveryChartPath = "istiod"
    58  	RepoGatewayChartPath   = "gateway"
    59  	IstiodReleaseName      = "istiod"
    60  	IngressReleaseName     = "istio-ingress"
    61  	ControlChartsDir       = "istio-control"
    62  	GatewayChartsDir       = "gateway"
    63  	CniChartsDir           = "istio-cni"
    64  	ZtunnelChartsDir       = "ztunnel"
    65  	RepoCniChartPath       = "cni"
    66  	CniReleaseName         = ReleasePrefix + "cni"
    67  	RepoZtunnelChartPath   = "ztunnel"
    68  	ZtunnelReleaseName     = "ztunnel"
    69  
    70  	RetryDelay   = 2 * time.Second
    71  	RetryTimeOut = 5 * time.Minute
    72  	Timeout      = 2 * time.Minute
    73  
    74  	defaultValues = `
    75  global:
    76    hub: %s
    77    %s
    78    variant: %q
    79  revision: "%s"
    80  `
    81  	ambientProfileOverride = `
    82  global:
    83    hub: %s
    84    %s
    85    variant: %q
    86  profile: ambient
    87  `
    88  	sampleEnvoyFilter = `
    89  apiVersion: networking.istio.io/v1alpha3
    90  kind: EnvoyFilter
    91  metadata:
    92    name: sample
    93  spec:
    94    workloadSelector:
    95      labels:
    96        istio: ingressgateway
    97    configPatches:
    98    - applyTo: NETWORK_FILTER # http connection manager is a filter in Envoy
    99      match:
   100        context: GATEWAY
   101        listener:
   102          filterChain:
   103            sni: app.example.com
   104            filter:
   105              name: "envoy.filters.network.http_connection_manager"
   106      patch:
   107        operation: MERGE
   108        value:
   109          typed_config:
   110            "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
   111            xff_num_trusted_hops: 5
   112            common_http_protocol_options:
   113              idle_timeout: 30s
   114  `
   115  
   116  	revisionedSampleEnvoyFilter = `
   117  apiVersion: networking.istio.io/v1alpha3
   118  kind: EnvoyFilter
   119  metadata:
   120    name: sample
   121    labels:
   122      istio.io/rev: %s
   123  spec:
   124    workloadSelector:
   125      labels:
   126        istio: ingressgateway
   127    configPatches:
   128    - applyTo: NETWORK_FILTER # http connection manager is a filter in Envoy
   129      match:
   130        context: GATEWAY
   131        listener:
   132          filterChain:
   133            sni: app.example.com
   134            filter:
   135              name: "envoy.filters.network.http_connection_manager"
   136      patch:
   137        operation: MERGE
   138        value:
   139          typed_config:
   140            "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
   141            xff_num_trusted_hops: 5
   142            common_http_protocol_options:
   143              idle_timeout: 30s
   144  `
   145  
   146  	extendedTelemetry = `
   147  apiVersion: telemetry.istio.io/v1
   148  kind: Telemetry
   149  metadata:
   150    name: sample
   151  spec:
   152    metrics:
   153      - providers:
   154        - name: prometheus
   155        reportingInterval: 10s
   156  `
   157  
   158  	revisionedExtendedTelemetry = `
   159  apiVersion: telemetry.istio.io/v1
   160  kind: Telemetry
   161  metadata:
   162    name: sample
   163    labels:
   164      istio.io/rev: %s
   165  spec:
   166    metrics:
   167      - providers:
   168        - name: prometheus
   169        reportingInterval: 10s
   170  `
   171  )
   172  
   173  // ManifestsChartPath is path of local Helm charts used for testing.
   174  var ManifestsChartPath = filepath.Join(env.IstioSrc, "manifests/charts")
   175  
   176  // adjustValuesForOpenShift adds the "openshift" or "openshift-ambient" profile to the
   177  // values if tests are running in OpenShift, and returns the modified values
   178  func adjustValuesForOpenShift(ctx framework.TestContext, values string) string {
   179  	if !ctx.Settings().OpenShift {
   180  		return values
   181  	}
   182  
   183  	if !strings.Contains(values, "profile: ") {
   184  		values += "\nprofile: openshift\n"
   185  	} else if strings.Contains(values, "profile: ambient") {
   186  		values = strings.ReplaceAll(values, "profile: ambient", "profile: openshift-ambient")
   187  	}
   188  
   189  	return values
   190  }
   191  
   192  // getValuesOverrides returns the values file created to pass into Helm override default values
   193  // for the hub and tag.
   194  //
   195  // Tag can be the empty string, which means the values file will not have a
   196  // tag. In other words, the tag will come from the chart. This is useful in the upgrade
   197  // tests, where we deploy an old Istio version using the chart, and we want to use
   198  // the tag that comes with the chart.
   199  func GetValuesOverrides(ctx framework.TestContext, hub, tag, variant, revision string, isAmbient bool) string {
   200  	workDir := ctx.CreateTmpDirectoryOrFail("helm")
   201  
   202  	// Only use a tag value if not empty. Not having a tag in values means: Use the tag directly from the chart
   203  	if tag != "" {
   204  		tag = "tag: " + tag
   205  	}
   206  
   207  	overrideValues := fmt.Sprintf(defaultValues, hub, tag, variant, revision)
   208  	if isAmbient {
   209  		overrideValues = fmt.Sprintf(ambientProfileOverride, hub, tag, variant)
   210  	}
   211  	overrideValues = adjustValuesForOpenShift(ctx, overrideValues)
   212  
   213  	overrideValuesFile := filepath.Join(workDir, "values.yaml")
   214  	if err := os.WriteFile(overrideValuesFile, []byte(overrideValues), os.ModePerm); err != nil {
   215  		ctx.Fatalf("failed to write iop cr file: %v", err)
   216  	}
   217  
   218  	return overrideValuesFile
   219  }
   220  
   221  var DefaultNamespaceConfig = NewNamespaceConfig()
   222  
   223  func NewNamespaceConfig(config ...types.NamespacedName) NamespaceConfig {
   224  	result := make(nsConfig, len(config))
   225  	for _, c := range config {
   226  		result[c.Name] = c.Namespace
   227  	}
   228  	return result
   229  }
   230  
   231  type nsConfig map[string]string
   232  
   233  func (n nsConfig) Get(name string) string {
   234  	if ns, ok := n[name]; ok {
   235  		return ns
   236  	}
   237  	return IstioNamespace
   238  }
   239  
   240  func (n nsConfig) Set(name, ns string) {
   241  	n[name] = ns
   242  }
   243  
   244  func (n nsConfig) AllNamespaces() []string {
   245  	return maps.Values(n)
   246  }
   247  
   248  type NamespaceConfig interface {
   249  	Get(name string) string
   250  	Set(name, ns string)
   251  	AllNamespaces() []string
   252  }
   253  
   254  // InstallIstio install Istio using Helm charts with the provided
   255  // override values file and fails the tests on any failures.
   256  func InstallIstio(t framework.TestContext, cs cluster.Cluster, h *helm.Helm, overrideValuesFile,
   257  	version string, installGateway bool, ambientProfile bool, nsConfig NamespaceConfig,
   258  ) {
   259  	for _, ns := range nsConfig.AllNamespaces() {
   260  		CreateNamespace(t, cs, ns)
   261  	}
   262  	CreateNamespace(t, cs, IstioNamespace)
   263  
   264  	versionArgs := ""
   265  
   266  	baseChartPath := RepoBaseChartPath
   267  	discoveryChartPath := RepoDiscoveryChartPath
   268  	gatewayChartPath := RepoGatewayChartPath
   269  	cniChartPath := RepoCniChartPath
   270  	ztunnelChartPath := RepoZtunnelChartPath
   271  
   272  	gatewayOverrideValuesFile := overrideValuesFile
   273  
   274  	if version != "" {
   275  		// Prepend ~ to the version, so that we can refer to the latest patch version of a minor version
   276  		versionArgs = fmt.Sprintf("--repo %s --version ~%s", t.Settings().HelmRepo, version)
   277  		// Currently the ambient in-place upgrade tests try an upgrade from previous release which is 1.20,
   278  		// and many of the profile override values seem to be unrecognized by the gateway installation.
   279  		// So, this is a workaround until we move to 1.21 where we can use --set profile=ambient for the install/upgrade.
   280  		// TODO: Remove this once the previous release version for the test becomes 1.21
   281  		// refer: https://github.com/istio/istio/issues/49242
   282  		if ambientProfile {
   283  			gatewayOverrideValuesFile = GetValuesOverrides(t, t.Settings().Image.Hub, version, t.Settings().Image.Variant, "", false)
   284  		}
   285  	} else {
   286  		baseChartPath = filepath.Join(ManifestsChartPath, BaseChart)
   287  		discoveryChartPath = filepath.Join(ManifestsChartPath, ControlChartsDir, DiscoveryChartsDir)
   288  		gatewayChartPath = filepath.Join(ManifestsChartPath, version, GatewayChartsDir)
   289  		cniChartPath = filepath.Join(ManifestsChartPath, version, CniChartsDir)
   290  		ztunnelChartPath = filepath.Join(ManifestsChartPath, version, ZtunnelChartsDir)
   291  
   292  	}
   293  
   294  	// Install base chart
   295  	err := h.InstallChart(BaseReleaseName, baseChartPath, nsConfig.Get(BaseReleaseName), overrideValuesFile, Timeout, versionArgs)
   296  	if err != nil {
   297  		t.Fatalf("failed to install istio %s chart: %v", BaseChart, err)
   298  	}
   299  
   300  	// Install discovery chart
   301  	err = h.InstallChart(IstiodReleaseName, discoveryChartPath, nsConfig.Get(IstiodReleaseName), overrideValuesFile, Timeout, versionArgs)
   302  	if err != nil {
   303  		t.Fatalf("failed to install istio %s chart: %v", DiscoveryChartsDir, err)
   304  	}
   305  
   306  	if installGateway {
   307  		err = h.InstallChart(IngressReleaseName, gatewayChartPath, nsConfig.Get(IngressReleaseName), gatewayOverrideValuesFile, Timeout, versionArgs)
   308  		if err != nil {
   309  			t.Fatalf("failed to install istio %s chart: %v", GatewayChartsDir, err)
   310  		}
   311  	}
   312  
   313  	if ambientProfile || t.Settings().OpenShift {
   314  		// Install cni chart
   315  		err = h.InstallChart(CniReleaseName, cniChartPath, nsConfig.Get(CniReleaseName), overrideValuesFile, Timeout, versionArgs)
   316  		if err != nil {
   317  			t.Fatalf("failed to install istio %s chart: %v", CniChartsDir, err)
   318  		}
   319  	}
   320  
   321  	if ambientProfile {
   322  
   323  		// Install ztunnel chart
   324  		err = h.InstallChart(ZtunnelReleaseName, ztunnelChartPath, nsConfig.Get(ZtunnelReleaseName), overrideValuesFile, Timeout, versionArgs)
   325  		if err != nil {
   326  			t.Fatalf("failed to install istio %s chart: %v", ZtunnelChartsDir, err)
   327  		}
   328  	}
   329  }
   330  
   331  // InstallIstioWithRevision install Istio using Helm charts with the provided
   332  // override values file and fails the tests on any failures.
   333  func InstallIstioWithRevision(t framework.TestContext, cs cluster.Cluster,
   334  	h *helm.Helm, version, revision, overrideValuesFile string, upgradeBaseChart, useTestData bool,
   335  ) {
   336  	CreateNamespace(t, cs, IstioNamespace)
   337  	versionArgs := ""
   338  	baseChartPath := RepoBaseChartPath
   339  	discoveryChartPath := RepoDiscoveryChartPath
   340  	if version != "" {
   341  		// Prepend ~ to the version, so that we can refer to the latest patch version of a minor version
   342  		versionArgs = fmt.Sprintf("--repo %s --version ~%s", t.Settings().HelmRepo, version)
   343  	} else {
   344  		baseChartPath = filepath.Join(ManifestsChartPath, BaseChart)
   345  		discoveryChartPath = filepath.Join(ManifestsChartPath, ControlChartsDir, DiscoveryChartsDir)
   346  	}
   347  	// base chart may already be installed if the Istio was previously already installed
   348  	if upgradeBaseChart {
   349  		// upgrade is always done with ../manifests/charts/base
   350  		err := h.UpgradeChart(BaseReleaseName, filepath.Join(ManifestsChartPath, BaseChart),
   351  			IstioNamespace, overrideValuesFile, Timeout)
   352  		if err != nil {
   353  			t.Fatalf("failed to upgrade istio %s chart", BaseChart)
   354  		}
   355  	} else {
   356  		err := h.InstallChart(BaseReleaseName, baseChartPath, IstioNamespace, overrideValuesFile, Timeout, versionArgs)
   357  		if err != nil {
   358  			t.Fatalf("failed to upgrade istio %s chart", BaseChart)
   359  		}
   360  	}
   361  
   362  	// install discovery chart with --set revision=NAME
   363  	if useTestData {
   364  		err := h.InstallChart(IstiodReleaseName+"-"+revision, discoveryChartPath, IstioNamespace, overrideValuesFile, Timeout, versionArgs)
   365  		if err != nil {
   366  			t.Fatalf("failed to install istio %s chart", DiscoveryChartsDir)
   367  		}
   368  	} else {
   369  		err := h.InstallChart(IstiodReleaseName+"-"+revision, filepath.Join(ManifestsChartPath, ControlChartsDir, DiscoveryChartsDir),
   370  			IstioNamespace, overrideValuesFile, Timeout)
   371  		if err != nil {
   372  			t.Fatalf("failed to install istio %s chart", DiscoveryChartsDir)
   373  		}
   374  
   375  	}
   376  }
   377  
   378  func CreateNamespace(t test.Failer, cs cluster.Cluster, namespace string) {
   379  	if _, err := cs.Kube().CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{
   380  		ObjectMeta: metav1.ObjectMeta{
   381  			Name: namespace,
   382  		},
   383  	}, metav1.CreateOptions{}); err != nil {
   384  		if kerrors.IsAlreadyExists(err) {
   385  			log.Debugf("%v namespace already exist", IstioNamespace)
   386  		} else {
   387  			t.Fatalf("failed to create %v namespace: %v", IstioNamespace, err)
   388  		}
   389  	}
   390  }
   391  
   392  // DeleteIstio deletes installed Istio Helm charts and resources
   393  func DeleteIstio(t framework.TestContext, h *helm.Helm, cs *kube.Cluster, config NamespaceConfig, isAmbient bool) {
   394  	scopes.Framework.Infof("cleaning up resources")
   395  	if err := h.DeleteChart(IngressReleaseName, config.Get(IngressReleaseName)); err != nil {
   396  		t.Errorf("failed to delete %s release: %v", IngressReleaseName, err)
   397  	}
   398  	if err := h.DeleteChart(IstiodReleaseName, config.Get(IstiodReleaseName)); err != nil {
   399  		t.Errorf("failed to delete %s release: %v", IstiodReleaseName, err)
   400  	}
   401  	if isAmbient {
   402  		if err := h.DeleteChart(ZtunnelReleaseName, config.Get(ZtunnelReleaseName)); err != nil {
   403  			t.Errorf("failed to delete %s release: %v", ZtunnelReleaseName, err)
   404  		}
   405  	}
   406  	if isAmbient || t.Settings().OpenShift {
   407  		if err := h.DeleteChart(CniReleaseName, config.Get(CniReleaseName)); err != nil {
   408  			t.Errorf("failed to delete %s release: %v", CniReleaseName, err)
   409  		}
   410  	}
   411  	if err := h.DeleteChart(BaseReleaseName, config.Get(BaseReleaseName)); err != nil {
   412  		t.Errorf("failed to delete %s release: %v", BaseReleaseName, err)
   413  	}
   414  	for _, ns := range config.AllNamespaces() {
   415  		if ns == constants.KubeSystemNamespace {
   416  			continue
   417  		}
   418  		if err := cs.Kube().CoreV1().Namespaces().Delete(context.TODO(), ns, metav1.DeleteOptions{}); err != nil {
   419  			t.Errorf("failed to delete %s namespace: %v", ns, err)
   420  		}
   421  		if err := kubetest.WaitForNamespaceDeletion(cs.Kube(), ns, retry.Timeout(RetryTimeOut)); err != nil {
   422  			t.Errorf("waiting for %s namespace to be deleted: %v", ns, err)
   423  		}
   424  	}
   425  }
   426  
   427  // VerifyInstallation verify that the Helm installation is successful
   428  func VerifyPodReady(ctx framework.TestContext, cs cluster.Cluster, ns, label string) {
   429  	retry.UntilSuccessOrFail(ctx, func() error {
   430  		if _, err := kubetest.CheckPodsAreReady(kubetest.NewPodFetch(cs, ns, label)); err != nil {
   431  			return fmt.Errorf("%s pod is not ready: %v", label, err)
   432  		}
   433  		return nil
   434  	}, retry.Timeout(RetryTimeOut), retry.Delay(RetryDelay))
   435  }
   436  
   437  // VerifyInstallation verify that the Helm installation is successful
   438  func VerifyInstallation(ctx framework.TestContext, cs cluster.Cluster, nsConfig NamespaceConfig, verifyGateway bool, verifyAmbient bool, revision string) {
   439  	scopes.Framework.Infof("=== verifying istio installation === ")
   440  
   441  	validatingwebhookName := "istiod-default-validator"
   442  	if revision != "" {
   443  		validatingwebhookName = fmt.Sprintf("istio-validator-%s-istio-system", revision)
   444  	}
   445  	VerifyValidatingWebhookConfigurations(ctx, cs, []string{
   446  		validatingwebhookName,
   447  	})
   448  
   449  	VerifyPodReady(ctx, cs, nsConfig.Get(IstiodReleaseName), "app=istiod")
   450  	if verifyAmbient || ctx.Settings().OpenShift {
   451  		VerifyPodReady(ctx, cs, nsConfig.Get(CniReleaseName), "k8s-app=istio-cni-node")
   452  	}
   453  	if verifyAmbient {
   454  		VerifyPodReady(ctx, cs, nsConfig.Get(ZtunnelReleaseName), "app=ztunnel")
   455  	}
   456  	if verifyGateway {
   457  		VerifyPodReady(ctx, cs, nsConfig.Get(IngressReleaseName), "app=istio-ingress")
   458  	}
   459  	scopes.Framework.Infof("=== succeeded ===")
   460  }
   461  
   462  func SetRevisionTagWithVersion(ctx framework.TestContext, h *helm.Helm, revision, revisionTag, version string) {
   463  	scopes.Framework.Infof("=== setting revision tag with version === ")
   464  	// Prepend ~ to the version, so that we can refer to the latest patch version of a minor version
   465  	template, err := h.Template(IstiodReleaseName+"-"+revision, RepoDiscoveryChartPath,
   466  		IstioNamespace, "templates/revision-tags.yaml", Timeout, "--version", "~"+version, "--repo", ctx.Settings().HelmRepo, "--set",
   467  		fmt.Sprintf("revision=%s", revision), "--set", fmt.Sprintf("revisionTags={%s}", revisionTag))
   468  	if err != nil {
   469  		ctx.Fatalf("failed to install istio %s chart", DiscoveryChartsDir)
   470  	}
   471  
   472  	err = ctx.ConfigIstio().YAML(IstioNamespace, template).Apply()
   473  	if err != nil {
   474  		ctx.Fatalf("failed to apply templated revision tags yaml: %v", err)
   475  	}
   476  
   477  	scopes.Framework.Infof("=== succeeded === ")
   478  }
   479  
   480  func SetRevisionTag(ctx framework.TestContext, h *helm.Helm, fileSuffix, revision, revisionTag, relPath, version string) {
   481  	scopes.Framework.Infof("=== setting revision tag === ")
   482  	template, err := h.Template(IstiodReleaseName+"-"+revision, filepath.Join(relPath, version, ControlChartsDir, DiscoveryChartsDir)+fileSuffix,
   483  		IstioNamespace, "templates/revision-tags.yaml", Timeout, "--set",
   484  		fmt.Sprintf("revision=%s", revision), "--set", fmt.Sprintf("revisionTags={%s}", revisionTag))
   485  	if err != nil {
   486  		ctx.Fatalf("failed to install istio %s chart", DiscoveryChartsDir)
   487  	}
   488  
   489  	err = ctx.ConfigIstio().YAML(IstioNamespace, template).Apply()
   490  	if err != nil {
   491  		ctx.Fatalf("failed to apply templated revision tags yaml: %v", err)
   492  	}
   493  
   494  	scopes.Framework.Infof("=== succeeded === ")
   495  }
   496  
   497  // VerifyMutatingWebhookConfigurations verifies that the proper number of mutating webhooks are running, used with
   498  // revisions and revision tags
   499  func VerifyMutatingWebhookConfigurations(ctx framework.TestContext, cs cluster.Cluster, names []string) {
   500  	scopes.Framework.Infof("=== verifying mutating webhook configurations === ")
   501  	if ok := kubetest.MutatingWebhookConfigurationsExists(cs.Kube(), names); !ok {
   502  		ctx.Fatalf("Not all mutating webhook configurations were installed. Expected [%v]", names)
   503  	}
   504  	scopes.Framework.Infof("=== succeeded ===")
   505  }
   506  
   507  // VerifyValidatingWebhookConfigurations verifies that the proper number of validating webhooks are running, used with
   508  // revisions and revision tags
   509  func VerifyValidatingWebhookConfigurations(ctx framework.TestContext, cs cluster.Cluster, names []string) {
   510  	scopes.Framework.Infof("=== verifying validating webhook configurations === ")
   511  	if ok := kubetest.ValidatingWebhookConfigurationsExists(cs.Kube(), names); !ok {
   512  		ctx.Fatalf("Not all validating webhook configurations were installed. Expected [%v]", names)
   513  	}
   514  	scopes.Framework.Infof("=== succeeded ===")
   515  }
   516  
   517  // verifyValidation verifies that Istio resource validation is active on the cluster.
   518  func verifyValidation(ctx framework.TestContext, revision string) {
   519  	ctx.Helper()
   520  	invalidGateway := &v1alpha3.Gateway{
   521  		ObjectMeta: metav1.ObjectMeta{
   522  			Name:      "invalid-istio-gateway",
   523  			Namespace: IstioNamespace,
   524  		},
   525  		Spec: networking.Gateway{},
   526  	}
   527  
   528  	if revision != "" {
   529  		invalidGateway.Labels = map[string]string{
   530  			"istio.io/rev": revision,
   531  		}
   532  	}
   533  	createOptions := metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}
   534  	istioClient := ctx.Clusters().Default().Istio().NetworkingV1alpha3()
   535  	retry.UntilOrFail(ctx, func() bool {
   536  		_, err := istioClient.Gateways(IstioNamespace).Create(context.TODO(), invalidGateway, createOptions)
   537  		rejected := err != nil
   538  		return rejected
   539  	})
   540  }