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 }