istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/pilot/revisioned_upgrade_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 pilot
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/hashicorp/go-multierror"
    27  
    28  	"istio.io/istio/pkg/config/protocol"
    29  	"istio.io/istio/pkg/log"
    30  	"istio.io/istio/pkg/test/framework"
    31  	"istio.io/istio/pkg/test/framework/components/echo"
    32  	"istio.io/istio/pkg/test/framework/components/echo/deployment"
    33  	"istio.io/istio/pkg/test/framework/components/echo/util/traffic"
    34  	"istio.io/istio/pkg/test/framework/components/namespace"
    35  	"istio.io/istio/pkg/test/framework/label"
    36  	kubetest "istio.io/istio/pkg/test/kube"
    37  )
    38  
    39  const (
    40  	callInterval     = 200 * time.Millisecond
    41  	successThreshold = 0.95
    42  )
    43  
    44  // TestRevisionedUpgrade tests a revision-based upgrade from the specified versions to current master
    45  func TestRevisionedUpgrade(t *testing.T) {
    46  	// nolint: staticcheck
    47  	framework.NewTest(t).
    48  		RequiresSingleCluster().
    49  		RequiresLocalControlPlane().
    50  		// Requires installation of CPs from manifests, won't succeed
    51  		// if existing CPs have different root cert
    52  		Label(label.CustomSetup).
    53  		Run(func(t framework.TestContext) {
    54  			t.Skip("https://github.com/istio/istio/pull/46213")
    55  			// Kubernetes 1.22 drops support for a number of legacy resources, so we cannot install the old versions
    56  			if !t.Clusters().Default().MaxKubeVersion(21) {
    57  				t.Skipf("k8s version not supported for %s (>%s)", t.Name(), "1.21")
    58  			}
    59  			versions := []string{NMinusOne, NMinusTwo, NMinusThree, NMinusFour}
    60  			for _, v := range versions {
    61  				t.NewSubTest(fmt.Sprintf("%s->master", v)).Run(func(t framework.TestContext) {
    62  					testUpgradeFromVersion(t, v)
    63  				})
    64  			}
    65  		})
    66  }
    67  
    68  // testUpgradeFromVersion tests an upgrade from the target version to the master version
    69  // provided fromVersion must be present in tests/integration/pilot/testdata/upgrade for the installation to succeed
    70  // TODO(monkeyanator) pass this a generic UpgradeFunc allowing for reuse across in-place and revisioned upgrades
    71  func testUpgradeFromVersion(t framework.TestContext, fromVersion string) {
    72  	configs := make(map[string]string)
    73  	t.CleanupConditionally(func() {
    74  		for _, config := range configs {
    75  			_ = t.ConfigIstio().YAML("istio-system", config).Delete()
    76  		}
    77  	})
    78  
    79  	// install control plane on the specified version and create namespace pointed to that control plane
    80  	installRevisionOrFail(t, fromVersion, configs)
    81  	revision := strings.ReplaceAll(fromVersion, ".", "-")
    82  	revisionedNamespace := namespace.NewOrFail(t, t, namespace.Config{
    83  		Prefix:   revision,
    84  		Inject:   true,
    85  		Revision: revision,
    86  	})
    87  
    88  	var revisionedInstance echo.Instance
    89  	builder := deployment.New(t)
    90  	builder.With(&revisionedInstance, echo.Config{
    91  		Service:   fmt.Sprintf("svc-%s", revision),
    92  		Namespace: revisionedNamespace,
    93  		Ports: []echo.Port{
    94  			{
    95  				Name:         "http",
    96  				Protocol:     protocol.HTTP,
    97  				WorkloadPort: 8080,
    98  			},
    99  		},
   100  	})
   101  	builder.BuildOrFail(t)
   102  
   103  	// Create a traffic generator between A and B.
   104  	g := traffic.NewGenerator(t, traffic.Config{
   105  		Source: apps.A[0],
   106  		Options: echo.CallOptions{
   107  			To:    apps.B,
   108  			Count: 1,
   109  			Port: echo.Port{
   110  				Name: "http",
   111  			},
   112  		},
   113  		Interval: callInterval,
   114  	}).Start()
   115  
   116  	if err := enableDefaultInjection(revisionedNamespace); err != nil {
   117  		t.Fatalf("could not relabel namespace to enable default injection: %v", err)
   118  	}
   119  
   120  	log.Infof("rolling out echo workloads for service %q", revisionedInstance.Config().Service)
   121  	if err := revisionedInstance.Restart(); err != nil {
   122  		t.Fatalf("revisioned instance rollout failed with: %v", err)
   123  	}
   124  	fetch := kubetest.NewPodMustFetch(t.Clusters().Default(), revisionedInstance.Config().Namespace.Name(), fmt.Sprintf("app=%s", revisionedInstance.Config().Service)) // nolint: lll
   125  	pods, err := kubetest.CheckPodsAreReady(fetch)
   126  	if err != nil {
   127  		t.Fatalf("failed to retrieve upgraded pods: %v", err)
   128  	}
   129  	for _, p := range pods {
   130  		for _, c := range p.Spec.Containers {
   131  			if strings.Contains(c.Image, fromVersion) {
   132  				t.Fatalf("expected post-upgrade container image not to include %q, got %q", fromVersion, c.Image)
   133  			}
   134  		}
   135  	}
   136  
   137  	// Stop the traffic generator and get the result.
   138  	g.Stop().CheckSuccessRate(t, successThreshold)
   139  }
   140  
   141  // enableDefaultInjection takes a namespaces and relabels it such that it will have a default sidecar injected
   142  func enableDefaultInjection(ns namespace.Instance) error {
   143  	var errs *multierror.Error
   144  	errs = multierror.Append(errs, ns.SetLabel("istio-injection", "enabled"))
   145  	errs = multierror.Append(errs, ns.RemoveLabel("istio.io/rev"))
   146  	return errs.ErrorOrNil()
   147  }