1 package nodepatcher
2
3 import (
4 "context"
5 "fmt"
6 "slices"
7 "strings"
8
9 "maps"
10
11 corev1 "k8s.io/api/core/v1"
12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13 "sigs.k8s.io/controller-runtime/pkg/client"
14
15 "k8s.io/apimachinery/pkg/util/validation"
16
17 "edge-infra.dev/pkg/edge/api/graph/model"
18 "edge-infra.dev/pkg/edge/capabilities"
19 "edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
20 "edge-infra.dev/pkg/sds/controlplaneguardian/identifier"
21 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
22 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
23 nodemeta "edge-infra.dev/pkg/sds/ien/node"
24 )
25
26 type NodePatcher struct{}
27
28
29 func (nodePatcherPlugin NodePatcher) Reconcile(ctx context.Context, ienode *v1ien.IENode, conf config.Config) (reconcile.Result, error) {
30 node, err := conf.GetHostNode(ctx)
31 if err != nil {
32 return reconcile.ResultRequeue, err
33 }
34
35 idf, err := identifier.FromClient(ctx, conf.GetClient())
36 if client.IgnoreNotFound(err) != nil {
37 return reconcile.ResultRequeue, err
38 }
39
40 nodeUpdated := node.DeepCopy()
41 if err := setNodeLabels(nodeUpdated, ienode, idf); err != nil {
42 return reconcile.ResultRequeue, err
43 }
44 setNodeOwnerReferences(nodeUpdated, ienode, idf)
45
46 if err := conf.GetClient().Patch(ctx, nodeUpdated, client.StrategicMergeFrom(node)); err != nil {
47 return reconcile.ResultRequeue, err
48 }
49 return reconcile.ResultSuccess, nil
50 }
51
52
53 func setNodeLabels(node *corev1.Node, ienode *v1ien.IENode, idf *identifier.ControlPlaneIdentifier) error {
54 labels := node.GetLabels()
55 if err := validateCustomNodeLabels(ienode); err != nil {
56 return err
57 }
58
59
60 maps.DeleteFunc(labels, func(key string, _ string) bool {
61 edgeCap := slices.ContainsFunc(capabilities.EdgeAutomatedCapabilityLabels, func(edgeLabel *model.LabelInput) bool {
62 return edgeLabel != nil && edgeLabel.Key == key
63 })
64 return strings.HasPrefix(key, v1ien.CustomNodePrefix) || edgeCap
65 })
66 maps.Copy(labels, ienode.CustomLabels())
67 addNodeLabels(ienode, labels, idf)
68 node.SetLabels(labels)
69 return nil
70 }
71
72
73 func validateCustomNodeLabels(ienode *v1ien.IENode) error {
74 customLabels := ienode.CustomLabels()
75 for key, value := range customLabels {
76 keyErrs := validation.IsQualifiedName(key)
77 if len(keyErrs) > 0 {
78 return fmt.Errorf("Invalid label key %s: %s", key, strings.Join(keyErrs, "; "))
79 }
80 valueErrs := validation.IsValidLabelValue(value)
81 if len(valueErrs) > 0 {
82 return fmt.Errorf("Invalid label value %s: %s", value, strings.Join(valueErrs, "; "))
83 }
84 }
85 return nil
86 }
87
88
89 func addNodeLabels(ienode *v1ien.IENode, labels map[string]string, idf *identifier.ControlPlaneIdentifier) {
90 if ienode.GetName() != "" {
91 labels[nodemeta.HostnameLabel] = ienode.GetName()
92 }
93 if string(ienode.Spec.Class) != "" {
94 labels[nodemeta.ClassLabel] = string(ienode.Spec.Class)
95 }
96 if ienode.Spec.Lane != "" {
97 labels[nodemeta.LaneLabel] = ienode.Spec.Lane
98 }
99
100 role := getRole(ienode, idf)
101
102 if role != "" {
103 labels[nodemeta.RoleLabel] = role
104 }
105
106 if role == string(v1ien.ControlPlane) {
107 labels[nodemeta.ControlPlaneLabel] = ""
108 delete(labels, nodemeta.WorkerClassLabel)
109 }
110 if role == string(v1ien.Worker) {
111 labels[nodemeta.WorkerClassLabel] = string(ienode.Spec.Class)
112 delete(labels, nodemeta.ControlPlaneLabel)
113 }
114 }
115
116
117
118 func getRole(ienode *v1ien.IENode, idf *identifier.ControlPlaneIdentifier) string {
119 if idf == nil || idf.ControlPlane == "" {
120 return string(ienode.Spec.Role)
121 }
122 if idf.ControlPlane == ienode.GetName() {
123 return string(v1ien.ControlPlane)
124 }
125 return string(v1ien.Worker)
126 }
127
128
129 func setNodeOwnerReferences(node *corev1.Node, ienode *v1ien.IENode, idf *identifier.ControlPlaneIdentifier) {
130 if getRole(ienode, idf) == string(v1ien.Worker) {
131 node.SetOwnerReferences([]metav1.OwnerReference{createOwnerReferenceToIEN(ienode)})
132 return
133 }
134
135 ownerReferences := []metav1.OwnerReference{}
136 for _, or := range node.ObjectMeta.OwnerReferences {
137 if or.Kind == v1ien.IENodeGVK.Kind && or.Name == ienode.Name {
138 continue
139 }
140 ownerReferences = append(ownerReferences, or)
141 }
142 node.OwnerReferences = ownerReferences
143 }
144
145 func createOwnerReferenceToIEN(ienode *v1ien.IENode) metav1.OwnerReference {
146 return metav1.OwnerReference{
147 APIVersion: ienode.APIVersion,
148 Kind: ienode.Kind,
149 Name: ienode.GetName(),
150 UID: ienode.GetUID(),
151 }
152 }
153
View as plain text