github.com/verrazzano/verrazzano@v1.7.0/cluster-operator/controllers/vmc/rancher.go (about) 1 // Copyright (c) 2021, 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 vmc 5 6 import ( 7 "crypto/md5" //nolint:gosec //#gosec G501 // package used for caching only, not security 8 "fmt" 9 "io" 10 "net/http" 11 12 "github.com/Jeffail/gabs/v2" 13 cons "github.com/verrazzano/verrazzano/pkg/constants" 14 "github.com/verrazzano/verrazzano/pkg/httputil" 15 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 16 "github.com/verrazzano/verrazzano/pkg/mcconstants" 17 "github.com/verrazzano/verrazzano/pkg/rancherutil" 18 "github.com/verrazzano/verrazzano/platform-operator/constants" 19 corev1 "k8s.io/api/core/v1" 20 "k8s.io/apimachinery/pkg/api/equality" 21 apierrors "k8s.io/apimachinery/pkg/api/errors" 22 "k8s.io/apimachinery/pkg/runtime/schema" 23 "k8s.io/apimachinery/pkg/util/json" 24 "k8s.io/apimachinery/pkg/util/yaml" 25 "sigs.k8s.io/controller-runtime/pkg/client" 26 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 27 ) 28 29 const ( 30 rancherNamespace = "cattle-system" 31 rancherIngressName = "rancher" 32 rancherTLSSecret = "tls-rancher-ingress" //nolint:gosec //#gosec G101 33 34 clusterPath = "/v3/cluster" 35 clustersPath = "/v3/clusters" 36 clustersByNamePath = "/v3/clusters?name=" 37 clusterRegTokenPath = "/v3/clusterregistrationtoken" //nolint:gosec //#gosec G101 38 manifestPath = "/v3/import/" 39 loginPath = "/v3-public/localProviders/local?action=login" 40 secretPathTemplate = "/api/v1/namespaces/%s/secrets/%s" //nolint:gosec //#gosec G101 41 secretCreateTemplate = "/api/v1/namespaces/%s/secrets" //nolint:gosec //#gosec G101 42 43 k8sClustersPath = "/k8s/clusters/" 44 45 rancherClusterStateActive = "active" 46 ) 47 48 type RancherCluster struct { 49 Name string 50 ID string 51 } 52 53 // RegisterManagedClusterWithRancher registers a managed cluster with Rancher and returns a chunk of YAML that 54 // must be applied on the managed cluster to complete the registration. 55 func RegisterManagedClusterWithRancher(rc *rancherutil.RancherConfig, clusterName string, rancherClusterID string, log vzlog.VerrazzanoLogger) (string, string, error) { 56 clusterID := rancherClusterID 57 var err error 58 if clusterID == "" { 59 log.Oncef("Registering managed cluster in Rancher with name: %s", clusterName) 60 clusterID, err = ImportClusterToRancher(rc, clusterName, nil, log) 61 if err != nil { 62 log.Errorf("Failed to import cluster to Rancher: %v", err) 63 return "", "", err 64 } 65 } 66 67 log.Oncef("Getting registration YAML from Rancher for cluster %s with id %s", clusterName, clusterID) 68 regYAML, err := getRegistrationYAMLFromRancher(rc, clusterID, log) 69 if err != nil { 70 log.Errorf("Failed to get registration YAML from Rancher: %v", err) 71 return "", "", err 72 } 73 74 return regYAML, clusterID, nil 75 } 76 77 // ImportClusterToRancher uses the Rancher API to import the cluster. The cluster will show as "pending" until the registration 78 // YAML is applied on the managed cluster. 79 func ImportClusterToRancher(rc *rancherutil.RancherConfig, clusterName string, labels map[string]string, log vzlog.VerrazzanoLogger) (string, error) { 80 action := http.MethodPost 81 82 payload, err := makeClusterPayload(clusterName, labels) 83 if err != nil { 84 return "", err 85 } 86 87 reqURL := rc.BaseURL + clusterPath 88 headers := map[string]string{"Content-Type": "application/json"} 89 headers["Authorization"] = "Bearer " + rc.APIAccessToken 90 91 response, responseBody, err := rancherutil.SendRequest(action, reqURL, headers, payload, rc, log) 92 93 if response != nil && response.StatusCode == http.StatusUnprocessableEntity { 94 // if we've already imported this cluster, we get an HTTP 422, so attempt to fetch the existing cluster 95 // and get the cluster ID from the response 96 log.Debugf("Cluster %s already registered with Rancher, attempting to fetch it", clusterName) 97 clusterID, err := GetClusterIDFromRancher(rc, clusterName, log) 98 if err != nil { 99 return "", err 100 } 101 return clusterID, nil 102 } 103 104 if err != nil { 105 return "", err 106 } 107 108 err = httputil.ValidateResponseCode(response, http.StatusCreated) 109 if err != nil { 110 return "", err 111 } 112 log.Oncef("Successfully registered managed cluster in Rancher with name: %s", clusterName) 113 114 return httputil.ExtractFieldFromResponseBodyOrReturnError(responseBody, "id", "unable to find cluster id in Rancher response") 115 } 116 117 // makeClusterPayload returns the payload for Rancher cluster creation, given a cluster name 118 // and labels to apply to it 119 func makeClusterPayload(clusterName string, labels map[string]string) (string, error) { 120 labelsJSONString, err := makeLabelsJSONString(labels) 121 if err != nil { 122 return "", err 123 } 124 payload := `{"type": "cluster", 125 "name":"` + clusterName + `", 126 "dockerRootDir": "/var/lib/docker", 127 "enableClusterAlerting": "false", 128 "enableClusterMonitoring": "false", 129 "enableNetworkPolicy": "false"` 130 131 if len(labelsJSONString) > 0 { 132 payload = fmt.Sprintf(`%s, "labels": %s }`, payload, labelsJSONString) 133 } else { 134 payload = fmt.Sprintf("%s}", payload) 135 } 136 return payload, nil 137 } 138 139 func makeLabelsJSONString(labels map[string]string) (string, error) { 140 if len(labels) == 0 { 141 return "", nil 142 } 143 labelsJSON, err := json.Marshal(labels) 144 if err != nil { 145 return "", err 146 } 147 return string(labelsJSON), nil 148 } 149 150 // DeleteClusterFromRancher uses the Rancher API to delete a cluster in Rancher. 151 func DeleteClusterFromRancher(rc *rancherutil.RancherConfig, clusterID string, log vzlog.VerrazzanoLogger) (bool, error) { 152 action := http.MethodDelete 153 reqURL := rc.BaseURL + clustersPath + "/" + clusterID 154 headers := map[string]string{"Authorization": "Bearer " + rc.APIAccessToken} 155 156 response, _, err := rancherutil.SendRequest(action, reqURL, headers, "", rc, log) 157 158 if response != nil && response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNotFound { 159 return false, fmt.Errorf("tried to delete cluster from Rancher but failed, response code: %d", response.StatusCode) 160 } 161 162 if err != nil { 163 return false, err 164 } 165 166 log.Oncef("Successfully deleted cluster %s from Rancher", clusterID) 167 return true, nil 168 } 169 170 // GetClusterIDFromRancher attempts to fetch the cluster from Rancher by name and pull out the cluster ID 171 func GetClusterIDFromRancher(rc *rancherutil.RancherConfig, clusterName string, log vzlog.VerrazzanoLogger) (string, error) { 172 action := http.MethodGet 173 174 reqURL := rc.BaseURL + clustersByNamePath + clusterName 175 headers := map[string]string{"Authorization": "Bearer " + rc.APIAccessToken} 176 177 response, responseBody, err := rancherutil.SendRequest(action, reqURL, headers, "", rc, log) 178 179 if response != nil && response.StatusCode != http.StatusOK { 180 return "", fmt.Errorf("tried to get cluster from Rancher but failed, response code: %d", response.StatusCode) 181 } 182 183 if err != nil { 184 return "", err 185 } 186 187 return httputil.ExtractFieldFromResponseBodyOrReturnError(responseBody, "data.0.id", "unable to find clusterId in Rancher response") 188 } 189 190 // GetAllClustersInRancher returns cluster information for every cluster registered with Rancher 191 func GetAllClustersInRancher(rc *rancherutil.RancherConfig, log vzlog.VerrazzanoLogger) ([]RancherCluster, []byte, error) { 192 reqURL := rc.BaseURL + clustersPath 193 headers := map[string]string{"Authorization": "Bearer " + rc.APIAccessToken} 194 195 hash := md5.New() //nolint:gosec //#gosec G401 196 clusters := []RancherCluster{} 197 for { 198 response, responseBody, err := rancherutil.SendRequest(http.MethodGet, reqURL, headers, "", rc, log) 199 if response != nil && response.StatusCode != http.StatusOK { 200 return nil, nil, fmt.Errorf("Unable to get clusters from Rancher, response code: %d", response.StatusCode) 201 } 202 203 if err != nil { 204 return nil, nil, err 205 } 206 207 // parse the response and iterate over the items 208 jsonString, err := gabs.ParseJSON([]byte(responseBody)) 209 if err != nil { 210 return nil, nil, err 211 } 212 213 var items []interface{} 214 var ok bool 215 if items, ok = jsonString.Path("data").Data().([]interface{}); !ok { 216 return nil, nil, fmt.Errorf("Unable to find expected data in Rancher clusters response: %v", jsonString) 217 } 218 219 for _, item := range items { 220 var i map[string]interface{} 221 var ok bool 222 if i, ok = item.(map[string]interface{}); !ok { 223 log.Infof("Expected item to be of type 'map[string]interface{}': %s", responseBody) 224 continue 225 } 226 var name, id interface{} 227 if name, ok = i["name"]; !ok { 228 log.Infof("Expected to find 'name' field in Rancher cluster data: %s", responseBody) 229 continue 230 } 231 if id, ok = i["id"]; !ok { 232 log.Infof("Expected to find 'id' field in Rancher cluster data: %s", responseBody) 233 continue 234 } 235 cluster := RancherCluster{Name: name.(string), ID: id.(string)} 236 clusters = append(clusters, cluster) 237 } 238 239 // add this response body to the hash 240 io.WriteString(hash, responseBody) 241 242 // if there is a "next page" link then use that to make another request 243 if reqURL, err = httputil.ExtractFieldFromResponseBodyOrReturnError(responseBody, "pagination.next", ""); err != nil { 244 break 245 } 246 } 247 248 // unfortunately Rancher does not support ETags, so we return a hash of the response bodies which allows the caller to know if 249 // there were any changes to the clusters 250 return clusters, hash.Sum(nil), nil 251 } 252 253 // isManagedClusterActiveInRancher returns true if the managed cluster is active 254 func isManagedClusterActiveInRancher(rc *rancherutil.RancherConfig, clusterID string, log vzlog.VerrazzanoLogger) (bool, error) { 255 reqURL := rc.BaseURL + clustersPath + "/" + clusterID 256 headers := map[string]string{"Authorization": "Bearer " + rc.APIAccessToken} 257 258 response, responseBody, err := rancherutil.SendRequest(http.MethodGet, reqURL, headers, "", rc, log) 259 260 if response != nil && response.StatusCode != http.StatusOK { 261 return false, fmt.Errorf("tried to get cluster from Rancher but failed, response code: %d", response.StatusCode) 262 } 263 264 if err != nil { 265 return false, err 266 } 267 268 state, err := httputil.ExtractFieldFromResponseBodyOrReturnError(responseBody, "state", "unable to find cluster state in Rancher response") 269 if err != nil { 270 return false, err 271 } 272 agentImage, err := httputil.ExtractFieldFromResponseBodyOrReturnError(responseBody, "agentImage", "unable to find agent image in Rancher response") 273 if err != nil { 274 return false, err 275 } 276 277 // Rancher temporarily sets the state of a new cluster to "active" before setting it to "pending", so we also check for the "agentImage" field 278 // to know that the cluster is really active 279 return state == rancherClusterStateActive && len(agentImage) > 0, nil 280 } 281 282 // getCACertFromManagedCluster attempts to get the CA cert from the managed cluster using the Rancher API proxy. It first checks for 283 // the Rancher TLS secret and if that is not found it looks for the Verrazzano system TLS secret. 284 func getCACertFromManagedCluster(rc *rancherutil.RancherConfig, clusterID string, log vzlog.VerrazzanoLogger) (string, error) { 285 // first look for the Rancher TLS secret 286 caCert, err := GetCACertFromManagedClusterSecret(rc, clusterID, rancherNamespace, cons.RancherTLSCA, cons.RancherTLSCAKey, log) 287 if err != nil { 288 return "", err 289 } 290 291 if caCert != "" { 292 return caCert, nil 293 } 294 295 // didn't find the Rancher secret so next look for the verrazzano-tls secret 296 caCert, err = GetCACertFromManagedClusterSecret(rc, clusterID, cons.VerrazzanoSystemNamespace, constants.VerrazzanoIngressSecret, mcconstants.CaCrtKey, log) 297 if err != nil { 298 return "", err 299 } 300 301 if caCert != "" { 302 return caCert, nil 303 } 304 305 return "", nil 306 } 307 308 // GetCACertFromManagedClusterSecret attempts to get the CA cert from a secret on the managed cluster using the Rancher API proxy 309 func GetCACertFromManagedClusterSecret(rc *rancherutil.RancherConfig, clusterID, namespace, secretName, secretKey string, log vzlog.VerrazzanoLogger) (string, error) { 310 const k8sAPISecretPattern = "%s/api/v1/namespaces/%s/secrets/%s" //nolint:gosec //#gosec G101 311 312 // use the Rancher API proxy on the managed cluster to fetch the secret 313 baseReqURL := rc.BaseURL + k8sClustersPath + clusterID 314 headers := map[string]string{"Authorization": "Bearer " + rc.APIAccessToken} 315 316 reqURL := fmt.Sprintf(k8sAPISecretPattern, baseReqURL, namespace, secretName) 317 response, responseBody, err := rancherutil.SendRequest(http.MethodGet, reqURL, headers, "", rc, log) 318 319 if response != nil { 320 if response.StatusCode == http.StatusNotFound { 321 return "", nil 322 } 323 if response.StatusCode != http.StatusOK { 324 return "", fmt.Errorf("tried to get managed cluster CA cert %s/%s from Rancher but failed, response code: %d", namespace, secretName, response.StatusCode) 325 } 326 } 327 if err != nil { 328 return "", err 329 } 330 331 // parse the response and pull out the secretKey value from the secret data 332 jsonString, err := gabs.ParseJSON([]byte(responseBody)) 333 if err != nil { 334 return "", err 335 } 336 337 if data, ok := jsonString.Path("data").Data().(map[string]interface{}); ok { 338 if caCert, ok := data[secretKey].(string); ok { 339 return caCert, nil 340 } 341 } 342 343 return "", nil 344 } 345 346 // isNamespaceCreated attempts to ascertain whether the given namesapce is created in the cluster 347 func isNamespaceCreated(client client.Client, ingressHost, clusterID, namespace string, log vzlog.VerrazzanoLogger) (bool, error) { 348 const k8sAPISecretPattern = "%s/api/v1/namespaces/%s" //nolint:gosec //#gosec G101 349 350 rc, err := rancherutil.NewAdminRancherConfig(client, ingressHost, log) 351 if err != nil || rc == nil { 352 return false, err 353 } 354 355 // use the Rancher API proxy on the managed cluster to fetch the secret 356 baseReqURL := rc.BaseURL + k8sClustersPath + clusterID 357 headers := map[string]string{"Authorization": "Bearer " + rc.APIAccessToken} 358 359 reqURL := fmt.Sprintf(k8sAPISecretPattern, baseReqURL, namespace) 360 response, _, err := rancherutil.SendRequest(http.MethodGet, reqURL, headers, "", rc, log) 361 362 if response != nil { 363 if response.StatusCode == http.StatusNotFound { 364 return false, nil 365 } 366 if response.StatusCode != http.StatusOK { 367 return false, fmt.Errorf("tried to get namespace %s via Rancher from clsuter %s but failed, response code: %d", namespace, clusterID, response.StatusCode) 368 } 369 } 370 if err != nil { 371 return false, err 372 } 373 374 return true, nil 375 } 376 377 // getRegistrationYAMLFromRancher creates a registration token in Rancher for the managed cluster and uses the 378 // returned token to fetch the registration (manifest) YAML. 379 func getRegistrationYAMLFromRancher(rc *rancherutil.RancherConfig, rancherClusterID string, log vzlog.VerrazzanoLogger) (string, error) { 380 headers := map[string]string{"Content-Type": "application/json"} 381 headers["Authorization"] = "Bearer " + rc.APIAccessToken 382 383 var token string 384 token, err := getRegistrationTokenFromRancher(rc, rancherClusterID, log) 385 if err != nil { 386 log.Oncef("Unable to fetch existing cluster registration token for cluster: %s with reason: %v, continuing to create a new token", rancherClusterID, err) 387 } 388 389 if token == "" { 390 action := http.MethodPost 391 payload := `{"type": "clusterRegistrationToken", "clusterId": "` + rancherClusterID + `"}` 392 reqURL := rc.BaseURL + clusterRegTokenPath 393 394 log.Infof("Creating cluster registration token for cluster %s", rancherClusterID) 395 response, manifestContent, err := rancherutil.SendRequest(action, reqURL, headers, payload, rc, log) 396 if err != nil { 397 return "", err 398 } 399 400 err = httputil.ValidateResponseCode(response, http.StatusCreated) 401 if err != nil { 402 return "", err 403 } 404 405 // get the manifest token from the response, construct a URL, and fetch its contents 406 token, err = httputil.ExtractFieldFromResponseBodyOrReturnError(manifestContent, "token", "unable to find manifest token in Rancher response") 407 if err != nil { 408 return "", err 409 } 410 } 411 // Rancher 2.5.x added the cluster ID to the manifest URL. 412 manifestURL := rc.BaseURL + manifestPath + token + "_" + rancherClusterID + ".yaml" 413 414 action := http.MethodGet 415 response, manifestContent, err := rancherutil.SendRequest(action, manifestURL, headers, "", rc, log) 416 417 if err != nil { 418 return "", err 419 } 420 421 err = httputil.ValidateResponseCode(response, http.StatusOK) 422 if err != nil { 423 return "", err 424 } 425 426 return manifestContent, nil 427 } 428 429 type ClusterRegistrationTokens struct { 430 ClusterID string `json:"clusterId"` 431 State string `json:"state"` 432 Token string `json:"token"` 433 } 434 435 func getRegistrationTokenFromRancher(rc *rancherutil.RancherConfig, rancherClusterID string, log vzlog.VerrazzanoLogger) (string, error) { 436 437 action := http.MethodGet 438 reqURL := rc.BaseURL + clusterRegTokenPath + "?state=active&clusterId=" + rancherClusterID 439 headers := map[string]string{"Content-Type": "application/json"} 440 headers["Authorization"] = "Bearer " + rc.APIAccessToken 441 442 response, manifestContent, err := rancherutil.SendRequest(action, reqURL, headers, "{}", rc, log) 443 if err != nil { 444 return "", err 445 } 446 447 err = httputil.ValidateResponseCode(response, http.StatusOK) 448 if err != nil { 449 return "", err 450 } 451 452 data, err := httputil.ExtractFieldFromResponseBodyOrReturnError(manifestContent, "data", "unable to find data token in Rancher response") 453 if err != nil { 454 return "", err 455 } 456 457 var items []ClusterRegistrationTokens 458 json.Unmarshal([]byte(data), &items) 459 for _, item := range items { 460 if item.ClusterID == rancherClusterID && item.State == "active" { 461 log.Oncef("ClusterRegistrationToken exists for the cluster %s", rancherClusterID) 462 return item.Token, nil 463 } 464 } 465 466 log.Oncef("No existing ClusterRegistrationToken found for cluster %s", rancherClusterID) 467 return "", nil 468 } 469 470 // createOrUpdateSecretRancherProxy simulates the controllerutil create or update function through the Rancher Proxy API for secrets 471 func createOrUpdateSecretRancherProxy(secret *corev1.Secret, rc *rancherutil.RancherConfig, clusterID string, f controllerutil.MutateFn, log vzlog.VerrazzanoLogger) (controllerutil.OperationResult, error) { 472 log.Debugf("Creating or Updating Secret %s/%s", secret.GetNamespace(), secret.GetName()) 473 if err := rancherSecretGet(secret, rc, clusterID, log); err != nil { 474 if !apierrors.IsNotFound(err) { 475 return controllerutil.OperationResultNone, err 476 } 477 if err := rancherSecretMutate(f, secret, log); err != nil { 478 return controllerutil.OperationResultNone, err 479 } 480 if err := rancherSecretCreate(secret, rc, clusterID, log); err != nil { 481 return controllerutil.OperationResultNone, err 482 } 483 return controllerutil.OperationResultCreated, nil 484 } 485 486 existingSec := secret.DeepCopyObject() 487 if err := rancherSecretMutate(f, secret, log); err != nil { 488 return controllerutil.OperationResultNone, err 489 } 490 if equality.Semantic.DeepEqual(existingSec, secret) { 491 return controllerutil.OperationResultNone, nil 492 } 493 if err := rancherSecretUpdate(secret, rc, clusterID, log); err != nil { 494 return controllerutil.OperationResultNone, err 495 } 496 return controllerutil.OperationResultUpdated, nil 497 } 498 499 // rancherSecretMutate mutates the rancher secret from the given Mutate function 500 func rancherSecretMutate(f controllerutil.MutateFn, secret *corev1.Secret, log vzlog.VerrazzanoLogger) error { 501 key := client.ObjectKeyFromObject(secret) 502 if err := f(); err != nil { 503 return err 504 } 505 if newKey := client.ObjectKeyFromObject(secret); key != newKey { 506 return log.ErrorfNewErr("MutateFn cannot mutate secret name and/or secret namespace") 507 } 508 return nil 509 } 510 511 // rancherSecretGet simulates a client get request through the Rancher proxy for secrets 512 func rancherSecretGet(secret *corev1.Secret, rc *rancherutil.RancherConfig, clusterID string, log vzlog.VerrazzanoLogger) error { 513 if secret == nil { 514 return log.ErrorNewErr("Failed to get secret, nil value passed to get request") 515 } 516 reqURL := constructSecretURL(secret, rc.Host, clusterID, false) 517 headers := map[string]string{"Authorization": "Bearer " + rc.APIAccessToken} 518 resp, body, err := rancherutil.SendRequest(http.MethodGet, reqURL, headers, "", rc, log) 519 if err != nil && (resp == nil || resp.StatusCode != 404) { 520 return err 521 } 522 if resp == nil { 523 return log.ErrorfNewErr("Failed to find response from GET request %s", secret.GetNamespace(), secret.GetName(), reqURL) 524 } 525 if resp.StatusCode == http.StatusNotFound { 526 return apierrors.NewNotFound(schema.ParseGroupResource("Secret"), secret.GetName()) 527 } 528 if resp.StatusCode != http.StatusOK { 529 return log.ErrorfNewErr("Failed to get secret %s/%s from GET request %s with code %d", secret.GetNamespace(), secret.GetName(), reqURL, resp.StatusCode) 530 } 531 532 // Unmarshall the response body into the secret object, simulating a typical Get request 533 err = yaml.Unmarshal([]byte(body), secret) 534 if err != nil { 535 return log.ErrorfNewErr("Failed to unmarshall response body into secret %s/%s from GET request %s: %v", secret.GetNamespace(), secret.GetName(), reqURL, err) 536 } 537 return nil 538 } 539 540 // rancherSecretCreate simulates a client create request through the Rancher proxy for secrets 541 func rancherSecretCreate(secret *corev1.Secret, rc *rancherutil.RancherConfig, clusterID string, log vzlog.VerrazzanoLogger) error { 542 if secret == nil { 543 return log.ErrorNewErr("Failed to create secret, nil value passed to create request") 544 } 545 reqURL := constructSecretURL(secret, rc.Host, clusterID, true) 546 payload, err := json.Marshal(secret) 547 if err != nil { 548 return log.ErrorfNewErr("Failed to marshall secret %s/%s: %v", secret.GetNamespace(), secret.GetName(), err) 549 } 550 headers := map[string]string{ 551 "Authorization": "Bearer " + rc.APIAccessToken, 552 "Content-Type": "application/json", 553 } 554 resp, _, err := rancherutil.SendRequest(http.MethodPost, reqURL, headers, string(payload), rc, log) 555 if err != nil { 556 return err 557 } 558 559 if resp == nil { 560 return log.ErrorfNewErr("Failed to find response from POST request %s", secret.GetNamespace(), secret.GetName(), reqURL) 561 } 562 if resp.StatusCode != http.StatusCreated { 563 return log.ErrorfNewErr("Failed to create secret %s/%s from POST request %s with code %d", secret.GetNamespace(), secret.GetName(), reqURL, resp.StatusCode) 564 } 565 return nil 566 } 567 568 // rancherSecretUpdate simulates a client update request through the Rancher proxy for secrets 569 func rancherSecretUpdate(secret *corev1.Secret, rc *rancherutil.RancherConfig, clusterID string, log vzlog.VerrazzanoLogger) error { 570 if secret == nil { 571 return log.ErrorNewErr("Failed to update secret, nil value passed to update request") 572 } 573 reqURL := constructSecretURL(secret, rc.Host, clusterID, false) 574 payload, err := json.Marshal(secret) 575 if err != nil { 576 return log.ErrorfNewErr("Failed to marshall secret %s/%s: %v", secret.GetNamespace(), secret.GetName(), err) 577 } 578 headers := map[string]string{ 579 "Authorization": "Bearer " + rc.APIAccessToken, 580 "Content-Type": "application/json", 581 } 582 resp, _, err := rancherutil.SendRequest(http.MethodPut, reqURL, headers, string(payload), rc, log) 583 if err != nil { 584 return err 585 } 586 587 if resp == nil { 588 return log.ErrorfNewErr("Failed to find response from PUT request %s", secret.GetNamespace(), secret.GetName(), reqURL) 589 } 590 if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { 591 return log.ErrorfNewErr("Failed to create secret %s/%s from PUT request %s with code %d", secret.GetNamespace(), secret.GetName(), reqURL, resp.StatusCode) 592 } 593 return nil 594 } 595 596 // constructSecretURL returns a formatted url string from path requirements and objects 597 func constructSecretURL(secret *corev1.Secret, host, clusterID string, create bool) string { 598 if create { 599 return "https://" + host + k8sClustersPath + clusterID + fmt.Sprintf(secretCreateTemplate, secret.GetNamespace()) 600 } 601 return "https://" + host + k8sClustersPath + clusterID + fmt.Sprintf(secretPathTemplate, secret.GetNamespace(), secret.GetName()) 602 }