github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/clusterapi/ocne-driver/environment.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 ocnedriver
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/Masterminds/semver/v3"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/hashicorp/go-retryablehttp"
    13  	"github.com/verrazzano/verrazzano/tests/e2e/backup/helpers"
    14  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    15  	"go.uber.org/zap"
    16  	"gopkg.in/yaml.v3"
    17  )
    18  
    19  var (
    20  	// Initialized by ensureOCNEDriverVarsInitialized, required environment variables
    21  	region                string
    22  	vcnID                 string
    23  	userID                string
    24  	tenancyID             string
    25  	fingerprint           string
    26  	privateKeyPath        string
    27  	nodePublicKeyPath     string
    28  	compartmentID         string
    29  	workerNodeSubnet      string
    30  	controlPlaneSubnet    string
    31  	loadBalancerSubnet    string
    32  	ocneClusterNameSuffix string
    33  
    34  	// Initialized by ensureOCNEDriverVarsInitialized, optional overrides
    35  	dockerRootDir           string
    36  	enableClusterAlerting   bool
    37  	enableClusterMonitoring bool
    38  	enableNetworkPolicy     bool
    39  	windowsPreferedCluster  bool
    40  	clusterCidr             string
    41  	controlPlaneMemoryGbs   int
    42  	controlPlaneOcpus       int
    43  	controlPlaneShape       string
    44  	controlPlaneVolumeGbs   int
    45  	corednsImageTag         string
    46  	etcdImageTag            string
    47  	imageDisplayName        string
    48  	imageID                 string
    49  	installCalico           bool
    50  	installCcm              bool
    51  	installVerrazzano       bool
    52  	kubernetesVersion       string
    53  	numControlPlaneNodes    int
    54  	ocneVersion             string
    55  	podCidr                 string
    56  	privateRegistry         string
    57  	proxyEndpoint           string
    58  	skipOcneInstall         bool
    59  	tigeraImageTag          string
    60  	useNodePvEncryption     bool
    61  	verrazzanoResource      string
    62  	verrazzanoTag           string
    63  	verrazzanoVersion       string
    64  	nodeShape               string
    65  	numWorkerNodes          int
    66  	applyYAMLs              string
    67  
    68  	// Initialized during before suite, and used across helper functions
    69  	rancherURL        string
    70  	httpClient        *retryablehttp.Client
    71  	cloudCredentialID string
    72  
    73  	ocneMetadataItemToInstall OCNEMetadataItem
    74  	ocneMetadataItemToUpgrade OCNEMetadataItem
    75  )
    76  
    77  // Verify required Environment Variables are set
    78  func verifyRequiredEnvironmentVariables() {
    79  	region = pkg.GetRequiredEnvVarOrFail("OCI_REGION")
    80  	userID = pkg.GetRequiredEnvVarOrFail("OCI_USER_ID")
    81  	tenancyID = pkg.GetRequiredEnvVarOrFail("OCI_TENANCY_ID")
    82  	fingerprint = pkg.GetRequiredEnvVarOrFail("OCI_CREDENTIALS_FINGERPRINT")
    83  	privateKeyPath = pkg.GetRequiredEnvVarOrFail("OCI_PRIVATE_KEY_PATH")
    84  	ocneClusterNameSuffix = pkg.GetRequiredEnvVarOrFail("OCNE_CLUSTER_NAME_SUFFIX")
    85  	vcnID = pkg.GetRequiredEnvVarOrFail("OCI_VCN_ID")
    86  	nodePublicKeyPath = pkg.GetRequiredEnvVarOrFail("NODE_PUBLIC_KEY_PATH")
    87  	compartmentID = pkg.GetRequiredEnvVarOrFail("OCI_COMPARTMENT_ID")
    88  	workerNodeSubnet = pkg.GetRequiredEnvVarOrFail("WORKER_NODE_SUBNET")
    89  	controlPlaneSubnet = pkg.GetRequiredEnvVarOrFail("CONTROL_PLANE_SUBNET")
    90  	loadBalancerSubnet = pkg.GetRequiredEnvVarOrFail("LOAD_BALANCER_SUBNET")
    91  }
    92  
    93  // Grabs info from optional environment variables.
    94  // Requires an existing cloud credential.
    95  func ensureOCNEDriverVarsInitialized(log *zap.SugaredLogger) error {
    96  	// optional overrides
    97  	dockerRootDir = pkg.GetEnvFallback("DOCKER_ROOT_DIR", "/var/lib/docker")
    98  	enableClusterAlerting = pkg.GetEnvFallbackBool("ENABLE_CLUSTER_ALERTING", false)
    99  	enableClusterMonitoring = pkg.GetEnvFallbackBool("ENABLE_CLUSTER_MONITORING", false)
   100  	enableNetworkPolicy = pkg.GetEnvFallbackBool("ENABLE_NETWORK_POLICY", false)
   101  	windowsPreferedCluster = pkg.GetEnvFallbackBool("WINDOWS_PREFERRED_CLUSTER", false)
   102  	clusterCidr = pkg.GetEnvFallback("CLUSTER_CIDR", "10.96.0.0/16")
   103  	controlPlaneMemoryGbs = pkg.GetEnvFallbackInt("CONTROL_PLANE_MEMORY_GBS", 16)
   104  	controlPlaneOcpus = pkg.GetEnvFallbackInt("CONTROL_PLANE_OCPUS", 2)
   105  	controlPlaneVolumeGbs = pkg.GetEnvFallbackInt("CONTROL_PLANE_VOLUME_GBS", 100)
   106  	imageID = pkg.GetEnvFallback("IMAGE_ID", "")
   107  	installCalico = pkg.GetEnvFallbackBool("INSTALL_CALICO", true)
   108  	installCcm = pkg.GetEnvFallbackBool("INSTALL_CCM", true)
   109  	installVerrazzano = pkg.GetEnvFallbackBool("INSTALL_VERRAZZANO", false)
   110  	numControlPlaneNodes = pkg.GetEnvFallbackInt("NUM_CONTROL_PLANE_NODES", 1)
   111  	podCidr = pkg.GetEnvFallback("POD_CIDR", "10.244.0.0/16")
   112  	privateRegistry = pkg.GetEnvFallback("PRIVATE_REGISTRY", "")
   113  	proxyEndpoint = pkg.GetEnvFallback("PROXY_ENDPOINT", "")
   114  	skipOcneInstall = pkg.GetEnvFallbackBool("SKIP_OCNE_INSTALL", false)
   115  	useNodePvEncryption = pkg.GetEnvFallbackBool("USE_NODE_PV_ENCRYPTION", true)
   116  	verrazzanoResource = pkg.GetEnvFallback("VERRAZZANO_RESOURCE", `apiVersion: install.verrazzano.io/v1beta1
   117  kind: Verrazzano
   118  metadata:
   119    name: managed
   120    namespace: default
   121  spec:
   122    profile: managed-cluster
   123    components:
   124  	ingressNGINX:
   125  	  overrides:
   126  		- values:
   127  			controller:
   128  			  service:
   129  				annotations:
   130  				  service.beta.kubernetes.io/oci-load-balancer-shape : "flexible"
   131  				  service.beta.kubernetes.io/oci-load-balancer-shape-flex-min: "10"
   132  				  service.beta.kubernetes.io/oci-load-balancer-shape-flex-max: "100"
   133  	  type: LoadBalancer
   134  	istio:
   135  	  overrides:
   136  		- values:
   137  			apiVersion: install.istio.io/v1alpha1
   138  			kind: IstioOperator
   139  			spec:
   140  			  values:
   141  				gateways:
   142  				  istio-ingressgateway:
   143  					serviceAnnotations:
   144  					  service.beta.kubernetes.io/oci-load-balancer-shape: "flexible"
   145  					  service.beta.kubernetes.io/oci-load-balancer-shape-flex-min: "10"
   146  					  service.beta.kubernetes.io/oci-load-balancer-shape-flex-max: "100"`)
   147  	numWorkerNodes = pkg.GetEnvFallbackInt("NUM_WORKER_NODES", 1)
   148  	applyYAMLs = pkg.GetEnvFallback("APPLY_YAMLS", "")
   149  	if err := fillOCNEMetadata(log); err != nil {
   150  		return err
   151  	}
   152  	if err := fillOCNEVersion(log); err != nil {
   153  		return err
   154  	}
   155  	if err := fillNodeImage(log); err != nil {
   156  		return err
   157  	}
   158  	if err := fillVerrazzanoVersions(log); err != nil {
   159  		return err
   160  	}
   161  	if err := fillNodeShapes(log); err != nil {
   162  		return err
   163  	}
   164  	return nil
   165  }
   166  
   167  // Initializes variables from OCNE metadata ConfigMap. Values are optionally overridden.
   168  func fillOCNEMetadata(log *zap.SugaredLogger) error {
   169  	var coreDNSFallback, etcdFallback, tigeraFallback, kubernetesFallback string
   170  
   171  	// Use ocne-metadata configmap to get fallback values
   172  	const ocneMetadataCMName = "ocne-metadata"
   173  	cm, err := pkg.GetConfigMap(ocneMetadataCMName, "verrazzano-capi")
   174  	if err != nil {
   175  		log.Errorf("error getting %s ConfigMap: %s", ocneMetadataCMName, err)
   176  		return err
   177  	}
   178  	if cm == nil {
   179  		err = fmt.Errorf("%s ConfigMap not found", ocneMetadataCMName)
   180  		log.Error(err)
   181  		return err
   182  	}
   183  	// Unmarshal dataYaml into a map,
   184  	// since the first and only top-level field's key is an unknown Kubernetes version
   185  	dataYaml := cm.Data["mapping"]
   186  	var mapToContents map[string]interface{}
   187  	if err = yaml.Unmarshal([]byte(dataYaml), &mapToContents); err != nil {
   188  		log.Errorf("yaml unmarshalling error: %s", err)
   189  		return err
   190  	}
   191  	if len(mapToContents) < 1 {
   192  		err = fmt.Errorf("data inside %s ConfigMap not formatted as expcted", ocneMetadataCMName)
   193  		log.Error(err)
   194  		return err
   195  	}
   196  
   197  	for k8sVersion, contents := range mapToContents {
   198  		// Retrieve the Kubernetes version
   199  		kubernetesFallback = k8sVersion
   200  
   201  		// Convert the inner contents to a Golang struct for easier access
   202  		var contentStruct OCNEMetadataContents
   203  		contentBytes, err := yaml.Marshal(contents)
   204  		if err != nil {
   205  			log.Errorf("yaml marshalling error: %s", err)
   206  			return err
   207  		}
   208  		if err = yaml.Unmarshal(contentBytes, &contentStruct); err != nil {
   209  			log.Errorf("yaml unmarshalling error: %s", err)
   210  			return err
   211  		}
   212  		coreDNSFallback = contentStruct.ContainerImages.Coredns
   213  		etcdFallback = contentStruct.ContainerImages.Etcd
   214  		tigeraFallback = contentStruct.ContainerImages.TigeraOperator
   215  
   216  		k8sSemVerFallback, err := semver.NewVersion(k8sVersion)
   217  		if err != nil {
   218  			log.Errorf("kubernetes version parsing error: %s", err)
   219  			return err
   220  		}
   221  		// finding the minimum kubernetes version to install a OCNE cluster
   222  		if ocneMetadataItemToInstall.KubernetesVersion == nil || k8sSemVerFallback.LessThan(ocneMetadataItemToInstall.KubernetesVersion) {
   223  			ocneMetadataItemToInstall = OCNEMetadataItem{KubernetesVersion: k8sSemVerFallback, OCNEMetadataContents: contentStruct}
   224  		}
   225  		// finding the maximum kubernetes version to update the OCNE cluster
   226  		if ocneMetadataItemToUpgrade.KubernetesVersion == nil || k8sSemVerFallback.GreaterThan(ocneMetadataItemToUpgrade.KubernetesVersion) {
   227  			ocneMetadataItemToUpgrade = OCNEMetadataItem{KubernetesVersion: k8sSemVerFallback, OCNEMetadataContents: contentStruct}
   228  		}
   229  	}
   230  
   231  	// Initialize values
   232  	corednsImageTag = pkg.GetEnvFallback("CORE_DNS_IMAGE_TAG", coreDNSFallback)
   233  	etcdImageTag = pkg.GetEnvFallback("ETCD_IMAGE_TAG", etcdFallback)
   234  	tigeraImageTag = pkg.GetEnvFallback("TIGERA_IMAGE_TAG", tigeraFallback)
   235  	kubernetesVersion = pkg.GetEnvFallback("KUBERNETES_VERSION", kubernetesFallback)
   236  	return nil
   237  }
   238  
   239  // Initializes OCNE Version, optionally overridden.
   240  func fillOCNEVersion(log *zap.SugaredLogger) error {
   241  	var ocneVersionFallback string
   242  
   243  	// Use Rancher API call to get fallback value
   244  	requestURL, adminToken := setupRequest(rancherURL, "/meta/ocne/ocneVersions", log)
   245  	response, err := helpers.HTTPHelper(httpClient, "GET", requestURL, adminToken, "Bearer", http.StatusOK, nil, log)
   246  	if err != nil {
   247  		log.Errorf("error requesting OCNE version from Rancher: %s", err)
   248  		return err
   249  	}
   250  	versionList := response.Children()
   251  	if len(versionList) < 1 {
   252  		err = fmt.Errorf("response to OCNE versions request does not have expected length")
   253  		log.Error(err)
   254  		return err
   255  	}
   256  	ocneVersionFallback = versionList[0].Data().(string)
   257  
   258  	// Initialize value
   259  	ocneVersion = pkg.GetEnvFallback("OCNE_VERSION", ocneVersionFallback)
   260  	return nil
   261  }
   262  
   263  // Initializes the node image, optionally overridden
   264  func fillNodeImage(log *zap.SugaredLogger) error {
   265  	var linuxImageFallback string
   266  
   267  	// Use Rancher API call to get fallback value
   268  	requestURL, adminToken := setupRequest(rancherURL, fmt.Sprintf("/meta/oci/nodeImages?cloudCredentialId=%s", cloudCredentialID), log)
   269  	response, err := helpers.HTTPHelper(httpClient, "GET", requestURL, adminToken, "Bearer", http.StatusOK, nil, log)
   270  	if err != nil {
   271  		log.Errorf("error requesting node images from Rancher: %s", err)
   272  		return err
   273  	}
   274  	imageList := response.Children()
   275  	for _, image := range imageList {
   276  		imageString := image.Data().(string)
   277  		// filter a suitable OL 8 image, same as what the rancher UI does
   278  		if strings.HasPrefix(imageString, "Oracle-Linux-8") && !strings.Contains(imageString, "aarch64") {
   279  			linuxImageFallback = imageString
   280  			break
   281  		}
   282  	}
   283  	if linuxImageFallback == "" {
   284  		err = fmt.Errorf("could not find a suitable node image")
   285  		log.Error(err)
   286  		return err
   287  	}
   288  
   289  	// Initialize value
   290  	imageDisplayName = pkg.GetEnvFallback("IMAGE_DISPLAY_NAME", linuxImageFallback)
   291  	return nil
   292  }
   293  
   294  // Initializes the VZ version and tag for the created OCNE clusters, optionally overridden.
   295  func fillVerrazzanoVersions(log *zap.SugaredLogger) error {
   296  	var vzTagFallback, vzVersionFallback string
   297  
   298  	// Use Rancher API call to get fallback values
   299  	requestURL, adminToken := setupRequest(rancherURL, "/meta/ocne/verrazzanoVersions", log)
   300  	response, err := helpers.HTTPHelper(httpClient, "GET", requestURL, adminToken, "Bearer", http.StatusOK, nil, log)
   301  	if err != nil {
   302  		log.Errorf("error requesting Verrazzano versions from Rancher: %s", err)
   303  		return err
   304  	}
   305  	responseMap := response.ChildrenMap()
   306  	if len(responseMap) < 1 {
   307  		err = fmt.Errorf("response to Verrazzano versions request does not have expected length")
   308  		log.Error(err)
   309  		return err
   310  	}
   311  	for version, tag := range responseMap {
   312  		vzTagFallback = tag.Data().(string)
   313  		vzVersionFallback = version
   314  	}
   315  
   316  	// Initialize values
   317  	verrazzanoTag = pkg.GetEnvFallback("VERRAZZANO_TAG", vzTagFallback)
   318  	verrazzanoVersion = pkg.GetEnvFallback("VERRAZZANO_VERSION", vzVersionFallback)
   319  	return nil
   320  }
   321  
   322  // Initializes the control plane and worker node shapes, optionally overridden.
   323  func fillNodeShapes(log *zap.SugaredLogger) error {
   324  	var cpShapeFallback, nodeShapeFallback string
   325  
   326  	// Use Rancher API call to get fallback values
   327  	requestURL, adminToken := setupRequest(rancherURL, fmt.Sprintf("/meta/oci/nodeShapes?cloudCredentialId=%s", cloudCredentialID), log)
   328  	response, err := helpers.HTTPHelper(httpClient, "GET", requestURL, adminToken, "Bearer", http.StatusOK, nil, log)
   329  	if err != nil {
   330  		log.Errorf("error requesting node shapes from Rancher: %s", err)
   331  		return err
   332  	}
   333  	shapeList := response.Children()
   334  	if len(shapeList) == 0 {
   335  		err = fmt.Errorf("request for node shapes to Rancher API returned an empty list")
   336  		log.Error(err)
   337  		return err
   338  	}
   339  	// If the list contains "VZ.Standard.E4.Flex", default to that, similar to the Rancher UI.
   340  	// Otherwise, use the first image in the list.
   341  	cpShapeFallback = shapeList[0].Data().(string)
   342  	nodeShapeFallback = shapeList[0].Data().(string)
   343  	for _, shape := range shapeList {
   344  		shapeString := shape.Data().(string)
   345  		if shapeString == "VM.Standard.E4.Flex" {
   346  			cpShapeFallback = shapeString
   347  			nodeShapeFallback = shapeString
   348  			break
   349  		}
   350  	}
   351  
   352  	// Initialize values
   353  	controlPlaneShape = pkg.GetEnvFallback("CONTROL_PLANE_SHAPE", cpShapeFallback)
   354  	nodeShape = pkg.GetEnvFallback("NODE_SHAPE", nodeShapeFallback)
   355  	return nil
   356  }