github.com/webmeshproj/webmesh-cni@v0.0.27/internal/controllers/node_controller.go (about)

     1  /*
     2  Copyright 2023 Avi Zimmerman <avi.zimmerman@gmail.com>.
     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  	"time"
    23  
    24  	v1 "github.com/webmeshproj/api/go/v1"
    25  	storagev1 "github.com/webmeshproj/storage-provider-k8s/api/storage/v1"
    26  	"github.com/webmeshproj/storage-provider-k8s/provider"
    27  	meshtypes "github.com/webmeshproj/webmesh/pkg/storage/types"
    28  	ctrl "sigs.k8s.io/controller-runtime"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    31  	"sigs.k8s.io/controller-runtime/pkg/handler"
    32  	"sigs.k8s.io/controller-runtime/pkg/log"
    33  
    34  	"github.com/webmeshproj/webmesh-cni/internal/host"
    35  )
    36  
    37  // StoragePeerFinalizer is the StoragePeer finalizer.
    38  const StoragePeerFinalizer = "storagepeer.cniv1.webmesh.io"
    39  
    40  //+kubebuilder:rbac:groups=storage.webmesh.io,resources=storagepeers/finalizers,verbs=get;update;patch
    41  
    42  // NodeReconciler watches for nodes joining and leaving the cluster and ensures
    43  // we have edges between the host node and them.
    44  type NodeReconciler struct {
    45  	client.Client
    46  	Host     host.Node
    47  	Provider *provider.Provider
    48  }
    49  
    50  // SetupWithManager sets up the node reconciler with the manager.
    51  func (r *NodeReconciler) SetupWithManager(mgr ctrl.Manager) error {
    52  	return ctrl.NewControllerManagedBy(mgr).
    53  		Named("node-edges").
    54  		Watches(&storagev1.StoragePeer{}, &handler.EnqueueRequestForObject{}).
    55  		Complete(r)
    56  }
    57  
    58  // Reconcile reconciles a node.
    59  func (r *NodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    60  	log := log.FromContext(ctx)
    61  	if !r.Host.Started() {
    62  		log.Info("Host not started yet, requeing")
    63  		return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 2}, nil
    64  	}
    65  	log.Info("Reconciling cluster CNI node")
    66  	var node storagev1.StoragePeer
    67  	if err := r.Get(ctx, req.NamespacedName, &node); err != nil {
    68  		if client.IgnoreNotFound(err) != nil {
    69  			log.Error(err, "Failed to lookup node")
    70  			return ctrl.Result{}, err
    71  		}
    72  		return ctrl.Result{}, nil
    73  	}
    74  	if node.GetId() == r.Host.ID().String() {
    75  		log.Info("Ignoring the local host node")
    76  		return ctrl.Result{}, nil
    77  	}
    78  	if node.GetDeletionTimestamp() != nil {
    79  		// Ensure the edge is removed.
    80  		log.Info("Removing edge to node", "source", r.Host.ID(), "target", node.GetName())
    81  		err := r.Provider.MeshDB().Peers().RemoveEdge(ctx, meshtypes.NodeID(r.Host.ID()), meshtypes.NodeID(node.GetId()))
    82  		if err != nil {
    83  			log.Error(err, "Failed to remove edge to node")
    84  			return ctrl.Result{}, err
    85  		}
    86  		if controllerutil.ContainsFinalizer(&node, StoragePeerFinalizer) {
    87  			updated := controllerutil.RemoveFinalizer(&node, StoragePeerFinalizer)
    88  			if updated {
    89  				log.Info("Removing finalizer from storage peer")
    90  				if err := r.Update(ctx, &node); err != nil {
    91  					return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %w", err)
    92  				}
    93  			}
    94  		}
    95  		return ctrl.Result{}, nil
    96  	}
    97  	// Make sure the finalizer is present first.
    98  	if !controllerutil.ContainsFinalizer(&node, StoragePeerFinalizer) {
    99  		updated := controllerutil.AddFinalizer(&node, StoragePeerFinalizer)
   100  		if updated {
   101  			log.V(1).Info("Adding finalizer to storage peer")
   102  			if err := r.Update(ctx, &node); err != nil {
   103  				return ctrl.Result{}, fmt.Errorf("failed to add finalizer: %w", err)
   104  			}
   105  			return ctrl.Result{}, nil
   106  		}
   107  	}
   108  	log.Info("Ensuring edge to node", "source", r.Host.ID(), "target", node.GetName())
   109  	err := r.Provider.MeshDB().Peers().PutEdge(ctx, meshtypes.MeshEdge{
   110  		MeshEdge: &v1.MeshEdge{
   111  			Source: r.Host.ID().String(),
   112  			Target: node.GetId(),
   113  			Weight: 100,
   114  		},
   115  	})
   116  	if err != nil {
   117  		log.Error(err, "Failed to add edge to node")
   118  		return ctrl.Result{}, err
   119  	}
   120  	return ctrl.Result{}, nil
   121  }