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
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
74 func (c *IENController) reconcilePlugins(ctx context.Context, ien *v1ien.IENode) (bool, internal.ReconcileTimeSet) {
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
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
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
117
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
125
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
156 func secretPredicateFilter() predicate.Funcs {
157 return predicate.NewPredicateFuncs(func(object client.Object) bool {
158 return object.GetNamespace() == sds.Namespace
159 })
160 }
161
162
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