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

     1  /*
     2  Copyright 2020 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  
    22  	"github.com/pkg/errors"
    23  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    24  	"k8s.io/apimachinery/pkg/types"
    25  	"k8s.io/client-go/tools/record"
    26  	infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
    27  	"sigs.k8s.io/cluster-api-provider-azure/pkg/coalescing"
    28  	"sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
    29  	"sigs.k8s.io/cluster-api-provider-azure/util/tele"
    30  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    31  	"sigs.k8s.io/cluster-api/util"
    32  	"sigs.k8s.io/cluster-api/util/annotations"
    33  	"sigs.k8s.io/cluster-api/util/patch"
    34  	"sigs.k8s.io/cluster-api/util/predicates"
    35  	ctrl "sigs.k8s.io/controller-runtime"
    36  	"sigs.k8s.io/controller-runtime/pkg/client"
    37  	"sigs.k8s.io/controller-runtime/pkg/handler"
    38  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    39  	"sigs.k8s.io/controller-runtime/pkg/source"
    40  )
    41  
    42  // AzureManagedClusterReconciler reconciles an AzureManagedCluster object.
    43  type AzureManagedClusterReconciler struct {
    44  	client.Client
    45  	Recorder         record.EventRecorder
    46  	Timeouts         reconciler.Timeouts
    47  	WatchFilterValue string
    48  }
    49  
    50  // SetupWithManager initializes this controller with a manager.
    51  func (amcr *AzureManagedClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options Options) error {
    52  	ctx, log, done := tele.StartSpanWithLogger(ctx,
    53  		"controllers.AzureManagedClusterReconciler.SetupWithManager",
    54  		tele.KVP("controller", "AzureManagedCluster"),
    55  	)
    56  	defer done()
    57  
    58  	var r reconcile.Reconciler = amcr
    59  	if options.Cache != nil {
    60  		r = coalescing.NewReconciler(amcr, options.Cache, log)
    61  	}
    62  
    63  	azManagedCluster := &infrav1.AzureManagedCluster{}
    64  	// create mapper to transform incoming AzureManagedControlPlanes into AzureManagedCluster requests
    65  	azureManagedControlPlaneMapper, err := AzureManagedControlPlaneToAzureManagedClusterMapper(ctx, amcr.Client, log)
    66  	if err != nil {
    67  		return errors.Wrap(err, "failed to create AzureManagedControlPlane to AzureManagedClusters mapper")
    68  	}
    69  
    70  	c, err := ctrl.NewControllerManagedBy(mgr).
    71  		WithOptions(options.Options).
    72  		For(azManagedCluster).
    73  		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, amcr.WatchFilterValue)).
    74  		// watch AzureManagedControlPlane resources
    75  		Watches(
    76  			&infrav1.AzureManagedControlPlane{},
    77  			handler.EnqueueRequestsFromMapFunc(azureManagedControlPlaneMapper),
    78  		).
    79  		Build(r)
    80  	if err != nil {
    81  		return errors.Wrap(err, "error creating controller")
    82  	}
    83  
    84  	// Add a watch on clusterv1.Cluster object for unpause notifications.
    85  	if err = c.Watch(
    86  		source.Kind(mgr.GetCache(), &clusterv1.Cluster{}),
    87  		handler.EnqueueRequestsFromMapFunc(util.ClusterToInfrastructureMapFunc(ctx, infrav1.GroupVersion.WithKind(infrav1.AzureManagedClusterKind), mgr.GetClient(), &infrav1.AzureManagedCluster{})),
    88  		predicates.ClusterUnpaused(log),
    89  		predicates.ResourceNotPausedAndHasFilterLabel(log, amcr.WatchFilterValue),
    90  	); err != nil {
    91  		return errors.Wrap(err, "failed adding a watch for ready clusters")
    92  	}
    93  	return nil
    94  }
    95  
    96  // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=azuremanagedclusters,verbs=get;list;watch;create;update;patch;delete
    97  // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=azuremanagedclusters/status,verbs=get;update;patch
    98  // +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch
    99  // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
   100  
   101  // Reconcile idempotently gets, creates, and updates a managed cluster.
   102  func (amcr *AzureManagedClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
   103  	ctx, cancel := context.WithTimeout(ctx, amcr.Timeouts.DefaultedLoopTimeout())
   104  	defer cancel()
   105  
   106  	ctx, log, done := tele.StartSpanWithLogger(
   107  		ctx,
   108  		"controllers.AzureManagedClusterReconciler.Reconcile",
   109  		tele.KVP("namespace", req.Namespace),
   110  		tele.KVP("name", req.Name),
   111  		tele.KVP("kind", infrav1.AzureManagedClusterKind),
   112  	)
   113  	defer done()
   114  
   115  	// Fetch the AzureManagedCluster instance
   116  	aksCluster := &infrav1.AzureManagedCluster{}
   117  	err := amcr.Get(ctx, req.NamespacedName, aksCluster)
   118  	if err != nil {
   119  		if apierrors.IsNotFound(err) {
   120  			return reconcile.Result{}, nil
   121  		}
   122  		return reconcile.Result{}, err
   123  	}
   124  
   125  	// Fetch the Cluster.
   126  	cluster, err := util.GetOwnerCluster(ctx, amcr.Client, aksCluster.ObjectMeta)
   127  	if err != nil {
   128  		return reconcile.Result{}, err
   129  	}
   130  	if cluster == nil {
   131  		log.Info("Cluster Controller has not yet set OwnerRef")
   132  		return reconcile.Result{}, nil
   133  	}
   134  
   135  	controlPlane := &infrav1.AzureManagedControlPlane{}
   136  	controlPlaneRef := types.NamespacedName{
   137  		Name:      cluster.Spec.ControlPlaneRef.Name,
   138  		Namespace: cluster.Namespace,
   139  	}
   140  
   141  	log = log.WithValues("cluster", cluster.Name)
   142  
   143  	// Return early if the object or Cluster is paused.
   144  	if annotations.IsPaused(cluster, aksCluster) {
   145  		log.Info("AzureManagedCluster or linked Cluster is marked as paused. Won't reconcile")
   146  		return ctrl.Result{}, nil
   147  	}
   148  
   149  	if err := amcr.Get(ctx, controlPlaneRef, controlPlane); err != nil {
   150  		return reconcile.Result{}, errors.Wrap(err, "failed to get control plane ref")
   151  	}
   152  
   153  	log = log.WithValues("controlPlane", controlPlaneRef.Name)
   154  
   155  	patchhelper, err := patch.NewHelper(aksCluster, amcr.Client)
   156  	if err != nil {
   157  		return reconcile.Result{}, errors.Wrap(err, "failed to init patch helper")
   158  	}
   159  
   160  	// Infrastructure must be ready before control plane. We should also enqueue
   161  	// requests from control plane to infra cluster to keep control plane endpoint accurate.
   162  	aksCluster.Status.Ready = true
   163  	aksCluster.Spec.ControlPlaneEndpoint = controlPlane.Spec.ControlPlaneEndpoint
   164  
   165  	if err := patchhelper.Patch(ctx, aksCluster); err != nil {
   166  		return reconcile.Result{}, err
   167  	}
   168  
   169  	log.Info("Successfully reconciled")
   170  
   171  	return reconcile.Result{}, nil
   172  }