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  }