github.com/verrazzano/verrazzano@v1.7.0/cluster-operator/controllers/capi/rancher.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  	"github.com/verrazzano/verrazzano/cluster-operator/controllers/vmc"
     9  	"github.com/verrazzano/verrazzano/pkg/constants"
    10  	vzctrl "github.com/verrazzano/verrazzano/pkg/controller"
    11  	"github.com/verrazzano/verrazzano/pkg/k8s/ready"
    12  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    13  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    14  	"github.com/verrazzano/verrazzano/pkg/rancherutil"
    15  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/common"
    16  	"go.uber.org/zap"
    17  	v1 "k8s.io/api/core/v1"
    18  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    19  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    20  	"k8s.io/apimachinery/pkg/types"
    21  	ctrl "sigs.k8s.io/controller-runtime"
    22  	"sigs.k8s.io/controller-runtime/pkg/client"
    23  )
    24  
    25  type RancherRegistration struct {
    26  	client.Client
    27  	Log                *zap.SugaredLogger
    28  	RancherIngressHost string
    29  }
    30  
    31  type ClusterRancherRegistrationFnType func(ctx context.Context, r *RancherRegistration, cluster *unstructured.Unstructured) (ctrl.Result, error)
    32  
    33  var clusterRancherRegistrationFn ClusterRancherRegistrationFnType = ensureRancherRegistration
    34  
    35  func SetClusterRancherRegistrationFunction(f ClusterRancherRegistrationFnType) {
    36  	clusterRancherRegistrationFn = f
    37  }
    38  
    39  func SetDefaultClusterRancherRegistrationFunction() {
    40  	clusterRancherRegistrationFn = ensureRancherRegistration
    41  }
    42  
    43  type ClusterRancherUnregistrationFnType func(ctx context.Context, r *RancherRegistration, cluster *unstructured.Unstructured) error
    44  
    45  var clusterRancherUnregistrationFn ClusterRancherUnregistrationFnType = UnregisterRancherCluster
    46  
    47  func SetClusterRancherUnregistrationFunction(f ClusterRancherUnregistrationFnType) {
    48  	clusterRancherUnregistrationFn = f
    49  }
    50  
    51  func SetDefaultClusterRancherUnregistrationFunction() {
    52  	clusterRancherUnregistrationFn = UnregisterRancherCluster
    53  }
    54  
    55  func (r *RancherRegistration) doReconcile(ctx context.Context, cluster *unstructured.Unstructured) (ctrl.Result, error) {
    56  	// only process CAPI cluster instances not managed by Rancher/container driver
    57  	_, ok := cluster.GetLabels()[clusterProvisionerLabel]
    58  	if ok {
    59  		return ctrl.Result{}, nil
    60  	}
    61  
    62  	err := ready.DeploymentsAreAvailable(r.Client, []types.NamespacedName{{
    63  		Namespace: common.CattleSystem,
    64  		Name:      common.RancherName,
    65  	}})
    66  	if err != nil {
    67  		return vzctrl.LongRequeue(), nil
    68  	}
    69  
    70  	// wait for kubeconfig and complete registration on workload cluster
    71  	return clusterRancherRegistrationFn(ctx, r, cluster)
    72  }
    73  
    74  // GetRancherAPIResources returns the set of resources required for interacting with Rancher
    75  func (r *RancherRegistration) GetRancherAPIResources(cluster *unstructured.Unstructured) (*rancherutil.RancherConfig, vzlog.VerrazzanoLogger, error) {
    76  	// Get the resource logger needed to log message using 'progress' and 'once' methods
    77  	log, err := vzlog.EnsureResourceLogger(&vzlog.ResourceConfig{
    78  		Name:           cluster.GetName(),
    79  		Namespace:      cluster.GetNamespace(),
    80  		ID:             string(cluster.GetUID()),
    81  		Generation:     cluster.GetGeneration(),
    82  		ControllerName: "capicluster",
    83  	})
    84  	if err != nil {
    85  		r.Log.Errorf("Failed to create controller logger for CAPI cluster controller", err)
    86  		return nil, nil, err
    87  	}
    88  
    89  	// using direct rancher API to register cluster
    90  	rc, err := rancherutil.NewAdminRancherConfig(r.Client, r.RancherIngressHost, log)
    91  	if err != nil {
    92  		r.Log.Error(err, "failed to create Rancher API client")
    93  		return nil, nil, err
    94  	}
    95  	return rc, log, nil
    96  }
    97  
    98  // UnregisterRancherCluster performs the operations required to de-register the cluster from Rancher
    99  func UnregisterRancherCluster(ctx context.Context, r *RancherRegistration, cluster *unstructured.Unstructured) error {
   100  	_, ok := cluster.GetLabels()[clusterProvisionerLabel]
   101  	if ok {
   102  		return nil
   103  	}
   104  
   105  	clusterID := getClusterID(ctx, r.Client, cluster)
   106  	if len(clusterID) == 0 {
   107  		// no cluster id found, nothing to do
   108  		return nil
   109  	}
   110  	rc, log, err := r.GetRancherAPIResources(cluster)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	_, err = vmc.DeleteClusterFromRancher(rc, clusterID, log)
   115  	if err != nil {
   116  		log.Errorf("Unable to unregister cluster %s from Rancher: %v", cluster.GetName(), err)
   117  		return err
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  // ensureRancherRegistration ensures that the CAPI cluster is registered with Rancher.
   124  func ensureRancherRegistration(ctx context.Context, r *RancherRegistration, cluster *unstructured.Unstructured) (ctrl.Result, error) {
   125  	workloadClient, err := getWorkloadClusterClient(r.Client, r.Log, cluster)
   126  	if err != nil {
   127  		return ctrl.Result{}, err
   128  	}
   129  
   130  	rc, log, err := r.GetRancherAPIResources(cluster)
   131  	if err != nil {
   132  		r.Log.Infof("Failed getting rancher api resources")
   133  		return ctrl.Result{}, err
   134  	}
   135  
   136  	clusterID := getClusterID(ctx, r.Client, cluster)
   137  
   138  	// register with Rancher
   139  	registryYaml, clusterID, registryErr := vmc.RegisterManagedClusterWithRancher(rc, cluster.GetName(), clusterID, log)
   140  	// handle registry failure error
   141  	if registryErr != nil {
   142  		r.Log.Error(err, "failed to obtain registration manifest from Rancher")
   143  		return ctrl.Result{}, registryErr
   144  	}
   145  	// it appears that in some circumstances the registry yaml may be empty so need to re-queue to re-attempt retrieval
   146  	if len(registryYaml) == 0 {
   147  		return vzctrl.ShortRequeue(), nil
   148  	}
   149  
   150  	if registrationInitiated != getClusterRegistrationStatus(ctx, r.Client, cluster) {
   151  		// apply registration yaml to managed cluster
   152  		yamlApplier := k8sutil.NewYAMLApplier(workloadClient, "")
   153  		err = yamlApplier.ApplyS(registryYaml)
   154  		if err != nil {
   155  			r.Log.Infof("Failed applying Rancher registration yaml in workload cluster")
   156  			return ctrl.Result{}, err
   157  		}
   158  		err = persistClusterStatus(ctx, r.Client, cluster, r.Log, clusterID, registrationInitiated)
   159  		if err != nil {
   160  			r.Log.Infof("Failed to perist cluster status")
   161  			return ctrl.Result{}, err
   162  		}
   163  	}
   164  
   165  	// get and label the cattle-system namespace
   166  	ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: common.CattleSystem}}
   167  	if _, err := ctrl.CreateOrUpdate(context.TODO(), workloadClient, ns, func() error {
   168  		if ns.Labels == nil {
   169  			ns.Labels = make(map[string]string)
   170  		}
   171  		ns.Labels[constants.LabelVerrazzanoNamespace] = common.CattleSystem
   172  		return nil
   173  	}); err != nil {
   174  		return ctrl.Result{}, err
   175  	}
   176  
   177  	return ctrl.Result{}, nil
   178  }