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  }