sigs.k8s.io/cluster-api-provider-azure@v1.17.0/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/builder"
    37  	"sigs.k8s.io/controller-runtime/pkg/client"
    38  	"sigs.k8s.io/controller-runtime/pkg/handler"
    39  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    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  	return 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  		// Add a watch on clusterv1.Cluster object for unpause notifications.
    80  		Watches(
    81  			&clusterv1.Cluster{},
    82  			handler.EnqueueRequestsFromMapFunc(util.ClusterToInfrastructureMapFunc(ctx, infrav1.GroupVersion.WithKind(infrav1.AzureManagedClusterKind), mgr.GetClient(), &infrav1.AzureManagedCluster{})),
    83  			builder.WithPredicates(
    84  				predicates.ClusterUnpaused(log),
    85  				predicates.ResourceNotPausedAndHasFilterLabel(log, amcr.WatchFilterValue),
    86  			),
    87  		).
    88  		Complete(r)
    89  }
    90  
    91  // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=azuremanagedclusters,verbs=get;list;watch;create;update;patch;delete
    92  // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=azuremanagedclusters/status,verbs=get;update;patch
    93  // +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch
    94  // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
    95  
    96  // Reconcile idempotently gets, creates, and updates a managed cluster.
    97  func (amcr *AzureManagedClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
    98  	ctx, cancel := context.WithTimeout(ctx, amcr.Timeouts.DefaultedLoopTimeout())
    99  	defer cancel()
   100  
   101  	ctx, log, done := tele.StartSpanWithLogger(
   102  		ctx,
   103  		"controllers.AzureManagedClusterReconciler.Reconcile",
   104  		tele.KVP("namespace", req.Namespace),
   105  		tele.KVP("name", req.Name),
   106  		tele.KVP("kind", infrav1.AzureManagedClusterKind),
   107  	)
   108  	defer done()
   109  
   110  	// Fetch the AzureManagedCluster instance
   111  	aksCluster := &infrav1.AzureManagedCluster{}
   112  	err := amcr.Get(ctx, req.NamespacedName, aksCluster)
   113  	if err != nil {
   114  		if apierrors.IsNotFound(err) {
   115  			return reconcile.Result{}, nil
   116  		}
   117  		return reconcile.Result{}, err
   118  	}
   119  
   120  	// Fetch the Cluster.
   121  	cluster, err := util.GetOwnerCluster(ctx, amcr.Client, aksCluster.ObjectMeta)
   122  	if err != nil {
   123  		return reconcile.Result{}, err
   124  	}
   125  	if cluster == nil {
   126  		log.Info("Cluster Controller has not yet set OwnerRef")
   127  		return reconcile.Result{}, nil
   128  	}
   129  
   130  	controlPlane := &infrav1.AzureManagedControlPlane{}
   131  	controlPlaneRef := types.NamespacedName{
   132  		Name:      cluster.Spec.ControlPlaneRef.Name,
   133  		Namespace: cluster.Namespace,
   134  	}
   135  
   136  	log = log.WithValues("cluster", cluster.Name)
   137  
   138  	// Return early if the object or Cluster is paused.
   139  	if annotations.IsPaused(cluster, aksCluster) {
   140  		log.Info("AzureManagedCluster or linked Cluster is marked as paused. Won't reconcile")
   141  		return ctrl.Result{}, nil
   142  	}
   143  
   144  	if err := amcr.Get(ctx, controlPlaneRef, controlPlane); err != nil {
   145  		return reconcile.Result{}, errors.Wrap(err, "failed to get control plane ref")
   146  	}
   147  
   148  	log = log.WithValues("controlPlane", controlPlaneRef.Name)
   149  
   150  	patchhelper, err := patch.NewHelper(aksCluster, amcr.Client)
   151  	if err != nil {
   152  		return reconcile.Result{}, errors.Wrap(err, "failed to init patch helper")
   153  	}
   154  
   155  	// Infrastructure must be ready before control plane. We should also enqueue
   156  	// requests from control plane to infra cluster to keep control plane endpoint accurate.
   157  	aksCluster.Status.Ready = true
   158  	aksCluster.Spec.ControlPlaneEndpoint = controlPlane.Spec.ControlPlaneEndpoint
   159  
   160  	if err := patchhelper.Patch(ctx, aksCluster); err != nil {
   161  		return reconcile.Result{}, err
   162  	}
   163  
   164  	log.Info("Successfully reconciled")
   165  
   166  	return reconcile.Result{}, nil
   167  }