blob: f7b8fa4ad04ec2750db2d25b637b647595fde8ae [file] [log] [blame]
//go:build integ
// +build integ
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package helmupgrade
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
)
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/test/framework"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster"
kubecluster "github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/cluster/kube"
"github.com/apache/dubbo-go-pixiu/pkg/test/helm"
kubetest "github.com/apache/dubbo-go-pixiu/pkg/test/kube"
"github.com/apache/dubbo-go-pixiu/pkg/test/scopes"
"github.com/apache/dubbo-go-pixiu/pkg/test/shell"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/retry"
helmtest "github.com/apache/dubbo-go-pixiu/tests/integration/helm"
"github.com/apache/dubbo-go-pixiu/tests/util/sanitycheck"
)
const (
gcrHub = "gcr.io/istio-release"
defaultValues = `
global:
hub: %s
tag: %s
revision: "%s"
`
tarGzSuffix = ".tar.gz"
istioRevLabel = "istio.io/rev"
prodTag = "prod"
canaryTag = "canary"
latestRevisionTag = "latest"
)
// upgradeCharts upgrades Istio using Helm charts with the provided
// override values file to the latest charts in $ISTIO_SRC/manifests
func upgradeCharts(ctx framework.TestContext, h *helm.Helm, overrideValuesFile string) {
execCmd := fmt.Sprintf(
"kubectl apply -n %v -f %v",
helmtest.IstioNamespace,
filepath.Join(helmtest.ManifestsChartPath, helmtest.BaseChart, helmtest.CRDsFolder))
_, err := shell.Execute(false, execCmd)
if err != nil {
ctx.Fatalf("couldn't run kubectl apply on crds folder: %v", err)
}
// Upgrade base chart
err = h.UpgradeChart(helmtest.BaseReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.BaseChart),
helmtest.IstioNamespace, overrideValuesFile, helmtest.Timeout, "--skip-crds")
if err != nil {
ctx.Fatalf("failed to upgrade istio %s chart", helmtest.BaseChart)
}
// Upgrade discovery chart
err = h.UpgradeChart(helmtest.IstiodReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.ControlChartsDir, helmtest.DiscoveryChart),
helmtest.IstioNamespace, overrideValuesFile, helmtest.Timeout)
if err != nil {
ctx.Fatalf("failed to upgrade istio %s chart", helmtest.DiscoveryChart)
}
// Upgrade ingress gateway chart
err = h.UpgradeChart(helmtest.IngressReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.GatewayChartsDir, helmtest.IngressGatewayChart),
helmtest.IstioNamespace, overrideValuesFile, helmtest.Timeout)
if err != nil {
ctx.Fatalf("failed to upgrade istio %s chart", helmtest.IngressGatewayChart)
}
// Upgrade egress gateway chart
err = h.UpgradeChart(helmtest.EgressReleaseName, filepath.Join(helmtest.ManifestsChartPath, helmtest.GatewayChartsDir, helmtest.EgressGatewayChart),
helmtest.IstioNamespace, overrideValuesFile, helmtest.Timeout)
if err != nil {
ctx.Fatalf("failed to upgrade istio %s chart", helmtest.EgressGatewayChart)
}
}
// deleteIstio deletes installed Istio Helm charts and resources
func deleteIstio(cs cluster.Cluster, h *helm.Helm, gatewayChartsInstalled bool) error {
scopes.Framework.Infof("cleaning up resources")
if gatewayChartsInstalled {
if err := h.DeleteChart(helmtest.EgressReleaseName, helmtest.IstioNamespace); err != nil {
return fmt.Errorf("failed to delete %s release", helmtest.EgressReleaseName)
}
if err := h.DeleteChart(helmtest.IngressReleaseName, helmtest.IstioNamespace); err != nil {
return fmt.Errorf("failed to delete %s release", helmtest.IngressReleaseName)
}
}
if err := h.DeleteChart(helmtest.IstiodReleaseName, helmtest.IstioNamespace); err != nil {
return fmt.Errorf("failed to delete %s release", helmtest.IstiodReleaseName)
}
return cleanupIstio(cs, h)
}
func cleanupIstio(cs cluster.Cluster, h *helm.Helm) error {
if err := h.DeleteChart(helmtest.BaseReleaseName, helmtest.IstioNamespace); err != nil {
return fmt.Errorf("failed to delete %s release", helmtest.BaseReleaseName)
}
if err := cs.CoreV1().Namespaces().Delete(context.TODO(), helmtest.IstioNamespace, metav1.DeleteOptions{}); err != nil {
return fmt.Errorf("failed to delete istio namespace: %v", err)
}
if err := kubetest.WaitForNamespaceDeletion(cs, helmtest.IstioNamespace, retry.Timeout(helmtest.RetryTimeOut)); err != nil {
return fmt.Errorf("waiting for istio namespace to be deleted: %v", err)
}
return nil
}
// deleteIstioCanary deletes installed Istio Helm charts and resources
func deleteIstioRevision(h *helm.Helm, revision string) error {
scopes.Framework.Infof("cleaning up revision resources (%s)", revision)
name := helmtest.IstiodReleaseName + "-" + strings.ReplaceAll(revision, ".", "-")
if err := h.DeleteChart(name, helmtest.IstioNamespace); err != nil {
return fmt.Errorf("failed to delete revision (%s)", name)
}
return nil
}
// getValuesOverrides returns the values file created to pass into Helm override default values
// for the hub and tag
func getValuesOverrides(ctx framework.TestContext, hub, tag, revision string) string {
workDir := ctx.CreateTmpDirectoryOrFail("helm")
overrideValues := fmt.Sprintf(defaultValues, hub, tag, revision)
overrideValuesFile := filepath.Join(workDir, "values.yaml")
if err := os.WriteFile(overrideValuesFile, []byte(overrideValues), os.ModePerm); err != nil {
ctx.Fatalf("failed to write iop cr file: %v", err)
}
return overrideValuesFile
}
// performInPlaceUpgradeFunc returns the provided function necessary to run inside an integration test
// for upgrade capability
func performInPlaceUpgradeFunc(previousVersion string) func(framework.TestContext) {
return func(t framework.TestContext) {
cs := t.Clusters().Default().(*kubecluster.Cluster)
h := helm.New(cs.Filename())
t.ConditionalCleanup(func() {
// only need to do call this once as helm doesn't need to remove
// all versions
err := deleteIstio(cs, h, true)
if err != nil {
t.Fatalf("could not delete istio: %v", err)
}
})
overrideValuesFile := getValuesOverrides(t, gcrHub, previousVersion, "")
helmtest.InstallIstio(t, cs, h, tarGzSuffix, overrideValuesFile, helmtest.TestDataChartPath, previousVersion, true)
helmtest.VerifyInstallation(t, cs, true)
helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
"istio-sidecar-injector",
})
helmtest.ValidatingWebhookConfigurations(t, cs, []string{
"istio-validator-dubbo-system",
})
_, oldClient, oldServer := sanitycheck.SetupTrafficTest(t, t, "")
sanitycheck.RunTrafficTestClientServer(t, oldClient, oldServer)
s := t.Settings()
overrideValuesFile = getValuesOverrides(t, s.Image.Hub, s.Image.Tag, "")
upgradeCharts(t, h, overrideValuesFile)
helmtest.VerifyInstallation(t, cs, true)
helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
"istio-sidecar-injector",
})
// in-place upgrades will only have the default validator from the new version
helmtest.ValidatingWebhookConfigurations(t, cs, []string{
"istiod-dubbo-validator",
})
_, newClient, newServer := sanitycheck.SetupTrafficTest(t, t, "")
sanitycheck.RunTrafficTestClientServer(t, newClient, newServer)
// now check that we are compatible with N-1 proxy with N proxy
sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
}
}
// performRevisionUpgradeFunc returns the provided function necessary to run inside an integration test
// for upgrade capability with revisions
func performRevisionUpgradeFunc(previousVersion, previousValidatingWebhookName string, validatingWebhookCarriesOver bool) func(framework.TestContext) {
return func(t framework.TestContext) {
cs := t.Clusters().Default().(*kubecluster.Cluster)
h := helm.New(cs.Filename())
t.ConditionalCleanup(func() {
err := deleteIstioRevision(h, canaryTag)
if err != nil {
t.Fatalf("could not delete istio: %v", err)
}
err = deleteIstio(cs, h, false)
if err != nil {
t.Fatalf("could not delete istio: %v", err)
}
})
overrideValuesFile := getValuesOverrides(t, gcrHub, previousVersion, "")
helmtest.InstallIstio(t, cs, h, tarGzSuffix, overrideValuesFile, helmtest.TestDataChartPath, previousVersion, false)
helmtest.VerifyInstallation(t, cs, false)
helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
"istio-sidecar-injector",
})
helmtest.ValidatingWebhookConfigurations(t, cs, []string{previousValidatingWebhookName})
_, oldClient, oldServer := sanitycheck.SetupTrafficTest(t, t, "")
sanitycheck.RunTrafficTestClientServer(t, oldClient, oldServer)
s := t.Settings()
overrideValuesFile = getValuesOverrides(t, s.Image.Hub, s.Image.Tag, canaryTag)
helmtest.InstallIstioWithRevision(t, cs, h, "", "", canaryTag, overrideValuesFile, true, false)
helmtest.VerifyInstallation(t, cs, false)
helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
"istio-sidecar-injector",
"istio-sidecar-injector-canary",
})
validatingWebhooks := []string{
"istiod-dubbo-validator",
}
if validatingWebhookCarriesOver {
validatingWebhooks = append(validatingWebhooks, previousValidatingWebhookName)
}
helmtest.ValidatingWebhookConfigurations(t, cs, validatingWebhooks)
_, newClient, newServer := sanitycheck.SetupTrafficTest(t, t, canaryTag)
sanitycheck.RunTrafficTestClientServer(t, newClient, newServer)
// now check that we are compatible with N-1 proxy with N proxy
sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
}
}
// performRevisionTagsUpgradeFunc returns the provided function necessary to run inside an integration test
// for upgrade capability with stable label revision upgrades
func performRevisionTagsUpgradeFunc(previousVersion, previousValidatingWebhookName string, validatingWebhookCarriesOver bool) func(framework.TestContext) {
return func(t framework.TestContext) {
cs := t.Clusters().Default().(*kubecluster.Cluster)
h := helm.New(cs.Filename())
t.ConditionalCleanup(func() {
err := deleteIstioRevision(h, latestRevisionTag)
if err != nil {
t.Fatalf("could not delete istio revision (%v): %v", latestRevisionTag, err)
}
err = deleteIstioRevision(h, previousVersion)
if err != nil {
t.Fatalf("could not delete istio revision (%v): %v", previousVersion, err)
}
err = cleanupIstio(cs, h)
if err != nil {
t.Fatalf("could not cleanup istio: %v", err)
}
})
// install MAJOR.MINOR.PATCH charts with revision set to "MAJOR-MINOR-PATCH" name. For example,
// helm install istio-base ../tests/integration/helm/testdata/1.10.0/base.tar.gz --namespace dubbo-system -f values.yaml
// helm install istiod-1-10 ../tests/integration/helm/testdata/1.10.0/istio-control/istio-discovery.tar.gz -f values.yaml
previousRevision := strings.ReplaceAll(previousVersion, ".", "-")
overrideValuesFile := getValuesOverrides(t, gcrHub, previousVersion, previousRevision)
helmtest.InstallIstioWithRevision(t, cs, h, tarGzSuffix, previousVersion, previousRevision, overrideValuesFile, false, true)
helmtest.VerifyInstallation(t, cs, false)
// helm template istiod-1-10-0 ../tests/integration/helm/testdata/1.10.0/istio-control/istio-discovery.tar.gz
// -s templates/revision-tags.yaml --set revision=1-10-0 --set revisionTags={prod}
helmtest.SetRevisionTag(t, h, tarGzSuffix, previousRevision, prodTag, helmtest.TestDataChartPath, previousVersion)
helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
"istio-revision-tag-prod",
fmt.Sprintf("istio-sidecar-injector-%s", previousRevision),
})
helmtest.ValidatingWebhookConfigurations(t, cs, []string{previousValidatingWebhookName})
// setup istio.io/rev=1-10-0 for the default-1 namespace
oldNs, oldClient, oldServer := sanitycheck.SetupTrafficTest(t, t, previousRevision)
sanitycheck.RunTrafficTestClientServer(t, oldClient, oldServer)
// install the charts from this branch with revision set to "latest"
// helm upgrade istio-base ../manifests/charts/base --namespace dubbo-system -f values.yaml
// helm install istiod-latest ../manifests/charts/istio-control/istio-discovery -f values.yaml
s := t.Settings()
overrideValuesFile = getValuesOverrides(t, s.Image.Hub, s.Image.Tag, latestRevisionTag)
helmtest.InstallIstioWithRevision(t, cs, h, "", "", latestRevisionTag, overrideValuesFile, true, false)
helmtest.VerifyInstallation(t, cs, false)
// helm template istiod-latest ../manifests/charts/istio-control/istio-discovery --namespace dubbo-system
// -s templates/revision-tags.yaml --set revision=latest --set revisionTags={canary}
helmtest.SetRevisionTag(t, h, "", latestRevisionTag, canaryTag, helmtest.ManifestsChartPath, "")
helmtest.VerifyMutatingWebhookConfigurations(t, cs, []string{
"istio-revision-tag-prod",
fmt.Sprintf("istio-sidecar-injector-%v", previousRevision),
"istio-revision-tag-canary",
"istio-sidecar-injector-latest",
})
validatingWebhooks := []string{
"istiod-dubbo-validator",
}
if validatingWebhookCarriesOver {
validatingWebhooks = append(validatingWebhooks, previousValidatingWebhookName)
}
// when installing from the latest charts default validator will be installed
helmtest.ValidatingWebhookConfigurations(t, cs, validatingWebhooks)
// setup istio.io/rev=latest for the default-2 namespace
_, newClient, newServer := sanitycheck.SetupTrafficTest(t, t, latestRevisionTag)
sanitycheck.RunTrafficTestClientServer(t, newClient, newServer)
// now check that we are compatible with N-1 proxy with N proxy between a client
// in default-1 namespace and a server in the default-2 namespace, respectively
sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
// change the mutating webhook configuration to use the latest revision (istiod-latest service in dubbo-system)
// helm template istiod-latest ../manifests/charts/istio-control/istio-discovery --namespace dubbo-system
// -s templates/revision-tags.yaml --set revision=latest --set revisionTags={prod}
helmtest.SetRevisionTag(t, h, "", latestRevisionTag, prodTag, helmtest.ManifestsChartPath, "")
// change the old namespace that was pointing to the old prod (1-10-0) to point to the
// 'latest' revision by setting the `istio.io/rev=prod` label on the namespace
err := oldNs.SetLabel(istioRevLabel, prodTag)
if err != nil {
t.Fatal("could not remove istio.io/rev from old namespace")
}
err = oldClient.Restart()
if err != nil {
t.Fatal("could not restart old client")
}
err = oldServer.Restart()
if err != nil {
t.Fatal("could not restart old server")
}
// make sure the restarted pods in default-1 namespace do not use
// the previous version (check for the previousVersion in the image string)
err = checkVersion(t, oldNs.Name(), previousVersion)
if err != nil {
t.Fatalf("found a pod in namespace (%s) with the previous version: %v", oldNs.Name(), err)
}
// now check traffic still works between the proxies
sanitycheck.RunTrafficTestClientServer(t, oldClient, newServer)
}
}
func checkVersion(t framework.TestContext, namespace, version string) error {
// func NewPodFetch(a istioKube.ExtendedClient, namespace string, selectors ...string) PodFetchFunc {
fetch := kubetest.NewPodFetch(t.Clusters().Default(), namespace)
pods, err := kubetest.CheckPodsAreReady(fetch)
if err != nil {
return fmt.Errorf("failed to retrieve pods: %v", err)
}
for _, p := range pods {
for _, c := range p.Spec.Containers {
if strings.Contains(c.Image, version) {
return fmt.Errorf("expected container image to not include version %q, got %q", version, c.Image)
}
}
}
return nil
}