sigs.k8s.io/cluster-api-provider-azure@v1.17.0/controllers/managedclusteradopt_controller.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	asocontainerservicev1 "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20231001"
    24  	asoresourcesv1 "github.com/Azure/azure-service-operator/v2/api/resources/v1api20200601"
    25  	corev1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	infrav1alpha "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha1"
    29  	"sigs.k8s.io/cluster-api-provider-azure/util/tele"
    30  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    31  	ctrl "sigs.k8s.io/controller-runtime"
    32  	"sigs.k8s.io/controller-runtime/pkg/client"
    33  	"sigs.k8s.io/controller-runtime/pkg/controller"
    34  	"sigs.k8s.io/controller-runtime/pkg/event"
    35  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    36  )
    37  
    38  const (
    39  	adoptAnnotation      = "sigs.k8s.io/cluster-api-provider-azure-adopt"
    40  	adoptAnnotationValue = "true"
    41  )
    42  
    43  // ManagedClusterAdoptReconciler adopts ASO ManagedCluster resources into a CAPI Cluster.
    44  type ManagedClusterAdoptReconciler struct {
    45  	client.Client
    46  }
    47  
    48  // SetupWithManager sets up the controller with the Manager.
    49  func (r *ManagedClusterAdoptReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
    50  	_, err := ctrl.NewControllerManagedBy(mgr).
    51  		WithOptions(options).
    52  		For(&asocontainerservicev1.ManagedCluster{}).
    53  		WithEventFilter(predicate.Funcs{
    54  			UpdateFunc: func(ev event.UpdateEvent) bool {
    55  				return ev.ObjectOld.GetAnnotations()[adoptAnnotation] != ev.ObjectNew.GetAnnotations()[adoptAnnotation]
    56  			},
    57  			DeleteFunc: func(_ event.DeleteEvent) bool { return false },
    58  		}).
    59  		Build(r)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  // +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=create
    68  
    69  // Reconcile reconciles an AzureASOManagedCluster.
    70  func (r *ManagedClusterAdoptReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, resultErr error) {
    71  	ctx, log, done := tele.StartSpanWithLogger(ctx,
    72  		"controllers.ManagedClusterAdoptReconciler.Reconcile",
    73  		tele.KVP("namespace", req.Namespace),
    74  		tele.KVP("name", req.Name),
    75  		tele.KVP("kind", "ManagedCluster"),
    76  	)
    77  	defer done()
    78  
    79  	managedCluster := &asocontainerservicev1.ManagedCluster{}
    80  	err := r.Get(ctx, req.NamespacedName, managedCluster)
    81  	if err != nil {
    82  		return ctrl.Result{}, client.IgnoreNotFound(err)
    83  	}
    84  
    85  	if managedCluster.GetAnnotations()[adoptAnnotation] != adoptAnnotationValue {
    86  		return ctrl.Result{}, nil
    87  	}
    88  
    89  	for _, owner := range managedCluster.GetOwnerReferences() {
    90  		if owner.APIVersion == infrav1alpha.GroupVersion.Identifier() &&
    91  			owner.Kind == infrav1alpha.AzureASOManagedControlPlaneKind {
    92  			return ctrl.Result{}, nil
    93  		}
    94  	}
    95  
    96  	log.Info("adopting")
    97  
    98  	cluster := &clusterv1.Cluster{
    99  		ObjectMeta: metav1.ObjectMeta{
   100  			Namespace: managedCluster.Namespace,
   101  			Name:      managedCluster.Name,
   102  		},
   103  		Spec: clusterv1.ClusterSpec{
   104  			InfrastructureRef: &corev1.ObjectReference{
   105  				APIVersion: infrav1alpha.GroupVersion.Identifier(),
   106  				Kind:       infrav1alpha.AzureASOManagedClusterKind,
   107  				Name:       managedCluster.Name,
   108  			},
   109  			ControlPlaneRef: &corev1.ObjectReference{
   110  				APIVersion: infrav1alpha.GroupVersion.Identifier(),
   111  				Kind:       infrav1alpha.AzureASOManagedControlPlaneKind,
   112  				Name:       managedCluster.Name,
   113  			},
   114  		},
   115  	}
   116  	err = r.Create(ctx, cluster)
   117  	if client.IgnoreAlreadyExists(err) != nil {
   118  		return ctrl.Result{}, err
   119  	}
   120  
   121  	resourceGroup := &asoresourcesv1.ResourceGroup{
   122  		ObjectMeta: metav1.ObjectMeta{
   123  			Namespace: managedCluster.Namespace,
   124  			Name:      managedCluster.Owner().Name,
   125  		},
   126  	}
   127  
   128  	err = r.Get(ctx, client.ObjectKeyFromObject(resourceGroup), resourceGroup)
   129  	if err != nil {
   130  		return ctrl.Result{}, fmt.Errorf("error getting ResourceGroup %s: %w", client.ObjectKeyFromObject(resourceGroup), err)
   131  	}
   132  
   133  	// filter down to what will be persisted in the AzureASOManagedCluster
   134  	resourceGroup = &asoresourcesv1.ResourceGroup{
   135  		TypeMeta: metav1.TypeMeta{
   136  			APIVersion: asoresourcesv1.GroupVersion.Identifier(),
   137  			Kind:       "ResourceGroup",
   138  		},
   139  		ObjectMeta: metav1.ObjectMeta{
   140  			Name: resourceGroup.Name,
   141  		},
   142  		Spec: resourceGroup.Spec,
   143  	}
   144  
   145  	asoManagedCluster := &infrav1alpha.AzureASOManagedCluster{
   146  		ObjectMeta: metav1.ObjectMeta{
   147  			Namespace: managedCluster.Namespace,
   148  			Name:      managedCluster.Name,
   149  		},
   150  		Spec: infrav1alpha.AzureASOManagedClusterSpec{
   151  			AzureASOManagedClusterTemplateResourceSpec: infrav1alpha.AzureASOManagedClusterTemplateResourceSpec{
   152  				Resources: []runtime.RawExtension{
   153  					{Object: resourceGroup},
   154  				},
   155  			},
   156  		},
   157  	}
   158  	err = r.Create(ctx, asoManagedCluster)
   159  	if client.IgnoreAlreadyExists(err) != nil {
   160  		return ctrl.Result{}, err
   161  	}
   162  
   163  	// agent pools are defined by AzureASOManagedMachinePools. Remove them from this resource.
   164  	managedClusterBefore := managedCluster.DeepCopy()
   165  	managedCluster.Spec.AgentPoolProfiles = nil
   166  	err = r.Patch(ctx, managedCluster, client.MergeFrom(managedClusterBefore))
   167  	if err != nil {
   168  		return ctrl.Result{}, err
   169  	}
   170  
   171  	managedCluster = &asocontainerservicev1.ManagedCluster{
   172  		TypeMeta: metav1.TypeMeta{
   173  			APIVersion: asocontainerservicev1.GroupVersion.Identifier(),
   174  			Kind:       "ManagedCluster",
   175  		},
   176  		ObjectMeta: metav1.ObjectMeta{
   177  			Name: managedCluster.Name,
   178  		},
   179  		Spec: managedCluster.Spec,
   180  	}
   181  
   182  	asoManagedControlPlane := &infrav1alpha.AzureASOManagedControlPlane{
   183  		ObjectMeta: metav1.ObjectMeta{
   184  			Namespace: cluster.Namespace,
   185  			Name:      managedCluster.Name,
   186  		},
   187  		Spec: infrav1alpha.AzureASOManagedControlPlaneSpec{
   188  			AzureASOManagedControlPlaneTemplateResourceSpec: infrav1alpha.AzureASOManagedControlPlaneTemplateResourceSpec{
   189  				Resources: []runtime.RawExtension{
   190  					{Object: managedCluster},
   191  				},
   192  			},
   193  		},
   194  	}
   195  	err = r.Create(ctx, asoManagedControlPlane)
   196  	if client.IgnoreAlreadyExists(err) != nil {
   197  		return ctrl.Result{}, err
   198  	}
   199  
   200  	return ctrl.Result{}, nil
   201  }