github.com/verrazzano/verrazzano@v1.7.0/platform-operator/controllers/secrets/cloud_credential.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 secrets 5 6 import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/rancher" 11 "go.uber.org/zap" 12 corev1 "k8s.io/api/core/v1" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 15 "k8s.io/apimachinery/pkg/runtime/schema" 16 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 17 "strings" 18 ) 19 20 const ( 21 rancherCcTenancyField = "ocicredentialConfig-tenancyId" 22 rancherCcUserField = "ocicredentialConfig-userId" 23 rancherCcFingerprintField = "ocicredentialConfig-fingerprint" 24 rancherCcRegionField = "ocicredentialConfig-region" 25 rancherCcPassphraseField = "ocicredentialConfig-passphrase" //nolint:gosec //#gosec G101 26 rancherCcKeyField = "ocicredentialConfig-privateKeyContents" 27 ociCapiTenancyField = "tenancy" 28 ociCapiUserField = "user" 29 ociCapiFingerprintField = "fingerprint" 30 ociCapiRegionField = "region" 31 ociCapiPassphraseField = "passphrase" //nolint:gosec //#gosec G101 32 ociCapiKeyField = "key" 33 ) 34 35 type rancherMgmtCluster struct { 36 Metadata struct { 37 Name string `json:"name"` 38 } `json:"metadata"` 39 Spec struct { 40 GenericEngineConfig struct { 41 CloudCredentialID string `json:"cloudCredentialId"` 42 } `json:"genericEngineConfig"` 43 } `json:"spec"` 44 } 45 46 // createOrUpdateCAPISecret updates CAPI secret based on the updated credentials 47 func (r *VerrazzanoSecretsReconciler) updateCAPISecret(updatedSecret *corev1.Secret, clusterCredential *corev1.Secret) error { 48 data := map[string][]byte{ 49 ociCapiTenancyField: updatedSecret.Data[rancherCcTenancyField], 50 ociCapiUserField: updatedSecret.Data[rancherCcUserField], 51 ociCapiFingerprintField: updatedSecret.Data[rancherCcFingerprintField], 52 ociCapiRegionField: clusterCredential.Data[ociCapiRegionField], 53 ociCapiPassphraseField: updatedSecret.Data[rancherCcPassphraseField], 54 ociCapiKeyField: updatedSecret.Data[rancherCcKeyField], 55 } 56 clusterCredential.Data = data 57 err := r.Client.Update(context.TODO(), clusterCredential) 58 if err != nil { 59 return err 60 } 61 return nil 62 } 63 64 // checkClusterCredentials checks whether the updated credential is being used by any OCNE cluster. If a cluster is using that credential, the OCNE cluster's copy of the credential also gets updated 65 func (r *VerrazzanoSecretsReconciler) checkClusterCredentials(updatedSecret *corev1.Secret) error { 66 ocneClustersList, err := r.getOCNEClustersList() 67 if err != nil { 68 zap.S().Errorf("Failed to get OCNE Cluster list") 69 return err 70 } 71 for _, cluster := range ocneClustersList.Items { 72 var rancherMgmtCluster rancherMgmtCluster 73 clusterJSON, err := cluster.MarshalJSON() 74 if err != nil { 75 return err 76 } 77 if err = json.Unmarshal(clusterJSON, &rancherMgmtCluster); err != nil { 78 return err 79 } 80 // if the cluster is an OCNE cluster 81 if rancherMgmtCluster.Spec.GenericEngineConfig.CloudCredentialID != "" { 82 // extract cloud credential name from CloudCredentialID field 83 capiCredential := strings.Split(rancherMgmtCluster.Spec.GenericEngineConfig.CloudCredentialID, ":") 84 // if cloud credential name matches updatedSecret name, get and update the cc copy held by the cluster 85 if len(capiCredential) >= 2 { 86 if capiCredential[1] == updatedSecret.Name { 87 secretName := fmt.Sprintf("%s-principal", rancherMgmtCluster.Metadata.Name) 88 clusterCredential := &corev1.Secret{ 89 ObjectMeta: metav1.ObjectMeta{ 90 Name: secretName, 91 Namespace: rancherMgmtCluster.Metadata.Name, 92 }, 93 } 94 // update cluster's cloud credential copy 95 _, err = controllerutil.CreateOrUpdate(context.TODO(), r.Client, clusterCredential, func() error { 96 r.updateCAPISecret(updatedSecret, clusterCredential) 97 return nil 98 }) 99 if err != nil { 100 zap.S().Errorf("Failed to update CAPI secret") 101 return err 102 } 103 } 104 } 105 } 106 } 107 return nil 108 } 109 110 // getOCNEClustersList returns the list of OCNE clusters 111 func (r *VerrazzanoSecretsReconciler) getOCNEClustersList() (*unstructured.UnstructuredList, error) { 112 var ocneClustersList *unstructured.UnstructuredList 113 gvr := GetOCNEClusterAPIGVRForResource("clusters") 114 ocneClustersList, err := r.DynamicClient.Resource(gvr).List(context.TODO(), metav1.ListOptions{}) 115 if err != nil { 116 return nil, fmt.Errorf("failed to list %s/%s/%s: %v", gvr.Resource, gvr.Group, gvr.Version, err) 117 } 118 return ocneClustersList, nil 119 } 120 121 // GetOCNEClusterAPIGVRForResource returns a clusters.cluster.x-k8s.io GroupVersionResource structure 122 func GetOCNEClusterAPIGVRForResource(resource string) schema.GroupVersionResource { 123 return schema.GroupVersionResource{ 124 Group: "management.cattle.io", 125 Version: "v3", 126 Resource: resource, 127 } 128 } 129 130 func isOCNECloudCredential(secret *corev1.Secret) bool { 131 // if secret is a cloud credential in the cattle-global-data ns 132 if secret.Namespace == rancher.CattleGlobalDataNamespace && secret.Data["ocicredentialConfig-fingerprint"] != nil { 133 return true 134 } 135 // secret is not cloud credential 136 return false 137 }