1 package mapper
2
3 import (
4 "encoding/json"
5 "fmt"
6 "slices"
7 "testing"
8
9 virtv1 "kubevirt.io/api/core/v1"
10
11 "edge-infra.dev/pkg/edge/api/graph/model"
12 "edge-infra.dev/pkg/edge/api/status"
13 "edge-infra.dev/pkg/edge/api/types"
14 "edge-infra.dev/pkg/edge/api/utils"
15 persistence "edge-infra.dev/pkg/edge/apis/persistence/v1alpha1"
16
17 helmAPIv2beta1 "github.com/fluxcd/helm-controller/api/v2beta1"
18 "github.com/fluxcd/pkg/apis/kustomize"
19 "github.com/stretchr/testify/assert"
20 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
21 "k8s.io/apimachinery/pkg/runtime"
22 )
23
24 const (
25 chartVersion = "1.0.0"
26 helmSecret = "helm-repo-secret"
27 persistencePatch = "- op: replace\n path: /spec/nodeSelectorTerms\n value:"
28 )
29
30
31 func TestToHelmRepositoriesModel(t *testing.T) {
32 helmRepo1 := GetTestHelmRepositorySecret()
33 helmRepo2 := GetTestHelmRepositorySecret()
34 repos := []*model.SecretManagerResponse{helmRepo1, helmRepo2}
35 releaseModels := ToHelmRepositoriesModels(repos)
36
37 for i, releaseModel := range releaseModels {
38 VerifyHelmRepositoryEqualModel(repos[i], releaseModel, t)
39 }
40 }
41
42
43 func TestToHelmReleasesModels(t *testing.T) {
44 helmRelease1 := getTestHelmRelease()
45 helmRelease2 := getTestHelmRelease()
46 releases := []helmAPIv2beta1.HelmRelease{*helmRelease1, *helmRelease2}
47 helmReleaseData := getTestHelmReleaseData(releases)
48 releaseModels := ToHelmReleasesModels(releases, helmReleaseData)
49
50 for i, releaseModel := range releaseModels {
51 conditions := releases[i].Status.Conditions
52 conditionTime := conditions[len(conditions)-1].LastTransitionTime.Format(TimeFormat)
53 releaseModelJSON, _ := utils.YAMLToJSON(releaseModel.ConfigValues)
54
55 assert.Equal(t, releases[i].Name, releaseModel.Name)
56 assert.Equal(t, releases[i].Spec.Chart.Spec.Chart, releaseModel.HelmChart)
57 assert.Equal(t, conditionTime, releaseModel.LastActionTime)
58 assert.Equal(t, releaseModel.StatusType, "Failed")
59 assert.Equal(t, releaseModel.VersionInstalled, "")
60 assert.Equal(t, releaseModel.VersionRequested, "2.0.x")
61 assert.Equal(t, releases[i].Spec.TargetNamespace, releaseModel.Namespace)
62 assert.JSONEq(t, string(releases[i].Spec.Values.Raw), string(releaseModelJSON))
63 }
64 }
65
66
67 func TestToHelmReleasesStatusModels(t *testing.T) {
68 helmRelease1 := getTestHelmRelease()
69 helmRelease2 := getTestHelmRelease()
70 releases := []helmAPIv2beta1.HelmRelease{*helmRelease1, *helmRelease2}
71
72 releaseModels := ToHelmReleasesStatusModels(releases)
73
74 for i, releaseModel := range releaseModels {
75 conditions := releases[i].Status.Conditions
76 conditionTime := conditions[len(conditions)-1].LastTransitionTime.Format(TimeFormat)
77 releaseModelJSON, _ := utils.YAMLToJSON(releaseModel.ConfigValues)
78
79 assert.Equal(t, releases[i].Name, releaseModel.Name)
80 assert.Equal(t, conditionTime, releaseModel.LastActionTime)
81 assert.Equal(t, releaseModel.StatusType, "Failed")
82 assert.Equal(t, releaseModel.VersionInstalled, "")
83 assert.Equal(t, releaseModel.VersionRequested, "2.0.x")
84 assert.JSONEq(t, string(releases[i].Spec.Values.Raw), string(releaseModelJSON))
85 }
86 }
87
88
89 func TestToHelmStatusTrue(t *testing.T) {
90 result := ToHelmStatus("True")
91 assert.Equal(t, result, "Succeeded")
92 }
93
94
95 func TestToHelmStatusFail(t *testing.T) {
96 result := ToHelmStatus("False")
97 assert.Equal(t, result, "Failed")
98 }
99
100
101 func TestToPodStatusTrue(t *testing.T) {
102 result := ToPodStatus("True")
103 assert.Equal(t, result, status.Running)
104 }
105
106
107 func TestToPodStatusFalse(t *testing.T) {
108 result := ToPodStatus("False")
109 assert.Equal(t, result, status.Error)
110 }
111
112
113 func TestToCreateHelmReleaseNil(t *testing.T) {
114 helm := getCreateHelmReleaseCreate(HelmReleaseNamespace, "")
115 model, err := ToCreateHelmRelease(name, helmRepository, helmChart, version, "", "", nil, model.WorkloadInstallationTypeServerPreferred, nil, "0.20", "")
116 assert.NoError(t, err)
117 assert.Equal(t, model.GetLabels()["parent-cluster"], Region)
118 verifyHelmReleaseEqualModel(helm, model.(*helmAPIv2beta1.HelmRelease), t)
119 }
120
121
122 func TestToCreateHelmReleaseNamespace(t *testing.T) {
123 namespace := "flux-system"
124 helm := getCreateHelmReleaseCreate(HelmReleaseNamespace, namespace)
125 model, err := ToCreateHelmRelease(name, helmRepository, helmChart, version, "", namespace, nil, model.WorkloadInstallationTypeServerPreferred, nil, "0.20", "")
126 assert.NoError(t, err)
127 assert.Equal(t, model.GetLabels()["parent-cluster"], Region)
128 verifyHelmReleaseEqualModel(helm, model.(*helmAPIv2beta1.HelmRelease), t)
129 }
130
131
132 func TestToCreateHelmReleaseNamespaceConvertConfigMap(t *testing.T) {
133 configMap := []byte(`{"replicaCount" : 2}`)
134 model, err := ToCreateHelmRelease(name, helmRepository, helmChart, version, "", namespace, configMap, model.WorkloadInstallationTypeServerPreferred, nil, "0.20", "")
135 assert.NoError(t, err)
136 assert.Equal(t, model.(*helmAPIv2beta1.HelmRelease).Spec.Values.Raw, configMap)
137 }
138
139
140 func TestToHelmChartResponse(t *testing.T) {
141 var versions []*string
142 name := "alert-manager"
143 mockVersions := []string{"0.3.0", "0.2.0", "0.1.4", "0.1.3", "0.1.2", "0.1.1"}
144
145 for i := range mockVersions {
146 versions = append(versions, &mockVersions[i])
147 }
148 helmChartVersions := getHelmChartVersions(name, versions)
149 model := ToHelmChartResponse(name, versions)
150 verifyHelmChartResponseModel(helmChartVersions, model, t)
151 }
152
153 func ToConvertTestUnstructuredToHelmReleases(resources *unstructured.UnstructuredList) ([]helmAPIv2beta1.HelmRelease, error) {
154 resp := make([]helmAPIv2beta1.HelmRelease, 0)
155 for _, item := range resources.Items {
156 converted := &helmAPIv2beta1.HelmRelease{}
157 err := runtime.DefaultUnstructuredConverter.
158 FromUnstructured(item.UnstructuredContent(), &converted)
159 if err != nil {
160 return nil, err
161 }
162 resp = append(resp, *converted)
163 }
164 return resp, nil
165 }
166
167 func TestConvertToConfigMapData(t *testing.T) {
168 res := []string{"{\"apiVersion\":\"v1\",\"data\":{\"banner.id\":\"3396a52c-6a22-4049-9593-5a63b596a100\",\"bsl.organization\":\"test-org\",\"cluster.edge.id\":\"3396a52c-6a22-4049-9593-5a63b596a201\",\"cluster.fleet\":\"fleet\",\"cluster.location\":\"region-infra\",\"cluster.name\":\"test-store\",\"cluster.type\":\"store\",\"edge.banner\":\"test-org\",\"edge.project.id\":\"test-org\",\"foreman.project.id\":\"topLevelProjectId\"},\"kind\":\"ConfigMap\",\"metadata\":{\"creationTimestamp\":null,\"name\":\"edge-info\",\"namespace\":\"kube-public\",\"resourceVersion\":\"1\"}}"}
169 configMaps, err := ConvertToConfigMapData(res)
170 assert.NoError(t, err)
171 assert.NotEmpty(t, configMaps)
172 assert.Equal(t, 10, len(configMaps))
173 }
174
175 func TestUpdateHelmWorkload(t *testing.T) {
176 helmWorkload := &model.HelmWorkload{
177 HelmEdgeID: "123456",
178 Name: "testHelmWorkload",
179 HelmChart: "test",
180 CreatedAt: "2022-10-19 17:34:04.174736+00",
181 UpdatedAt: "2022-10-19 17:34:04.174736+00",
182 HelmChartVersion: "2.8.0",
183 ConfigValues: nil,
184 Namespace: "testNamespace",
185 HelmRepoSecret: "testHelmRepoSecret",
186 InstalledBy: nil,
187 UpdateAvailable: nil,
188 UpgradeableVersions: nil,
189 DowngradeableVersions: nil,
190 Secrets: nil,
191 }
192 helmConfig := "{testHelmConfigString}"
193 helmWorkload.ConfigValues = &helmConfig
194
195 installedBy := ""
196 helmWorkload.InstalledBy = &installedBy
197
198 var versions []*string
199 mockVersions := []string{"3.0.0", "2.8.0", "0.1.4", "0.1.3"}
200 for i := range mockVersions {
201 versions = append(versions, &mockVersions[i])
202 }
203 var secrets []*model.HelmSecrets
204 mockSecrets := []model.HelmSecrets{
205 {
206 SecretEdgeID: "bbf70912-2c0a-4df1-a588-a1d16303be31",
207 Name: "test-secret1",
208 CreatedAt: "2022-10-19T13:34:56.458324-04:00",
209 UpdatedAt: "2022-10-19T13:34:56.458324-04:00",
210 },
211 {
212 SecretEdgeID: "bbf70912-2c0a-4df1-a588-a1d16303be31",
213 Name: "test-secret2",
214 CreatedAt: "2022-10-19T13:34:56.458324-04:00",
215 UpdatedAt: "2022-10-19T13:34:56.458324-04:00",
216 },
217 }
218 for i := range mockSecrets {
219 secrets = append(secrets, &mockSecrets[i])
220 }
221
222 resultHelmWorkload := UpdateHelmWorkload(helmWorkload, versions, secrets, nil, nil)
223 assert.Equal(t, resultHelmWorkload.Name, "testHelmWorkload")
224 assert.Equal(t, len(resultHelmWorkload.Secrets), 2)
225 assert.Equal(t, len(resultHelmWorkload.UpgradeableVersions), 1)
226 assert.Equal(t, len(resultHelmWorkload.DowngradeableVersions), 2)
227 }
228
229
230 func TestToCreateHelmReleasePostRendererPatchStrings(t *testing.T) {
231 type test struct {
232 testName string
233 installationType model.WorkloadInstallationType
234 expectedLen int
235 expectedPatchStr string
236 operator string
237 }
238
239 tests := []test{
240 {
241 testName: "installationTypeServerPreferred", expectedLen: 6, operator: "\"operator\":\"In\"", installationType: model.WorkloadInstallationTypeServerPreferred,
242 expectedPatchStr: "- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"weight\":1,\"preference\":{\"matchExpressions\":[{\"key\":\"node.ncr.com/class\",\"operator\":\"In\",\"values\":[\"server\"]}]}}]} }",
243 },
244 {
245 testName: "installationTypeAny", expectedLen: 6, operator: "\"operator\":\"In\"", installationType: model.WorkloadInstallationTypeAny,
246 expectedPatchStr: "- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"weight\":1,\"preference\":{\"matchExpressions\":[{\"key\":\"node.ncr.com/class\",\"operator\":\"In\",\"values\":[\"server\"]}]}}]} }",
247 },
248 {
249 testName: "installationTypeTouchPoint", expectedLen: 8, operator: "\"operator\":\"NotIn\"", installationType: model.WorkloadInstallationTypeTouchpoint,
250 expectedPatchStr: "- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"node.ncr.com/class\",\"operator\":\"NotIn\",\"values\":[\"server\"]}]}]}} }",
251 },
252 {
253 testName: "installationTypeServer", expectedLen: 8, operator: "\"operator\":\"In\"", installationType: model.WorkloadInstallationTypeServer,
254 expectedPatchStr: "- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"node.ncr.com/class\",\"operator\":\"In\",\"values\":[\"server\"]}]}]}} }",
255 },
256 {
257 testName: "installationTypeNoMod", expectedLen: 1, operator: "", installationType: model.WorkloadInstallationTypeNoMod,
258 expectedPatchStr: "",
259 },
260 {
261 testName: "installationTypeAdvanced", expectedLen: 1, operator: "", installationType: model.WorkloadInstallationTypeNoMod,
262 expectedPatchStr: "",
263 },
264 }
265
266 for _, tc := range tests {
267 t.Run(tc.testName, func(t *testing.T) {
268 helmRelease, err := ToCreateHelmRelease(name, helmRepository, helmChart, chartVersion, helmSecret, namespace, nil, tc.installationType, nil, "0.20", "")
269 assert.NoError(t, err)
270 assert.NotNil(t, helmRelease)
271 if tc.testName == "installationTypeServer" || tc.testName == "installationTypeTouchPoint" {
272 patchPersStrFromSpec := helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches[len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches)-1].Patch
273 assert.Contains(t, patchPersStrFromSpec, persistencePatch)
274 }
275 if tc.testName == "installationTypeNoMod" || tc.testName == "installationTypeAdvanced" {
276 assert.Equal(t, tc.expectedLen, len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers))
277 assert.Equal(t, []helmAPIv2beta1.PostRenderer{
278 {
279 Kustomize: &helmAPIv2beta1.Kustomize{
280 Patches: make([]kustomize.Patch, 0),
281 },
282 },
283 }, helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers)
284 } else {
285 patchStrFromSpec := helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches[0].Patch
286 assert.Equal(t, tc.expectedLen, len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches))
287 assert.Contains(t, patchStrFromSpec, tc.operator)
288 assert.Equal(t, tc.expectedPatchStr, patchStrFromSpec)
289 }
290 })
291 }
292 }
293
294 func TestToCreateHelmReleasePostRendererPatchStringsTypeAdvanced(t *testing.T) {
295 type test struct {
296 testName string
297 installationType model.WorkloadInstallationType
298 expectedLen int
299 expectedPatchStr string
300 operator string
301 }
302
303 customLabels := []*model.Label{
304 {Key: "custom-1", Description: "label 1"},
305 {Key: "custom-2", Description: "label 2"},
306 }
307
308 tests := []test{
309 {
310 testName: "installationTypeAdvanced", expectedLen: 8, operator: "Exists", installationType: model.WorkloadInstallationTypeAdvanced,
311 expectedPatchStr: "- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/custom-1\",\"operator\":\"Exists\"},{\"key\":\"custom.node.ncr.com/custom-2\",\"operator\":\"Exists\"}]}]}} }",
312 },
313 }
314
315 for _, tc := range tests {
316 t.Run(tc.testName, func(t *testing.T) {
317 helmRelease, err := ToCreateHelmRelease(name, helmRepository, helmChart, chartVersion, helmSecret, namespace, nil, tc.installationType, customLabels, "0.20", "")
318 assert.NoError(t, err)
319 assert.NotNil(t, helmRelease)
320
321 patchStrFromSpec := helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches[0].Patch
322 assert.Equal(t, tc.expectedLen, len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches))
323 assert.Contains(t, patchStrFromSpec, tc.operator)
324 assert.Equal(t, tc.expectedPatchStr, patchStrFromSpec)
325 patchPersStrFromSpec := helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches[len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches)-1].Patch
326 assert.Contains(t, patchPersStrFromSpec, persistencePatch)
327 })
328 }
329 }
330
331 func TestToCreateHelmReleasePostRendererPatchTargets(t *testing.T) {
332 type test struct {
333 testName string
334 installationType model.WorkloadInstallationType
335 expectedLen int
336 expectedVersion string
337 patchTargets []string
338 }
339
340 tests := []test{
341 {
342 testName: "installationTypeServerPreferred", expectedLen: 6, installationType: model.WorkloadInstallationTypeServerPreferred,
343 expectedVersion: "v1", patchTargets: []string{"Deployment", "StatefulSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine"},
344 },
345 {
346 testName: "installationTypeAdvanced", expectedLen: 1, installationType: model.WorkloadInstallationTypeAdvanced,
347 expectedVersion: "v1", patchTargets: []string{},
348 },
349 {
350 testName: "installationTypeAny", expectedLen: 6, installationType: model.WorkloadInstallationTypeAny,
351 expectedVersion: "v1", patchTargets: []string{"Deployment", "StatefulSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine"},
352 },
353 {
354 testName: "installationTypeTouchPoint", expectedLen: 8, installationType: model.WorkloadInstallationTypeTouchpoint,
355 expectedVersion: "v1", patchTargets: []string{"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
356 },
357 {
358 testName: "installationTypeServer", expectedLen: 8, installationType: model.WorkloadInstallationTypeServer,
359 expectedVersion: "v1", patchTargets: []string{"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
360 },
361 {
362 testName: "installationTypeNoMod", expectedLen: 1, installationType: model.WorkloadInstallationTypeNoMod,
363 expectedVersion: "", patchTargets: []string{},
364 },
365 }
366
367 for _, tc := range tests {
368 t.Run(tc.testName, func(t *testing.T) {
369 helmRelease, err := ToCreateHelmRelease(name, helmRepository, helmChart, chartVersion, helmSecret, namespace, nil, tc.installationType, nil, "0.20", "")
370 assert.NoError(t, err)
371 assert.NotNil(t, helmRelease)
372 if tc.testName == "installationTypeNoMod" || tc.testName == "installationTypeAdvanced" {
373 assert.Equal(t, tc.expectedLen, len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers))
374 assert.Equal(t, []helmAPIv2beta1.PostRenderer{
375 {
376 Kustomize: &helmAPIv2beta1.Kustomize{
377 Patches: make([]kustomize.Patch, 0),
378 },
379 },
380 }, helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers)
381 } else {
382 totalPatchesLen := len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches)
383
384 assert.Equal(t, tc.expectedLen, totalPatchesLen)
385 assert.Equal(t, len(tc.patchTargets), totalPatchesLen)
386 for _, patch := range helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches {
387 if patch.Target.Kind == persistence.Kind {
388 assert.Equal(t, persistence.GroupVersion.Version, patch.Target.Version)
389 assert.Contains(t, patch.Patch, "- op: replace\n path: /spec/nodeSelectorTerms\n value: ")
390 } else if patch.Target.Kind == "CronJob" {
391 assert.Equal(t, tc.expectedVersion, patch.Target.Version)
392 assert.Contains(t, patch.Patch, "- op: add\n path: /spec/jobTemplate/spec/template/spec/affinity\n value: {\"nodeAffinity\": ")
393 } else if patch.Target.Kind == "VirtualMachine" {
394 assert.Equal(t, tc.expectedVersion, patch.Target.Version)
395 assert.Equal(t, "kubevirt.io", patch.Target.Group)
396 assert.Contains(t, patch.Patch, "- op: add\n path: /spec/template/spec/affinity\n value:\n nodeAffinity:")
397 assert.Contains(t, patch.Patch, "- op: add\n path: /spec/template/metadata/annotations/edge.ncr.com~1helm-edge-id\n value: \"\"\n")
398 } else {
399 assert.Equal(t, tc.expectedVersion, patch.Target.Version)
400 assert.Contains(t, patch.Patch, "- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": ")
401 }
402 assert.Contains(t, tc.patchTargets, patch.Target.Kind)
403 }
404 }
405 })
406 }
407 }
408
409 func TestToCreateHelmReleasePostRendererPatchTargetsAdvanced(t *testing.T) {
410 type test struct {
411 testName string
412 installationType model.WorkloadInstallationType
413 expectedLen int
414 expectedVersion string
415 patchTargets []string
416 }
417
418 customLabels := []*model.Label{
419 {Key: "custom-1", Description: "label 1"},
420 {Key: "custom-2", Description: "label 2"},
421 }
422
423 tests := []test{
424 {
425 testName: "installationTypeAdvanced", expectedLen: 8, installationType: model.WorkloadInstallationTypeAdvanced,
426 expectedVersion: "v1", patchTargets: []string{"Deployment", "DaemonSet", "StatefulSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
427 },
428 }
429
430 for _, tc := range tests {
431 t.Run(tc.testName, func(t *testing.T) {
432 helmRelease, err := ToCreateHelmRelease(name, helmRepository, helmChart, chartVersion, helmSecret, namespace, nil, tc.installationType, customLabels, "0.20", "")
433 assert.NoError(t, err)
434 assert.NotNil(t, helmRelease)
435
436 totalPatchesLen := len(helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches)
437
438 assert.Equal(t, tc.expectedLen, totalPatchesLen)
439 assert.Equal(t, len(tc.patchTargets), totalPatchesLen)
440 for _, patch := range helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches {
441 if patch.Target.Kind == persistence.Kind {
442 assert.Equal(t, persistence.GroupVersion.Version, patch.Target.Version)
443 } else {
444 assert.Equal(t, tc.expectedVersion, patch.Target.Version)
445 }
446 assert.Contains(t, tc.patchTargets, patch.Target.Kind)
447 }
448 })
449 }
450 }
451
452 func TestToHelmVersion(t *testing.T) {
453 type test struct {
454 testName string
455 helmWorkloadQueryData types.HelmWorkloadQuery
456 helmWorkload *model.HelmWorkload
457 expectedLen int
458 }
459
460 helmConfig := "testHelmConfig"
461 installedBy := "testInstalledBy"
462
463 helmWorkloadQueryData := types.HelmWorkloadQuery{
464 ClusterEdgeID: "testClusterEdgeID",
465 HelmEdgeID: "be8536ff-d463-4aff-8fa9-fe81fec1ddc1",
466 Name: "test-helm-workload",
467 Namespace: "nginx",
468 HelmChart: "nginx",
469 HelmRepository: "test-repo",
470 HelmChartVersion: "2.3.1",
471 HelmConfig: &helmConfig,
472 InstalledBy: &installedBy,
473 WorkloadInstallationType: "mockInstallationType",
474 CreatedAt: "mockCreatedAt",
475 UpdatedAt: "mockUpdatedAt",
476 HelmRepoSecret: "test-helm-workload",
477 ProjectID: "mockProjectID",
478 }
479
480 helmWorkload := &model.HelmWorkload{
481 HelmEdgeID: helmWorkloadQueryData.HelmEdgeID,
482 HelmChart: helmWorkloadQueryData.HelmChart,
483 Name: helmWorkloadQueryData.Name,
484 HelmRepository: helmWorkloadQueryData.HelmRepository,
485 CreatedAt: helmWorkloadQueryData.CreatedAt,
486 UpdatedAt: helmWorkloadQueryData.UpdatedAt,
487 HelmChartVersion: helmWorkloadQueryData.HelmChartVersion,
488 ConfigValues: helmWorkloadQueryData.HelmConfig,
489 Namespace: helmWorkloadQueryData.Namespace,
490 HelmRepoSecret: helmWorkloadQueryData.HelmRepoSecret,
491 InstalledBy: helmWorkloadQueryData.InstalledBy,
492 InstallationType: &helmWorkloadQueryData.WorkloadInstallationType,
493 ClusterEdgeID: helmWorkloadQueryData.ClusterEdgeID,
494 }
495
496 tests := []test{
497 {
498 testName: "happyPath", expectedLen: 3, helmWorkloadQueryData: helmWorkloadQueryData, helmWorkload: helmWorkload,
499 },
500 }
501
502 for _, tc := range tests {
503 t.Run(tc.testName, func(t *testing.T) {
504 helmWorkload := ToHelmWorkload(tc.helmWorkloadQueryData)
505 assert.Equal(t, tc.helmWorkload, helmWorkload)
506 })
507 }
508 }
509
510 func TestToCreateHelmRelease(t *testing.T) {
511 helmEdgeID := "3cf1e89e-8338-4227-b93c-3d56a8075f83"
512 testcases := []struct {
513 title string
514 name string
515 helmRepository string
516 helmChart string
517 version string
518 secretManagerSecret string
519 namespace string
520 configMap []byte
521 installationType model.WorkloadInstallationType
522 customLabels []*model.Label
523 storeversion string
524 expected string
525 }{
526 {
527 title: "Test Case 1 - No Mod - 0.21",
528 name: "testOne",
529 helmRepository: "edge-test",
530 helmChart: "podinfo",
531 version: "1.0",
532 secretManagerSecret: "edge-helm",
533 namespace: "default",
534 configMap: nil,
535 installationType: model.WorkloadInstallationTypeNoMod,
536 customLabels: nil,
537 storeversion: "0.21",
538 expected: `{"kind":"HelmRelease","apiVersion":"helm.toolkit.fluxcd.io/v2","metadata":{"name":"testOne","namespace":"flux-system","creationTimestamp":null,"labels":{"parent-cluster":"region-infra","secret-mamager.edge.ncr.com":"edge-helm"}},"spec":{"chart":{"spec":{"chart":"podinfo","version":"1.0","sourceRef":{"kind":"HelmRepository","name":"edge-test"}}},"interval":"2m0s","releaseName":"testOne","targetNamespace":"default","timeout":"15m0s","driftDetection":{"mode":"enabled"},"install":{"remediation":{"retries":3}},"upgrade":{"remediation":{"retries":3}},"postRenderers":[{"kustomize":{"patches":[{"patch":"- op: add\n path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n value: disabled","target":{"version":"v1","kind":"IENPatch"}},{"patch":"- op: add\n path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n value: disabled","target":{"version":"v1","kind":"StatefulSet"}},{"patch":"- op: add\n path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n value: disabled","target":{"version":"v1","kind":"DaemonSet"}}]}}]},"status":{}}`,
539 },
540 {
541 title: "Test Case 2 - No Mod - 0.19 (No Patches)",
542 name: "testTwo",
543 helmRepository: "edge-test",
544 helmChart: "podinfo",
545 version: "1.0",
546 secretManagerSecret: "edge-helm",
547 namespace: "default",
548 configMap: nil,
549 installationType: model.WorkloadInstallationTypeNoMod,
550 customLabels: nil,
551 storeversion: "0.19",
552 expected: `{"kind":"HelmRelease","apiVersion":"helm.toolkit.fluxcd.io/v2beta1","metadata":{"name":"testTwo","namespace":"flux-system","creationTimestamp":null,"labels":{"parent-cluster":"region-infra","secret-mamager.edge.ncr.com":"edge-helm"}},"spec":{"chart":{"spec":{"chart":"podinfo","version":"1.0","sourceRef":{"kind":"HelmRepository","name":"edge-test"}}},"interval":"2m0s","releaseName":"testTwo","targetNamespace":"default","timeout":"15m0s","install":{"remediation":{"retries":3}},"upgrade":{"remediation":{"retries":3}},"postRenderers":[{"kustomize":{}}]},"status":{}}`,
553 },
554 {
555 title: "Test Case 3 - Advanced Install Type - 0.21 (Node Targeting & Drift Detection Patches)",
556 name: "testThree",
557 helmRepository: "edge-test",
558 helmChart: "podinfo",
559 version: "1.0",
560 secretManagerSecret: "edge-helm",
561 namespace: "default",
562 configMap: nil,
563 installationType: model.WorkloadInstallationTypeAdvanced,
564 customLabels: []*model.Label{
565 {
566 Key: "hello",
567 Description: "world",
568 },
569 },
570 storeversion: "0.21",
571 expected: `{"kind":"HelmRelease","apiVersion":"helm.toolkit.fluxcd.io/v2","metadata":{"name":"testThree","namespace":"flux-system","creationTimestamp":null,"labels":{"parent-cluster":"region-infra","secret-mamager.edge.ncr.com":"edge-helm"}},"spec":{"chart":{"spec":{"chart":"podinfo","version":"1.0","sourceRef":{"kind":"HelmRepository","name":"edge-test"}}},"interval":"2m0s","releaseName":"testThree","targetNamespace":"default","timeout":"15m0s","driftDetection":{"mode":"enabled"},"install":{"remediation":{"retries":3}},"upgrade":{"remediation":{"retries":3}},"postRenderers":[{"kustomize":{"patches":[{"patch":"- op: add\n path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n value: disabled","target":{"version":"v1","kind":"IENPatch"}},{"patch":"- op: add\n path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n value: disabled","target":{"version":"v1","kind":"StatefulSet"}},{"patch":"- op: add\n path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n value: disabled","target":{"version":"v1","kind":"DaemonSet"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"Deployment"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"StatefulSet"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"DaemonSet"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"ReplicaSet"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"Job"}},{"patch":"- op: add\n path: /spec/jobTemplate/spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"CronJob"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value:\n nodeAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n nodeSelectorTerms:\n - matchExpressions:\n - key: custom.node.ncr.com/hello\n operator: Exists\n- op: add\n path: /spec/template/metadata/annotations/edge.ncr.com~1helm-edge-id\n value: 3cf1e89e-8338-4227-b93c-3d56a8075f83\n","target":{"group":"kubevirt.io","version":"v1","kind":"VirtualMachine"}},{"patch":"- op: replace\n path: /spec/nodeSelectorTerms\n value: [{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]","target":{"group":"edge.ncr.com","version":"v1alpha1","kind":"Persistence"}}]}}]},"status":{}}`,
572 },
573 {
574 title: "Test Case 4 - Advanced Install Type - 0.19 (Node Targeting Patch)",
575 name: "testFour",
576 helmRepository: "edge-test",
577 helmChart: "podinfo",
578 version: "1.0",
579 secretManagerSecret: "edge-helm",
580 namespace: "default",
581 configMap: nil,
582 installationType: model.WorkloadInstallationTypeAdvanced,
583 customLabels: []*model.Label{
584 {
585 Key: "hello",
586 Description: "world",
587 },
588 },
589 storeversion: "0.19",
590 expected: `{"kind":"HelmRelease","apiVersion":"helm.toolkit.fluxcd.io/v2beta1","metadata":{"name":"testFour","namespace":"flux-system","creationTimestamp":null,"labels":{"parent-cluster":"region-infra","secret-mamager.edge.ncr.com":"edge-helm"}},"spec":{"chart":{"spec":{"chart":"podinfo","version":"1.0","sourceRef":{"kind":"HelmRepository","name":"edge-test"}}},"interval":"2m0s","releaseName":"testFour","targetNamespace":"default","timeout":"15m0s","install":{"remediation":{"retries":3}},"upgrade":{"remediation":{"retries":3}},"postRenderers":[{"kustomize":{"patches":[{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"Deployment"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"StatefulSet"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"DaemonSet"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"ReplicaSet"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"Job"}},{"patch":"- op: add\n path: /spec/jobTemplate/spec/template/spec/affinity\n value: {\"nodeAffinity\": {\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]}} }","target":{"version":"v1","kind":"CronJob"}},{"patch":"- op: add\n path: /spec/template/spec/affinity\n value:\n nodeAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n nodeSelectorTerms:\n - matchExpressions:\n - key: custom.node.ncr.com/hello\n operator: Exists\n- op: add\n path: /spec/template/metadata/annotations/edge.ncr.com~1helm-edge-id\n value: 3cf1e89e-8338-4227-b93c-3d56a8075f83\n","target":{"group":"kubevirt.io","version":"v1","kind":"VirtualMachine"}},{"patch":"- op: replace\n path: /spec/nodeSelectorTerms\n value: [{\"matchExpressions\":[{\"key\":\"custom.node.ncr.com/hello\",\"operator\":\"Exists\"}]}]","target":{"group":"edge.ncr.com","version":"v1alpha1","kind":"Persistence"}}]}}]},"status":{}}`,
591 },
592 }
593 for _, testcase := range testcases {
594 t.Run(testcase.title, func(t *testing.T) {
595 hr, err := ToCreateHelmRelease(testcase.name, testcase.helmRepository, testcase.helmChart, testcase.version, testcase.secretManagerSecret, testcase.namespace, testcase.configMap, testcase.installationType, testcase.customLabels, testcase.storeversion, helmEdgeID)
596 assert.NoError(t, err)
597 res, err := json.Marshal(hr)
598 assert.NoError(t, err)
599 assert.Equal(t, testcase.expected, string(res))
600 })
601 }
602 }
603
604 func TestVirtualMachinePatches(t *testing.T) {
605 helmEdgeID := "c014b82f-c5c8-4186-8bba-1ca89fb28793"
606 helmRelease, err := ToCreateHelmRelease(name, helmRepository, helmChart, chartVersion, helmSecret, namespace, nil, model.WorkloadInstallationTypeServer, nil, "0.20", helmEdgeID)
607 assert.NoError(t, err)
608 assert.NotNil(t, helmRelease)
609 patchList := helmRelease.(*helmAPIv2beta1.HelmRelease).Spec.PostRenderers[0].Kustomize.Patches
610 expectedTarget := kustomize.Selector{
611 Kind: "VirtualMachine",
612 Group: virtv1.GroupVersion.Group,
613 Version: virtv1.GroupVersion.Version,
614 }
615 expectedPatch := fmt.Sprintf(`- op: add
616 path: /spec/template/spec/affinity
617 value:
618 nodeAffinity:
619 requiredDuringSchedulingIgnoredDuringExecution:
620 nodeSelectorTerms:
621 - matchExpressions:
622 - key: node.ncr.com/class
623 operator: In
624 values:
625 - server
626 - op: add
627 path: /spec/template/metadata/annotations/edge.ncr.com~1helm-edge-id
628 value: %s
629 `, helmEdgeID)
630 assert.True(t, slices.ContainsFunc(patchList, func(patch kustomize.Patch) bool {
631 return patch.Target != nil && *patch.Target == expectedTarget && patch.Patch == expectedPatch
632 }))
633 }
634
View as plain text