sidero/app/caps-controller-manager/controllers/serverbinding_controller.go
Andrey Smirnov 90e7804640 chore: bump dependencies in go.mod
This also bumps cluster-api version to the latest 0.3 release.

Closes #476 #484 #486 #487 #490 #491 #492 #493

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
2021-07-06 13:51:10 -07:00

232 lines
6.6 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package controllers
import (
"context"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/cluster-api/util/patch"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
infrav1 "github.com/talos-systems/sidero/app/caps-controller-manager/api/v1alpha3"
metalv1alpha1 "github.com/talos-systems/sidero/app/sidero-controller-manager/api/v1alpha1"
)
// ServerBindingReconciler reconciles a ServerBinding object.
type ServerBindingReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
Recorder record.EventRecorder
}
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=serverbindings,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=serverbindings/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=metalmachines,verbs=get;list;watch
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=metalmachines/status,verbs=get;list;watch
// +kubebuilder:rbac:groups=metal.sidero.dev,resources=serverclasses,verbs=get;list;watch;
// +kubebuilder:rbac:groups=metal.sidero.dev,resources=serverclasses/status,verbs=get;list;watch;
// +kubebuilder:rbac:groups=metal.sidero.dev,resources=servers,verbs=get;list;watch;
// +kubebuilder:rbac:groups=metal.sidero.dev,resources=servers/status,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
func (r *ServerBindingReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, err error) {
ctx := context.Background()
logger := r.Log.WithValues("serverbinding", req.NamespacedName)
serverBinding := &infrav1.ServerBinding{}
err = r.Get(ctx, req.NamespacedName, serverBinding)
if apierrors.IsNotFound(err) {
return r.reconcileTransition(logger, req)
}
if err != nil {
return ctrl.Result{}, err
}
// Initialize the patch helper
patchHelper, err := patch.NewHelper(serverBinding, r)
if err != nil {
return ctrl.Result{}, err
}
// Always attempt to Patch the ServerBinding object and status after each reconciliation.
defer func() {
if e := patchHelper.Patch(ctx, serverBinding); e != nil {
logger.Error(e, "failed to patch metalMachine")
if err == nil {
err = e
}
}
}()
var server metalv1alpha1.Server
err = r.Get(ctx, req.NamespacedName, &server)
if err != nil {
if apierrors.IsNotFound(err) {
serverBinding.Status.Ready = false
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
serverBinding.Status.Ready = true
return ctrl.Result{}, nil
}
func (r *ServerBindingReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error {
if err := mgr.GetFieldIndexer().IndexField(&infrav1.MetalMachine{}, infrav1.MetalMachineServerRefField, func(rawObj runtime.Object) []string {
metalMachine := rawObj.(*infrav1.MetalMachine)
if metalMachine.Spec.ServerRef == nil {
return nil
}
return []string{metalMachine.Spec.ServerRef.Name}
}); err != nil {
return err
}
// This mapRequests handler allows us to transition to the new scheme with ServerBinding.
mapRequests := handler.ToRequestsFunc(
func(a handler.MapObject) []reconcile.Request {
metalMachine := &infrav1.MetalMachine{}
if err := r.Get(context.Background(), types.NamespacedName{Namespace: a.Meta.GetNamespace(), Name: a.Meta.GetName()}, metalMachine); err != nil {
return nil
}
if metalMachine.Spec.ServerRef == nil {
return nil
}
return []reconcile.Request{
{
NamespacedName: types.NamespacedName{
Name: metalMachine.Spec.ServerRef.Name,
Namespace: metalMachine.Spec.ServerRef.Namespace,
},
},
}
})
return ctrl.NewControllerManagedBy(mgr).
WithOptions(options).
For(&infrav1.ServerBinding{}).
Watches(
&source.Kind{Type: &infrav1.MetalMachine{}},
&handler.EnqueueRequestsFromMapFunc{
ToRequests: mapRequests,
},
).
Complete(r)
}
func (r *ServerBindingReconciler) reconcileTransition(logger logr.Logger, req ctrl.Request) (_ ctrl.Result, err error) {
ctx := context.Background()
logger.Info("reconciling missing serverbinding")
var metalMachineList infrav1.MetalMachineList
if err := r.List(ctx, &metalMachineList, client.MatchingFields(fields.Set{infrav1.MetalMachineServerRefField: req.Name})); err != nil {
return ctrl.Result{}, err
}
var serverBinding infrav1.ServerBinding
serverBinding.Namespace = req.Namespace
serverBinding.Name = req.Name
serverBinding.Labels = map[string]string{}
found := false
for _, metalMachine := range metalMachineList.Items {
if !metalMachine.DeletionTimestamp.IsZero() {
continue
}
if metalMachine.Spec.ServerRef != nil {
if metalMachine.Spec.ServerRef.Name == serverBinding.Name && metalMachine.Spec.ServerRef.Namespace == serverBinding.Namespace {
found = true
serverBinding.Spec.MetalMachineRef = corev1.ObjectReference{
Kind: metalMachine.Kind,
UID: metalMachine.UID,
Namespace: metalMachine.Namespace,
Name: metalMachine.Name,
}
if metalMachine.Spec.ServerClassRef != nil {
serverBinding.Spec.ServerClassRef = metalMachine.Spec.ServerClassRef.DeepCopy()
}
for label, value := range metalMachine.Labels {
serverBinding.Labels[label] = value
}
break
}
}
}
if !found {
logger.Info("no matching metalmachine found")
return ctrl.Result{}, nil
}
var server metalv1alpha1.Server
if err = r.Get(ctx, req.NamespacedName, &server); err != nil {
if apierrors.IsNotFound(err) {
// broken link?
logger.Info("server not found", "name", req.Name)
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
for _, ownerRef := range server.OwnerReferences {
if ownerRef.Kind == "ServerClass" {
serverBinding.Spec.ServerClassRef = &corev1.ObjectReference{
Kind: ownerRef.Kind,
Name: ownerRef.Name,
}
}
}
logger.Info("creating missing server binding", "metalmachine", serverBinding.Spec.MetalMachineRef.Name)
err = r.Create(ctx, &serverBinding)
if err != nil {
if apierrors.IsAlreadyExists(err) {
err = nil
}
}
return ctrl.Result{}, err
}