github.com/verrazzano/verrazzano@v1.7.0/cluster-operator/controllers/capi/verrazzano.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 capi 5 6 import ( 7 "context" 8 "fmt" 9 clustersv1alpha1 "github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1" 10 "github.com/verrazzano/verrazzano/cluster-operator/controllers/rancher" 11 "github.com/verrazzano/verrazzano/pkg/constants" 12 vzctrl "github.com/verrazzano/verrazzano/pkg/controller" 13 "github.com/verrazzano/verrazzano/pkg/k8sutil" 14 "go.uber.org/zap" 15 v1 "k8s.io/api/core/v1" 16 "k8s.io/apimachinery/pkg/api/errors" 17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 19 "k8s.io/apimachinery/pkg/types" 20 ctrl "sigs.k8s.io/controller-runtime" 21 "sigs.k8s.io/controller-runtime/pkg/client" 22 ) 23 24 type VerrazzanoRegistration struct { 25 client.Client 26 Log *zap.SugaredLogger 27 } 28 29 type VerrazzanoReconcileFnType func(ctx context.Context, cluster *unstructured.Unstructured, r *CAPIClusterReconciler) (ctrl.Result, error) 30 31 var verrazzanoReconcileFn VerrazzanoReconcileFnType = doVerrazzanoReconcile 32 33 func SetVerrazzanoReconcileFunction(f VerrazzanoReconcileFnType) { 34 verrazzanoReconcileFn = f 35 } 36 37 func SetDefaultVerrazzanoReconcileFunction() { 38 verrazzanoReconcileFn = doVerrazzanoReconcile 39 } 40 41 // doVerrazzanoReconcile performs the reconciliation of the CAPI cluster to register it with Verrazzano 42 func doVerrazzanoReconcile(ctx context.Context, cluster *unstructured.Unstructured, r *CAPIClusterReconciler) (ctrl.Result, error) { 43 v := r.VerrazzanoRegistrar 44 v.Log.Debugf("Registering cluster %s with Verrazzano", cluster.GetName()) 45 46 // register the cluster if Verrazzano installed on workload cluster 47 workloadClient, err := getWorkloadClusterClient(v.Client, v.Log, cluster) 48 if err != nil { 49 v.Log.Errorf("Error getting workload cluster %s client: %v", cluster.GetName(), err) 50 return ctrl.Result{}, err 51 } 52 53 // ensure Verrazzano is installed and ready in workload cluster 54 ready, err := v.isVerrazzanoReady(ctx, workloadClient) 55 if err != nil { 56 return ctrl.Result{}, err 57 } 58 if !ready { 59 v.Log.Infof("Verrazzano not installed or not ready in cluster %s", cluster.GetName()) 60 return vzctrl.LongRequeue(), nil 61 } 62 63 vmc := &clustersv1alpha1.VerrazzanoManagedCluster{ 64 ObjectMeta: metav1.ObjectMeta{ 65 Name: cluster.GetName(), 66 Namespace: constants.VerrazzanoMultiClusterNamespace, 67 }, 68 } 69 // if verrazzano-tls-ca exists, the cluster is untrusted 70 err = workloadClient.Get(ctx, types.NamespacedName{Name: constants.PrivateCABundle, 71 Namespace: constants.VerrazzanoSystemNamespace}, &v1.Secret{}) 72 if err != nil { 73 if errors.IsNotFound(err) { 74 // cluster is trusted 75 v.Log.Infof("Cluster %s is using trusted certs", cluster.GetName()) 76 } else { 77 // unexpected error 78 return ctrl.Result{}, err 79 } 80 } else { 81 // need to create a CA secret in admin cluster 82 // get the workload cluster API CA cert 83 caCrt, err := v.getWorkloadClusterCACert(workloadClient) 84 if err != nil { 85 return ctrl.Result{}, err 86 } 87 // persist the workload API certificate on the admin cluster 88 adminWorkloadCertSecret := &v1.Secret{ObjectMeta: metav1.ObjectMeta{ 89 Name: fmt.Sprintf("ca-secret-%s", cluster.GetName()), 90 Namespace: constants.VerrazzanoMultiClusterNamespace}} 91 if _, err := ctrl.CreateOrUpdate(context.TODO(), v.Client, adminWorkloadCertSecret, func() error { 92 if len(adminWorkloadCertSecret.Data) == 0 { 93 adminWorkloadCertSecret.Data = make(map[string][]byte) 94 } 95 adminWorkloadCertSecret.Data["cacrt"] = caCrt 96 97 return nil 98 }); err != nil { 99 return ctrl.Result{}, err 100 } 101 102 if _, err := r.createOrUpdateWorkloadClusterVMC(ctx, cluster, vmc, func() error { 103 if vmc.Labels == nil { 104 vmc.Labels = make(map[string]string) 105 } 106 vmc.Labels[rancher.CreatedByLabel] = rancher.CreatedByVerrazzano 107 vmc.Spec = clustersv1alpha1.VerrazzanoManagedClusterSpec{ 108 CASecret: fmt.Sprintf("ca-secret-%s", cluster.GetName()), 109 } 110 return nil 111 }); err != nil { 112 return ctrl.Result{}, err 113 } 114 } 115 116 // wait for VMC status to indicate the VMC is ready 117 existingVMC := &clustersv1alpha1.VerrazzanoManagedCluster{} 118 err = v.Get(ctx, types.NamespacedName{Namespace: vmc.Namespace, Name: vmc.Name}, existingVMC) 119 if err != nil { 120 return ctrl.Result{}, err 121 } 122 for _, condition := range existingVMC.Status.Conditions { 123 if condition.Type == clustersv1alpha1.ConditionReady && condition.Status != v1.ConditionTrue { 124 return vzctrl.ShortRequeue(), nil 125 } 126 } 127 128 manifest, err := v.getClusterManifest(cluster) 129 if err != nil { 130 return ctrl.Result{}, err 131 } 132 133 // apply the manifest to workload cluster 134 yamlApplier := k8sutil.NewYAMLApplier(workloadClient, "") 135 err = yamlApplier.ApplyS(string(manifest)) 136 if err != nil { 137 v.Log.Errorf("Failed applying cluster manifest to workload cluster %s", cluster.GetName()) 138 return ctrl.Result{}, err 139 } 140 141 return ctrl.Result{}, nil 142 } 143 144 // getWorkloadClusterCACert retrieves the API endpoint CA certificate from the workload cluster 145 func (v *VerrazzanoRegistration) getWorkloadClusterCACert(workloadClient client.Client) ([]byte, error) { 146 caCrtSecret := &v1.Secret{} 147 err := workloadClient.Get(context.TODO(), types.NamespacedName{ 148 Name: constants.VerrazzanoIngressTLSSecret, 149 Namespace: constants.VerrazzanoSystemNamespace}, 150 caCrtSecret) 151 if err != nil { 152 return nil, err 153 } 154 caCrt, ok := caCrtSecret.Data["ca.crt"] 155 if !ok { 156 return nil, fmt.Errorf("Workload cluster CA certificate not found in verrazzano-tls secret") 157 } 158 return caCrt, nil 159 } 160 161 // isVerrazzanoReady checks to see whether the Verrazzano resource on the workload cluster is ready 162 func (v *VerrazzanoRegistration) isVerrazzanoReady(ctx context.Context, workloadClient client.Client) (bool, error) { 163 if err := workloadClient.Get(ctx, 164 types.NamespacedName{Name: constants.Verrazzano, Namespace: constants.VerrazzanoSystemNamespace}, 165 &v1.Secret{}); err != nil { 166 if !errors.IsNotFound(err) { 167 v.Log.Debugf("Failed to retrieve verrazzano secret: %v", err) 168 return false, err 169 } 170 v.Log.Debugf("Verrazzano secret not found") 171 return false, nil 172 } 173 return true, nil 174 } 175 176 // getClusterManifest retrieves the registration manifest for the workload cluster 177 func (v *VerrazzanoRegistration) getClusterManifest(cluster *unstructured.Unstructured) ([]byte, error) { 178 // retrieve the manifest for the workload cluster 179 manifestSecret := &v1.Secret{} 180 err := v.Get(context.TODO(), types.NamespacedName{ 181 Name: fmt.Sprintf("verrazzano-cluster-%s-manifest", cluster.GetName()), 182 Namespace: constants.VerrazzanoMultiClusterNamespace}, 183 manifestSecret) 184 if err != nil { 185 return nil, err 186 } 187 manifest, ok := manifestSecret.Data["yaml"] 188 if !ok { 189 return nil, fmt.Errorf("Error retrieving cluster manifest for %s", cluster.GetName()) 190 } 191 return manifest, nil 192 } 193 194 // UnregisterVerrazzanoCluster performs the operations required to de-register the cluster from Verrazzano 195 func UnregisterVerrazzanoCluster(ctx context.Context, v *VerrazzanoRegistration, cluster *unstructured.Unstructured) error { 196 workloadClient, err := getWorkloadClusterClient(v.Client, v.Log, cluster) 197 if err != nil { 198 return err 199 } 200 // get the list of cluster related resources 201 manifest, err := v.getClusterManifest(cluster) 202 if err != nil { 203 return err 204 } 205 206 // remove the resources from workload cluster 207 yamlApplier := k8sutil.NewYAMLApplier(workloadClient, "") 208 err = yamlApplier.DeleteS(string(manifest)) 209 if err != nil { 210 v.Log.Errorf("Failed deleting resources of cluster manifest from workload cluster %s", cluster.GetName()) 211 return err 212 } 213 214 return nil 215 }