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 }