1 package mapper
2
3 import (
4 "encoding/json"
5 "fmt"
6 "slices"
7
8 "github.com/fluxcd/pkg/apis/kustomize"
9 corev1 "k8s.io/api/core/v1"
10 virtv1 "kubevirt.io/api/core/v1"
11
12 "edge-infra.dev/pkg/edge/api/graph/model"
13 persistence "edge-infra.dev/pkg/edge/apis/persistence/v1alpha1"
14 "edge-infra.dev/pkg/edge/capabilities"
15 kinformmodel "edge-infra.dev/pkg/f8n/kinform/model"
16 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
17 "edge-infra.dev/pkg/sds/lib/jsonpatch"
18 )
19
20 type HelmReleasePostRenderer struct {
21 installationType model.WorkloadInstallationType
22 customLabels []*model.Label
23 helmEdgeID string
24 }
25
26 func (h *HelmReleasePostRenderer) GetNodeSelectorTerm() corev1.NodeSelectorTerm {
27 operator := corev1.NodeSelectorOpIn
28 key := "node.ncr.com/class"
29
30 if h.installationType == model.WorkloadInstallationTypeTouchpoint {
31 operator = corev1.NodeSelectorOpNotIn
32 }
33
34 return corev1.NodeSelectorTerm{
35 MatchExpressions: []corev1.NodeSelectorRequirement{
36 {
37 Key: key,
38 Operator: operator,
39 Values: []string{"server"},
40 },
41 },
42 }
43 }
44
45 func (h *HelmReleasePostRenderer) GetAffinityPatchStr() string {
46 affinity := h.nodeAffinity()
47 jsonAffinityByteArr, err := json.Marshal(affinity)
48 if err != nil {
49 fmt.Println(err)
50 }
51 return string(jsonAffinityByteArr)
52 }
53
54 func (h *HelmReleasePostRenderer) nodeAffinity() corev1.NodeAffinity {
55 if h.installationType == model.WorkloadInstallationTypeAdvanced && h.customLabels != nil {
56 return h.CreateCustomLabelsAffinity()
57 }
58 return h.CreateAffinity()
59 }
60
61 func (h *HelmReleasePostRenderer) GetNodeSelectorPatchStr() string {
62 var jsonNodeSelectorByteArr []byte
63 var err error
64
65 if h.installationType == model.WorkloadInstallationTypeAdvanced && h.customLabels != nil {
66 jsonNodeSelectorByteArr, err = json.Marshal(h.createCustomLabelNodeSelectorTerms())
67 } else {
68 jsonNodeSelectorByteArr, err = json.Marshal([]corev1.NodeSelectorTerm{
69 h.GetNodeSelectorTerm(),
70 })
71 }
72
73 if err != nil {
74 fmt.Println(err)
75 }
76
77 return string(jsonNodeSelectorByteArr)
78 }
79
80
81
82
83
84
85 func disableDriftDetectionForProblemKinds() []kustomize.Patch {
86 patches := make([]kustomize.Patch, 0)
87 problemKinds := []string{"IENPatch", "StatefulSet", "DaemonSet"}
88 for _, problemKind := range problemKinds {
89 patch := kustomize.Patch{
90 Target: &kustomize.Selector{
91 Version: "v1",
92 Kind: problemKind,
93 },
94 Patch: "- op: add\n path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n value: disabled",
95 }
96 patches = append(patches, patch)
97 }
98 return patches
99 }
100
101 func (h *HelmReleasePostRenderer) GetKustomizePatches() ([]kustomize.Patch, error) {
102 patchKindsByInstallationType := map[model.WorkloadInstallationType][]string{
103 model.WorkloadInstallationTypeServerPreferred: {"Deployment", "StatefulSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine"},
104 model.WorkloadInstallationTypeAny: {"Deployment", "StatefulSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine"},
105 model.WorkloadInstallationTypeAdvanced: {"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
106 model.WorkloadInstallationTypeTouchpoint: {"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
107 model.WorkloadInstallationTypeServer: {"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
108 }
109
110 patchesKinds := patchKindsByInstallationType[h.installationType]
111 jsonAffinityStr := h.GetAffinityPatchStr()
112 jsonNodeSelectorStr := h.GetNodeSelectorPatchStr()
113
114 var patches []kustomize.Patch
115 for _, patchKind := range patchesKinds {
116 kustomizeSelector := kustomize.Selector{
117 Version: "v1",
118 Kind: patchKind,
119 }
120
121 kubePatch := kustomize.Patch{}
122 switch patchKind {
123 case persistence.Kind:
124 kustomizeSelector.Version = persistence.GroupVersion.Version
125 kustomizeSelector.Group = persistence.GroupVersion.Group
126 kubePatch.Patch = fmt.Sprintf("- op: replace\n path: /spec/nodeSelectorTerms\n value: %s", jsonNodeSelectorStr)
127 kubePatch.Target = &kustomizeSelector
128 case "CronJob":
129 kubePatch.Patch = fmt.Sprintf("- op: add\n path: /spec/jobTemplate/spec/template/spec/affinity\n value: {\"nodeAffinity\": %s }", jsonAffinityStr)
130 kubePatch.Target = &kustomizeSelector
131 case "VirtualMachine":
132 var err error
133 kubePatch, err = h.vmPatch()
134 if err != nil {
135 return nil, err
136 }
137 default:
138 kubePatch.Patch = fmt.Sprintf("- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": %s }", jsonAffinityStr)
139 kubePatch.Target = &kustomizeSelector
140 }
141
142 patches = append(patches, kubePatch)
143 }
144
145 return patches, nil
146 }
147
148 func (h *HelmReleasePostRenderer) vmPatch() (kustomize.Patch, error) {
149 kustomizeSelector := kustomize.Selector{
150 Version: virtv1.SchemeGroupVersion.Version,
151 Group: virtv1.SchemeGroupVersion.Group,
152 Kind: "VirtualMachine",
153 }
154 ops := []jsonpatch.Operation{}
155 affinity := h.nodeAffinity()
156 ops = append(ops, jsonpatch.Operation{
157 Op: jsonpatch.Add,
158 Path: "/spec/template/spec/affinity",
159 Value: corev1.Affinity{NodeAffinity: &affinity},
160 })
161 ops = append(ops, jsonpatch.Operation{
162 Op: jsonpatch.Add,
163 Path: fmt.Sprintf("/spec/template/metadata/annotations/%s", kinformmodel.HelmEdgeIDAnnotation),
164 Value: h.helmEdgeID,
165 })
166 patchStr, err := jsonpatch.NewPatch(ops...).String()
167 if err != nil {
168 return kustomize.Patch{}, err
169 }
170 return kustomize.Patch{
171 Patch: patchStr,
172 Target: &kustomizeSelector,
173 }, nil
174 }
175
176 func (h *HelmReleasePostRenderer) CreateAffinity() corev1.NodeAffinity {
177 affinity := corev1.NodeAffinity{}
178
179 if h.installationType == model.WorkloadInstallationTypeAny || h.installationType == model.WorkloadInstallationTypeServerPreferred {
180 affinity.PreferredDuringSchedulingIgnoredDuringExecution = []corev1.PreferredSchedulingTerm{
181 {
182 Weight: 1,
183 Preference: h.GetNodeSelectorTerm(),
184 },
185 }
186 } else {
187 affinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{
188 NodeSelectorTerms: []corev1.NodeSelectorTerm{
189 h.GetNodeSelectorTerm(),
190 },
191 }
192 }
193
194 return affinity
195 }
196
197 func (h *HelmReleasePostRenderer) CreateCustomLabelsAffinity() corev1.NodeAffinity {
198 affinity := corev1.NodeAffinity{}
199
200 nodeSelectorTerms := h.createCustomLabelNodeSelectorTerms()
201
202 affinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{
203 NodeSelectorTerms: nodeSelectorTerms,
204 }
205
206 return affinity
207 }
208
209 func (h *HelmReleasePostRenderer) createCustomLabelNodeSelectorTerms() []corev1.NodeSelectorTerm {
210 nodeSelectorTerm := corev1.NodeSelectorTerm{}
211 if h.customLabels != nil {
212 for _, label := range h.customLabels {
213 key := label.Key
214 if !slices.Contains(capabilities.EdgeAutomatedCapabilityLabelTypes, label.Type) {
215 key = fmt.Sprintf(v1ien.CustomNodeLabel, label.Key)
216 }
217 matchExpression := corev1.NodeSelectorRequirement{
218 Key: key,
219 Operator: corev1.NodeSelectorOpExists,
220 }
221 nodeSelectorTerm.MatchExpressions = append(nodeSelectorTerm.MatchExpressions, matchExpression)
222 }
223 }
224 return []corev1.NodeSelectorTerm{
225 nodeSelectorTerm,
226 }
227 }
228
229 func (h *HelmReleasePostRenderer) CreatePostrender() (interface{}, error) {
230 return h.GetKustomizePatches()
231 }
232
View as plain text