...

Source file src/edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/ien_controller.go

Documentation: edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent

     1  package nodeagent
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"maps"
     8  
     9  	corev1 "k8s.io/api/core/v1"
    10  	ctrl "sigs.k8s.io/controller-runtime"
    11  	"sigs.k8s.io/controller-runtime/pkg/builder"
    12  	"sigs.k8s.io/controller-runtime/pkg/client"
    13  	"sigs.k8s.io/controller-runtime/pkg/handler"
    14  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    15  
    16  	"edge-infra.dev/pkg/k8s/runtime/conditions"
    17  	"edge-infra.dev/pkg/k8s/runtime/controller/metrics"
    18  	"edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
    19  	"edge-infra.dev/pkg/k8s/runtime/patch"
    20  	"edge-infra.dev/pkg/sds"
    21  
    22  	"edge-infra.dev/pkg/sds/controlplaneguardian/identifier"
    23  	"edge-infra.dev/pkg/sds/ien"
    24  	v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
    25  	"edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
    26  	"edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/internal"
    27  	"edge-infra.dev/pkg/sds/ien/topology"
    28  )
    29  
    30  // IENode controller
    31  type IENController struct {
    32  	Controller
    33  	Plugins map[string]IENPlugin
    34  }
    35  
    36  func (c *IENController) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, recErr error) {
    37  	var (
    38  		result            reconcile.Result = reconcile.ResultEmpty
    39  		requeue           bool
    40  		reconcileDuration internal.ReconcileTimeSet
    41  		reconcileStartAt  = time.Now()
    42  	)
    43  
    44  	ienode := &v1ien.IENode{}
    45  	if err := c.Client.Get(ctx, req.NamespacedName, ienode); err != nil {
    46  		return ctrl.Result{}, client.IgnoreNotFound(err)
    47  	}
    48  	c.IENode = ienode
    49  
    50  	metrics, err := c.initializeControllerMetrics(ctx)
    51  	if err != nil {
    52  		return ctrl.Result{RequeueAfter: c.RequeueTime}, err
    53  	}
    54  
    55  	patcher := patch.NewSerialPatcher(c.IENode, c.Client)
    56  	defer func() {
    57  		res, recErr = c.Summarizer(ctx, patcher, result)
    58  		metrics.RecordMetrics(reconcileDuration, time.Since(reconcileStartAt).Seconds())
    59  	}()
    60  
    61  	conditions.MarkFalse(c.IENode, string(v1ien.IENController), string(v1ien.Progressing), "%s", string(v1ien.Reconciling))
    62  
    63  	if requeue, reconcileDuration = c.reconcilePlugins(ctx, c.IENode); requeue {
    64  		conditions.MarkFalse(c.IENode, string(v1ien.IENController), string(v1ien.Progressing), "%s", string(v1ien.Requeueing))
    65  		result = reconcile.ResultRequeue
    66  		return
    67  	}
    68  
    69  	conditions.MarkTrue(c.IENode, string(v1ien.IENController), string(v1ien.Successful), "%s", string(v1ien.Succeeded))
    70  	return res, recErr
    71  }
    72  
    73  // reconcilePlugins iterates through all attached plugins and invokes them.
    74  func (c *IENController) reconcilePlugins(ctx context.Context, ien *v1ien.IENode) (bool, internal.ReconcileTimeSet) { //nolint: dupl
    75  	requireRequeue := false
    76  	pluginReconcileTimes := make(internal.ReconcileTimeSet)
    77  	for name, plugin := range c.Plugins {
    78  		log := ctrl.LoggerFrom(ctx)
    79  		log = log.WithValues("plugin", name)
    80  
    81  		// check if plugin is enabled in config map
    82  		if !c.isPluginEnabled(ctx, ien, name) {
    83  			continue
    84  		}
    85  
    86  		pluginReconcileStart := time.Now()
    87  		res, err := plugin.Reconcile(ctx, ien, c.Config)
    88  		pluginReconcileTimes[name] = time.Since(pluginReconcileStart).Seconds()
    89  
    90  		switch {
    91  		case err != nil:
    92  			log.Error(err, "failed to run plugin")
    93  			conditions.MarkFalse(ien, name, string(v1ien.PluginFailed), "%v", err)
    94  			requireRequeue = true
    95  			continue
    96  		case res == reconcile.ResultRequeue:
    97  			log.Info("requeing plugin")
    98  			conditions.MarkTrue(ien, name, string(v1ien.PluginRequeuing), "%s", string(v1ien.Reconciling))
    99  			requireRequeue = true
   100  		case res == reconcile.ResultSuccess:
   101  			log.V(1).Info("plugin ran successfully")
   102  			conditions.MarkTrue(ien, name, string(v1ien.PluginSuccessful), "%s", string(v1ien.Succeeded))
   103  		}
   104  	}
   105  	return requireRequeue, pluginReconcileTimes
   106  }
   107  
   108  // registers node agent conditions for IENController
   109  func (c *IENController) registerConditions() {
   110  	for pluginName := range maps.Keys(c.Plugins) {
   111  		IENodeConditions.Owned = append(IENodeConditions.Owned, pluginName)
   112  		IENodeConditions.Summarize = append(IENodeConditions.Summarize, pluginName)
   113  	}
   114  }
   115  
   116  // matchesHostnamePredicate returns a filter predicate that will only return
   117  // true and reconcile if the object name matches the hostname.
   118  func matchesHostnamePredicate(hostname string) builder.Predicates {
   119  	return builder.WithPredicates(predicate.GenerationChangedPredicate{}, predicate.NewPredicateFuncs(func(obj client.Object) bool {
   120  		return obj.GetName() == hostname
   121  	}))
   122  }
   123  
   124  // setupIENControllerWithManager is the SetupWithManager function to use when
   125  // registering the IENode controller.
   126  func setupIENControllerWithManager(c *IENController, mgr ctrl.Manager) error {
   127  	hostname, err := ien.GetHostname()
   128  	if err != nil {
   129  		return err
   130  	}
   131  	return ctrl.NewControllerManagedBy(mgr).
   132  		For(&v1ien.IENode{}, matchesHostnamePredicate(hostname)).
   133  		Watches(
   134  			&corev1.Node{},
   135  			handler.EnqueueRequestsFromMapFunc(c.createReconcileRequests),
   136  		).
   137  		Watches(
   138  			&corev1.ConfigMap{},
   139  			handler.EnqueueRequestsFromMapFunc(c.createReconcileRequests),
   140  			builder.WithPredicates(
   141  				predicate.Or(
   142  					topology.PredicateFilter(),
   143  					identifier.PredicateFilter(),
   144  				),
   145  			),
   146  		).
   147  		Watches(
   148  			&corev1.Secret{},
   149  			handler.EnqueueRequestsFromMapFunc(c.createReconcileRequests),
   150  			builder.WithPredicates(secretPredicateFilter()),
   151  		).
   152  		Complete(c)
   153  }
   154  
   155  // secretPredicateFilter returns the predicate function for objects in sds namespace
   156  func secretPredicateFilter() predicate.Funcs {
   157  	return predicate.NewPredicateFuncs(func(object client.Object) bool {
   158  		return object.GetNamespace() == sds.Namespace
   159  	})
   160  }
   161  
   162  // Instantiates a new IENode controller
   163  func newIENController(mgr ctrl.Manager, conf config.Config, metrics metrics.Metrics) IENController {
   164  	ienctl := IENController{
   165  		Plugins: IENPlugins,
   166  		Controller: Controller{
   167  			Client:      mgr.GetClient(),
   168  			Log:         ctrl.Log.WithName(ienctlName),
   169  			Name:        ienctlName,
   170  			Config:      conf,
   171  			RequeueTime: RequeueTime,
   172  			Conditions:  IENodeConditions,
   173  			Metrics:     metrics,
   174  		},
   175  	}
   176  	ienctl.registerConditions()
   177  	return ienctl
   178  }
   179  

View as plain text