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 }