github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/clusterapi/capi-overrides/capi_overrides_test.go (about) 1 // Copyright (c) 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package capioverrides 5 6 import ( 7 "context" 8 "fmt" 9 "time" 10 11 . "github.com/onsi/ginkgo/v2" 12 . "github.com/onsi/gomega" 13 "github.com/verrazzano/verrazzano/pkg/bom" 14 "github.com/verrazzano/verrazzano/pkg/k8sutil" 15 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 16 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 17 capipkg "github.com/verrazzano/verrazzano/tests/e2e/pkg/clusterapi" 18 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework" 19 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 "k8s.io/client-go/dynamic" 22 ) 23 24 const ( 25 waitTimeout = 5 * time.Minute 26 pollingInterval = 10 * time.Second 27 longWaitTimeout = 20 * time.Minute 28 coreURLFmt = "https://github.com/verrazzano/cluster-api/releases/%s/core-components.yaml" 29 ocneBootstrapURLFmt = "https://github.com/verrazzano/cluster-api-provider-ocne/releases/%s/bootstrap-components.yaml" 30 ocneControlPlaneURLFmt = "https://github.com/verrazzano/cluster-api-provider-ocne/releases/%s/control-plane-components.yaml" 31 ociInfraURLFmt = "https://github.com/oracle/cluster-api-provider-oci/releases/%s/infrastructure-components.yaml" 32 33 verrazzanoCAPINamespace = "verrazzano-capi" 34 capiCMDeployment = "capi-controller-manager" 35 capiOcneBootstrapCMDeployment = "capi-ocne-bootstrap-controller-manager" 36 capiOcneControlPlaneCMDeployment = "capi-ocne-control-plane-controller-manager" 37 capiociCMDeployment = "capoci-controller-manager" 38 39 managerContainerName = "manager" 40 41 capiComponentName = "cluster-api" 42 43 // globalRegistryOverride - format string to override the registry to use for all providers 44 globalRegistryOverride = ` 45 { 46 "global": { 47 "registry": "%s" 48 } 49 } 50 ` 51 // tagOverrides - format string to override the image tags of each provider 52 tagOverrides = ` 53 { 54 "defaultProviders": { 55 "oci": { 56 "image": { 57 "tag": "%s" 58 } 59 }, 60 "ocneBootstrap": { 61 "image": { 62 "tag": "%s" 63 } 64 }, 65 "ocneControlPlane": { 66 "image": { 67 "tag": "%s" 68 } 69 }, 70 "core": { 71 "image": { 72 "tag": "%s" 73 } 74 } 75 } 76 }` 77 78 // repoOverrides - format string to override the image registry and repository tags of each provider 79 repoOverrides = ` 80 { 81 "defaultProviders": { 82 "oci": { 83 "image": { 84 "registry": "%s", 85 "repository": "%s" 86 } 87 }, 88 "ocneBootstrap": { 89 "image": { 90 "registry": "%s", 91 "repository": "%s" 92 } 93 }, 94 "ocneControlPlane": { 95 "image": { 96 "registry": "%s", 97 "repository": "%s" 98 } 99 }, 100 "core": { 101 "image": { 102 "registry": "%s", 103 "repository": "%s" 104 } 105 } 106 } 107 }` 108 109 // versionOverrides - format string to override the version of each provider 110 versionOverrides = ` 111 { 112 "defaultProviders": { 113 "oci": { 114 "version": "%s" 115 }, 116 "ocneBootstrap": { 117 "version": "%s" 118 }, 119 "ocneControlPlane": { 120 "version": "%s" 121 }, 122 "core": { 123 "version": "%s" 124 } 125 } 126 }` 127 128 // urlOverrides - format string to override the URL of each provider 129 urlOverrides = ` 130 { 131 "defaultProviders": { 132 "oci": { 133 "url": "%s" 134 }, 135 "ocneBootstrap": { 136 "url": "%s" 137 }, 138 "ocneControlPlane": { 139 "url": "%s" 140 }, 141 "core": { 142 "url": "%s" 143 } 144 } 145 }` 146 ) 147 148 var t = framework.NewTestFramework("capi_overrides") 149 150 var _ = t.Describe("Cluster API", Label("f:platform-lcm.install"), func() { 151 var dynClient dynamic.Interface 152 153 // Get dynamic client 154 Eventually(func() (dynamic.Interface, error) { 155 kubePath, err := k8sutil.GetKubeConfigLocation() 156 if err != nil { 157 return nil, err 158 } 159 dynClient, err = pkg.GetDynamicClientInCluster(kubePath) 160 return dynClient, err 161 }, waitTimeout, pollingInterval).ShouldNot(BeNil()) 162 163 // Get the components from the BOM to pick up their current versions 164 bomDoc, ociComp, ocneComp, coreComp := getComponentsFromBom() 165 Expect(bomDoc).ToNot(BeNil()) 166 Expect(ociComp).ToNot(BeNil()) 167 Expect(ocneComp).ToNot(BeNil()) 168 Expect(coreComp).ToNot(BeNil()) 169 170 t.Context("initial state", func() { 171 // GIVEN the Cluster API is installed 172 // WHEN we check the initial state of the Verrazzano install 173 // THEN we successfully find it ready 174 capipkg.WhenClusterAPIInstalledIt(t, "the VZ status is ready", func() { 175 Eventually(isStatusReady, waitTimeout, pollingInterval).Should(BeTrue()) 176 }) 177 }) 178 179 t.Context("override global registry", func() { 180 // GIVEN a CAPI environment 181 // WHEN we override the global registry 182 // THEN the overrides get successfully applied 183 capipkg.WhenClusterAPIInstalledIt(t, "and wait for deployments to use it", func() { 184 Eventually(func() error { 185 return updateClusterAPIOverrides(fmt.Sprintf(globalRegistryOverride, bomDoc.Registry)) 186 }, waitTimeout, pollingInterval).Should(BeNil()) 187 Eventually(isGenerationMet, waitTimeout, pollingInterval).Should(BeTrue(), "%s lastReconciledGeneration did not meet %v", capiComponentName) 188 Eventually(isStatusReady, waitTimeout, pollingInterval).Should(BeTrue(), "Did not reach Ready status") 189 }) 190 }) 191 192 t.Context("override image tags", func() { 193 // GIVEN a CAPI environment 194 // WHEN we override the image tags 195 // THEN the overrides get successfully applied 196 capipkg.WhenClusterAPIInstalledIt(t, "and wait for deployments to use it", func() { 197 ociImageTag := ociComp.SubComponents[0].Images[0].ImageTag 198 ocneImageTag := ocneComp.SubComponents[0].Images[0].ImageTag 199 coreImageTag := coreComp.SubComponents[0].Images[0].ImageTag 200 Eventually(func() error { 201 return updateClusterAPIOverrides(fmt.Sprintf(tagOverrides, ociImageTag, ocneImageTag, ocneImageTag, coreImageTag)) 202 }, waitTimeout, pollingInterval).Should(BeNil()) 203 Eventually(isGenerationMet, waitTimeout, pollingInterval).Should(BeTrue(), "%s lastReconciledGeneration did not meet %v", capiComponentName) 204 Eventually(isStatusReady, waitTimeout, pollingInterval).Should(BeTrue(), "Did not reach Ready status") 205 }) 206 }) 207 208 t.Context("override repository", func() { 209 // GIVEN a CAPI environment 210 // WHEN we override the registry/repository tags 211 // THEN the overrides get successfully applied 212 capipkg.WhenClusterAPIInstalledIt(t, "and wait for deployments to use it", func() { 213 ociRepo := ociComp.SubComponents[0].Repository 214 ocneRepo := ocneComp.SubComponents[0].Repository 215 coreRepo := ocneComp.SubComponents[0].Repository 216 registry := bomDoc.Registry 217 Eventually(func() error { 218 return updateClusterAPIOverrides(fmt.Sprintf(repoOverrides, registry, ociRepo, registry, ocneRepo, registry, 219 ocneRepo, registry, coreRepo)) 220 }, waitTimeout, pollingInterval).Should(BeNil()) 221 Eventually(isGenerationMet, waitTimeout, pollingInterval).Should(BeTrue(), "%s lastReconciledGeneration did not meet %v", capiComponentName) 222 Eventually(isStatusReady, waitTimeout, pollingInterval).Should(BeTrue(), "Did not reach Ready status") 223 }) 224 }) 225 226 t.Context("override oci, core, ocneBootstrap and ocneControlPlane versions from BOM", func() { 227 // GIVEN the CAPI environment is ready 228 // WHEN we override versions 229 // THEN the overrides get successfully applied 230 capipkg.WhenClusterAPIInstalledIt(t, "and wait for reconcile to complete", func() { 231 // Using the current actual versions from the BOM, these are expected to work but download 232 // from the internet instead of from the container image. 233 Eventually(func() error { 234 return updateClusterAPIOverrides(fmt.Sprintf(versionOverrides, ociComp.Version, ocneComp.Version, ocneComp.Version, coreComp.Version)) 235 }, waitTimeout, pollingInterval).Should(BeNil()) 236 Eventually(isGenerationMet, longWaitTimeout, pollingInterval).Should(BeTrue(), "%s lastReconciledGeneration did not meet %v", capiComponentName) 237 Eventually(isStatusReady, longWaitTimeout, pollingInterval).Should(BeTrue(), "Did not reach Ready status") 238 239 _, err := pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneControlPlaneCMDeployment, managerContainerName, ocneComp.Version) 240 Expect(err).ShouldNot(HaveOccurred()) 241 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneBootstrapCMDeployment, managerContainerName, ocneComp.Version) 242 Expect(err).ShouldNot(HaveOccurred()) 243 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiociCMDeployment, managerContainerName, ociComp.Version) 244 Expect(err).ShouldNot(HaveOccurred()) 245 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiCMDeployment, managerContainerName, coreComp.Version) 246 Expect(err).ShouldNot(HaveOccurred()) 247 }) 248 }) 249 250 t.Context("adhoc override oci, core, ocneBootstrap and ocneControlPlane versions", func() { 251 // GIVEN the CAPI environment is ready 252 // WHEN we override versions 253 // THEN the overrides get successfully applied 254 capipkg.WhenClusterAPIInstalledIt(t, "and wait for reconcile to complete", func() { 255 // Using the current actual versions from the BOM, these are expected to work but download 256 // from the internet instead of from the container image. 257 Eventually(func() error { 258 return updateClusterAPIOverrides(fmt.Sprintf(versionOverrides, "v0.11.0", "v1.6.1", "v1.6.1", "v1.4.2")) 259 }, waitTimeout, pollingInterval).Should(BeNil()) 260 Eventually(isGenerationMet, longWaitTimeout, pollingInterval).Should(BeTrue(), "%s lastReconciledGeneration did not meet %v", capiComponentName) 261 Eventually(isStatusReady, longWaitTimeout, pollingInterval).Should(BeTrue(), "Did not reach Ready status") 262 263 _, err := pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneControlPlaneCMDeployment, managerContainerName, "v1.6.1") 264 Expect(err).ShouldNot(HaveOccurred()) 265 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneBootstrapCMDeployment, managerContainerName, "v1.6.1") 266 Expect(err).ShouldNot(HaveOccurred()) 267 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiociCMDeployment, managerContainerName, "v0.11.0") 268 Expect(err).ShouldNot(HaveOccurred()) 269 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiCMDeployment, managerContainerName, "v1.4.2") 270 Expect(err).ShouldNot(HaveOccurred()) 271 }) 272 }) 273 274 t.Context("override URL of each provider", func() { 275 // GIVEN the CAPI environment is ready 276 // WHEN we override the URL of each provider 277 // THEN the overrides get successfully applied 278 capipkg.WhenClusterAPIInstalledIt(t, "and wait for reconcile to complete", func() { 279 // Using the current actual versions from the BOM, these are expected to work but download 280 // from the internet instead of from the container image. 281 Eventually(func() error { 282 return updateClusterAPIOverrides(fmt.Sprintf(urlOverrides, 283 fmt.Sprintf(ociInfraURLFmt, "v0.11.0"), 284 fmt.Sprintf(ocneBootstrapURLFmt, "v1.6.1"), 285 fmt.Sprintf(ocneControlPlaneURLFmt, "v1.6.1"), 286 fmt.Sprintf(coreURLFmt, "v1.4.2"))) 287 }, waitTimeout, pollingInterval).Should(BeNil()) 288 Eventually(isGenerationMet, waitTimeout, pollingInterval).Should(BeTrue(), "%s lastReconciledGeneration did not meet %v", capiComponentName) 289 Eventually(isStatusReady, waitTimeout, pollingInterval).Should(BeTrue(), "Did not reach Ready status") 290 291 _, err := pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneControlPlaneCMDeployment, managerContainerName, "v1.6.1") 292 Expect(err).ShouldNot(HaveOccurred()) 293 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneBootstrapCMDeployment, managerContainerName, "v1.6.1") 294 Expect(err).ShouldNot(HaveOccurred()) 295 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiociCMDeployment, managerContainerName, "v0.11.0") 296 Expect(err).ShouldNot(HaveOccurred()) 297 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiCMDeployment, managerContainerName, "v1.4.2") 298 Expect(err).ShouldNot(HaveOccurred()) 299 }) 300 }) 301 302 t.Context("restore VZ to default values for clusterAPI", func() { 303 // GIVEN the CAPI environment is ready 304 // WHEN we remove the overrides 305 // THEN the default values will get restored 306 capipkg.WhenClusterAPIInstalledIt(t, "and wait for reconcile to complete", func() { 307 Eventually(func() error { 308 return updateClusterAPIOverrides("") 309 }, waitTimeout, pollingInterval).Should(BeNil()) 310 Eventually(isGenerationMet, waitTimeout, pollingInterval).Should(BeTrue(), "%s lastReconciledGeneration did not meet %v", capiComponentName) 311 Eventually(isStatusReady, waitTimeout, pollingInterval).Should(BeTrue(), "Did not reach Ready status") 312 313 _, err := pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneControlPlaneCMDeployment, managerContainerName, ocneComp.Version) 314 Expect(err).ShouldNot(HaveOccurred()) 315 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiOcneBootstrapCMDeployment, managerContainerName, ocneComp.Version) 316 Expect(err).ShouldNot(HaveOccurred()) 317 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiociCMDeployment, managerContainerName, ociComp.Version) 318 Expect(err).ShouldNot(HaveOccurred()) 319 _, err = pkg.ValidateDeploymentContainerImage(verrazzanoCAPINamespace, capiCMDeployment, managerContainerName, coreComp.Version) 320 Expect(err).ShouldNot(HaveOccurred()) 321 }) 322 }) 323 }) 324 325 func isStatusReady() bool { 326 return isStatusMet(v1beta1.VzStateReady) 327 } 328 329 // isGenerationMet - Return boolean indicating if expected status is met 330 func isGenerationMet() (bool, error) { 331 // Get the VZ resource 332 vz, err := pkg.GetVerrazzanoV1beta1() 333 if err != nil { 334 return false, err 335 } 336 componentStatus, found := vz.Status.Components[capiComponentName] 337 if !found { 338 return false, fmt.Errorf("did not find status for component %s", capiComponentName) 339 } 340 t.Logs.Debugf("VZ generation: %v, %s generation: %v", vz.Generation, capiComponentName, componentStatus.LastReconciledGeneration) 341 return componentStatus.LastReconciledGeneration == vz.Generation, nil 342 } 343 344 // isStatusMet - Return boolean indicating if expected status is met 345 func isStatusMet(state v1beta1.VzStateType) bool { 346 // Get the VZ resource 347 vz, err := pkg.GetVerrazzanoV1beta1() 348 if err != nil { 349 return false 350 } 351 return vz.Status.State == state 352 } 353 354 // updateClusterAPIOverrides - Update the VZ with the set of overrides pass for clusterAPI component 355 func updateClusterAPIOverrides(overrides string) error { 356 // Get the VZ resource 357 vz, err := pkg.GetVerrazzanoV1beta1() 358 if err != nil { 359 return err 360 } 361 362 // Get the client 363 client, err := pkg.GetVerrazzanoClientset() 364 if err != nil { 365 return err 366 } 367 368 // Update the VZ with the overrides 369 if len(overrides) == 0 { 370 // Restore the VZ to default values 371 vz.Spec.Components.ClusterAPI = nil 372 } else { 373 vz.Spec.Components.ClusterAPI = &v1beta1.ClusterAPIComponent{} 374 vz.Spec.Components.ClusterAPI.InstallOverrides = v1beta1.InstallOverrides{ 375 ValueOverrides: []v1beta1.Overrides{ 376 { 377 Values: &apiextensionsv1.JSON{ 378 Raw: []byte(overrides), 379 }, 380 }, 381 }, 382 } 383 } 384 385 _, err = client.VerrazzanoV1beta1().Verrazzanos(vz.Namespace).Update(context.TODO(), vz, metav1.UpdateOptions{}) 386 return err 387 } 388 389 // getComponentsFromBom - return some components from the BOM file 390 func getComponentsFromBom() (*bom.BomDoc, *bom.BomComponent, *bom.BomComponent, *bom.BomComponent) { 391 // Get the BOM from the installed Platform Operator 392 bomDoc, err := pkg.GetBOMDoc() 393 if err != nil { 394 return nil, nil, nil, nil 395 } 396 397 // Find the Rancher and CAPI components 398 var ociComp *bom.BomComponent 399 var capiComp *bom.BomComponent 400 var coreComp *bom.BomComponent 401 for i, component := range bomDoc.Components { 402 switch component.Name { 403 case "capi-oci": 404 ociComp = &bomDoc.Components[i] 405 case "capi-ocne": 406 capiComp = &bomDoc.Components[i] 407 case "capi-cluster-api": 408 coreComp = &bomDoc.Components[i] 409 } 410 } 411 return bomDoc, ociComp, capiComp, coreComp 412 }