1 package mapper
2
3 import (
4 "encoding/json"
5 "strconv"
6 "time"
7
8 "helm.sh/helm/v3/pkg/repo"
9 "sigs.k8s.io/controller-runtime/pkg/client"
10
11 helmApiV2 "github.com/fluxcd/helm-controller/api/v2"
12 helmApiV2beta1 "github.com/fluxcd/helm-controller/api/v2beta1"
13 "github.com/fluxcd/pkg/apis/kustomize"
14 goVersion "github.com/hashicorp/go-version"
15 corev1 "k8s.io/api/core/v1"
16 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
19 "edge-infra.dev/pkg/edge/constants"
20 "edge-infra.dev/pkg/edge/flux/bootstrap"
21
22 "edge-infra.dev/pkg/edge/api/graph/model"
23 "edge-infra.dev/pkg/edge/api/status"
24 "edge-infra.dev/pkg/edge/api/types"
25 "edge-infra.dev/pkg/edge/api/utils"
26 )
27
28 func ToHelmRepositoryKeyValues(name string, url string, username *string, password *string) []*model.KeyValues {
29 kv := []*model.KeyValues{
30 {Key: constants.HelmURL, Value: url},
31 {Key: constants.HelmRepoName, Value: name},
32 }
33 if username != nil {
34 kv = append(kv, &model.KeyValues{Key: constants.HelmUsername, Value: *username})
35 }
36 if password != nil {
37 kv = append(kv, &model.KeyValues{Key: constants.HelmPassword, Value: *password})
38 }
39 return kv
40 }
41
42 func ToHelmRepositoriesModels(helmRepos []*model.SecretManagerResponse) []*model.HelmRepository {
43 helmRepositoryModels := make([]*model.HelmRepository, 0)
44 for _, helmRepo := range helmRepos {
45 helmRepositoryModels = append(helmRepositoryModels, ToHelmRepositoriesModel(helmRepo))
46 }
47 return helmRepositoryModels
48 }
49
50 func ToHelmRepositoriesModel(hs *model.SecretManagerResponse) *model.HelmRepository {
51 repoName, _, _, repoURL := GetHelmSecretData(hs)
52 hr := &model.HelmRepository{
53 Name: repoName,
54 URL: repoURL,
55 Secret: hs.Name,
56 }
57 if hs.Workload != nil {
58 hr.Workload = *hs.Workload
59 }
60 if hs.Created != nil {
61 hr.CreatedOn = *hs.Created
62 }
63 return hr
64 }
65
66 func GetHelmSecretData(smr *model.SecretManagerResponse) (name string, username string, password string, repoURL string) {
67 for _, keyValues := range smr.Values {
68 if keyValues.Key == constants.HelmUsername {
69 username = keyValues.Value
70 } else if keyValues.Key == constants.HelmPassword {
71 password = keyValues.Value
72 } else if keyValues.Key == constants.HelmURL {
73 repoURL = keyValues.Value
74 } else if keyValues.Key == constants.HelmRepoName {
75 name = keyValues.Value
76 }
77 }
78 return
79 }
80
81 func ToHelmReleasesModels(helmReleases []helmApiV2beta1.HelmRelease, helmReleasesData map[string][]*string) []*model.HelmRelease {
82 helmReleaseModels := make([]*model.HelmRelease, 0)
83 for _, helmRelease := range helmReleases {
84 versions := helmReleasesData[helmRelease.Spec.Chart.Spec.Chart]
85 helmReleaseModels = append(helmReleaseModels, ToHelmReleasesModel(&helmRelease, versions))
86 }
87 return helmReleaseModels
88 }
89
90 func ToHelmReleasesStatusModels(helmReleases []helmApiV2beta1.HelmRelease) []*model.HelmReleaseStatus {
91 helmReleaseModels := make([]*model.HelmReleaseStatus, 0)
92 for _, helmRelease := range helmReleases {
93 helmReleaseModels = append(helmReleaseModels, ToHelmReleasesStatusModel(&helmRelease))
94 }
95 return helmReleaseModels
96 }
97
98 func ToHelmReleasesStatusModel(h *helmApiV2beta1.HelmRelease) *model.HelmReleaseStatus {
99 conditions := h.Status.Conditions
100 lastUpdateTime := ""
101 helmStatus := ""
102 installedCondition := &model.HelmCondition{}
103 readyCondition := &model.HelmCondition{}
104 var config string
105
106 if h.Spec.Values != nil {
107 config = convertConfigJSONtoYAML(h)
108 }
109
110 if len(conditions) > 0 {
111 lastUpdateTime = conditions[len(conditions)-1].LastTransitionTime.Format(TimeFormat)
112 helmStatus = ToHelmStatus(string(conditions[len(conditions)-1].Status))
113 installedCondition = ToHelmConditionModel(conditions, helmApiV2.ReleasedCondition)
114 readyCondition = ToHelmConditionModel(conditions, "Ready")
115 }
116
117 var versionInstalled string
118 if h.Status.History.Latest() != nil {
119 versionInstalled = h.Status.History.Latest().ChartVersion
120 }
121 helmReleaseStatusModel := &model.HelmReleaseStatus{
122 Name: h.Name,
123 LastActionTime: lastUpdateTime,
124 StatusType: helmStatus,
125 VersionInstalled: versionInstalled,
126 VersionRequested: h.Spec.Chart.Spec.Version,
127 InstallCondition: installedCondition,
128 ReadyCondition: readyCondition,
129 ConfigValues: &config,
130 }
131
132 if h.Spec.Values != nil {
133 jsonStr := string(h.Spec.Values.Raw)
134
135 if jsonStr != "" {
136 configMapYaml, _ := utils.JSONToYAML(&jsonStr)
137 yamlStr := string(configMapYaml)
138 helmReleaseStatusModel.ConfigValues = &yamlStr
139 }
140 }
141
142 return helmReleaseStatusModel
143 }
144
145 func ToHelmReleasesModel(h *helmApiV2beta1.HelmRelease, versions []*string) *model.HelmRelease {
146 conditions := h.Status.Conditions
147 lastUpdateTime := ""
148 helmStatus := ""
149 installedCondition := &model.HelmCondition{}
150 readyCondition := &model.HelmCondition{}
151 var config string
152
153 if h.Spec.Values != nil {
154 config = convertConfigJSONtoYAML(h)
155 }
156
157 if len(conditions) > 0 {
158 lastUpdateTime = conditions[len(conditions)-1].LastTransitionTime.Format(TimeFormat)
159 helmStatus = ToHelmStatus(string(conditions[len(conditions)-1].Status))
160 installedCondition = ToHelmConditionModel(conditions, helmApiV2.ReleasedCondition)
161 readyCondition = ToHelmConditionModel(conditions, "Ready")
162 }
163 upgradeableVersions, downgradeableVersions, updateAvailable := ToHelmVersion(versions, h.Spec.Chart.Spec.Version)
164 var versionInstalled string
165 if h.Status.History.Latest() != nil {
166 versionInstalled = h.Status.History.Latest().ChartVersion
167 }
168 helmReleaseModel := &model.HelmRelease{
169 Name: h.Name,
170 HelmChart: h.Spec.Chart.Spec.Chart,
171 LastActionTime: lastUpdateTime,
172 StatusType: helmStatus,
173 VersionInstalled: versionInstalled,
174 VersionRequested: h.Spec.Chart.Spec.Version,
175 InstallCondition: installedCondition,
176 ReadyCondition: readyCondition,
177 Namespace: h.Spec.TargetNamespace,
178 UpgradeableVersions: upgradeableVersions,
179 DowngradeableVersions: downgradeableVersions,
180 UpdateAvailable: &updateAvailable,
181 ConfigValues: &config,
182 }
183
184 if h.Spec.Values != nil {
185 jsonStr := string(h.Spec.Values.Raw)
186 if jsonStr != "" {
187 configMapYaml, _ := utils.JSONToYAML(&jsonStr)
188 yamlStr := string(configMapYaml)
189 helmReleaseModel.ConfigValues = &yamlStr
190 }
191 }
192
193 return helmReleaseModel
194 }
195
196 func ToHelmStatus(statusType string) string {
197 status := "Unknown"
198 if statusType == "True" {
199 status = "Succeeded"
200 } else if statusType == "False" {
201 status = "Failed"
202 }
203 return status
204 }
205
206
207 func ToTrimedValue(value string) string {
208 if len(value) > 0 && value[0] == '"' {
209 return value[1 : len(value)-1]
210 }
211 return value
212 }
213
214 func ToPodStatus(value string) string {
215 switch value {
216 case "True":
217 return status.Running
218 case "False":
219 return status.Error
220 default:
221 return value
222 }
223 }
224
225 func ToHelmConditionModel(conditions []metav1.Condition, s string) *model.HelmCondition {
226 for i, item := range conditions {
227 if item.Type == s {
228 status := string(item.Status)
229 statusBool, _ := strconv.ParseBool(status)
230 ltt := item.LastTransitionTime.Format(TimeFormat)
231 return &model.HelmCondition{
232 LastTransitionTime: <t,
233 Message: &conditions[i].Message,
234 Reason: &conditions[i].Reason,
235 Status: &status,
236 Type: &conditions[i].Type,
237 Installed: &statusBool,
238 Ready: &statusBool,
239 }
240 }
241 }
242 return nil
243 }
244
245 func ToHelmVersion(versions []*string, installedVersion string) ([]*model.HelmVersion, []*model.HelmVersion, bool) {
246 if len(versions) > 0 {
247 upgradeableVersions := make([]*model.HelmVersion, 0)
248 downgradeableVersions := make([]*model.HelmVersion, 0)
249 var updateAvailable = false
250 for _, version := range versions {
251 if compareHelmVersions(*version, installedVersion) {
252 updateAvailable = true
253 upgradeableVersions = append(upgradeableVersions, &model.HelmVersion{
254 Version: *version,
255 })
256 } else if *version != installedVersion {
257 downgradeableVersions = append(downgradeableVersions, &model.HelmVersion{
258 Version: *version,
259 })
260 }
261 }
262 return upgradeableVersions, downgradeableVersions, updateAvailable
263 }
264 return nil, nil, false
265 }
266
267 func compareHelmVersions(versionOne string, versionTwo string) bool {
268 v1, err := goVersion.NewVersion(versionOne)
269 if err != nil {
270 return false
271 }
272 v2, err := goVersion.NewVersion(versionTwo)
273 if err != nil {
274 return false
275 }
276 return v1.GreaterThan(v2)
277 }
278
279 func getHelmReleasePostRenderer(installationType model.WorkloadInstallationType, customLabels []*model.Label, helmEdgeID string) (interface{}, error) {
280 helmReleasePostRenderer := &HelmReleasePostRenderer{
281 installationType: installationType,
282 customLabels: customLabels,
283 helmEdgeID: helmEdgeID,
284 }
285 return helmReleasePostRenderer.CreatePostrender()
286 }
287
288 func ToCreateHelmRelease(name, helmRepository, helmChart, version, secretManagerSecret string, namespace string, configMap []byte, installationType model.WorkloadInstallationType, customLabels []*model.Label, clusterVersion string, helmEdgeID string) (client.Object, error) {
289 var (
290 labels = map[string]string{
291 "parent-cluster": Region,
292 SecretManagerSecretLabel: secretManagerSecret,
293 }
294 helmRelease client.Object
295 )
296 supportsFluxV2 := bootstrap.SupportsFluxV024(clusterVersion)
297
298 if supportsFluxV2 {
299 hr := &helmApiV2.HelmRelease{
300 TypeMeta: metav1.TypeMeta{
301 APIVersion: helmApiV2.GroupVersion.String(),
302 Kind: "HelmRelease",
303 },
304 ObjectMeta: metav1.ObjectMeta{
305 Name: name,
306 Namespace: HelmReleaseNamespace,
307 Labels: labels,
308 },
309 Spec: helmApiV2.HelmReleaseSpec{
310 Interval: metav1.Duration{
311 Duration: time.Duration(2) * time.Minute,
312 },
313 ReleaseName: name,
314 TargetNamespace: namespace,
315 Chart: &helmApiV2.HelmChartTemplate{
316 Spec: helmApiV2.HelmChartTemplateSpec{
317 Chart: helmChart,
318 Version: version,
319 SourceRef: helmApiV2.CrossNamespaceObjectReference{
320 Kind: "HelmRepository",
321 Name: helmRepository,
322 },
323 },
324 },
325 Timeout: &metav1.Duration{
326 Duration: time.Duration(HelmReleaseTimeout) * time.Minute,
327 },
328 DriftDetection: &helmApiV2.DriftDetection{
329 Mode: helmApiV2.DriftDetectionEnabled,
330 },
331 Install: &helmApiV2.Install{
332 Remediation: &helmApiV2.InstallRemediation{
333 Retries: 3,
334 },
335 },
336 Upgrade: &helmApiV2.Upgrade{
337 Remediation: &helmApiV2.UpgradeRemediation{
338 Retries: 3,
339 },
340 },
341 },
342 }
343
344 postRenderers := []helmApiV2.PostRenderer{
345 {
346 Kustomize: &helmApiV2.Kustomize{
347 Patches: disableDriftDetectionForProblemKinds(),
348 },
349 },
350 }
351 if installationType == model.WorkloadInstallationTypeAdvanced {
352 if len(customLabels) > 0 {
353 patches, err := getHelmReleasePostRenderer(installationType, customLabels, helmEdgeID)
354 if err != nil {
355 return nil, err
356 }
357 postRenderers[0].Kustomize.Patches = append(postRenderers[0].Kustomize.Patches, patches.([]kustomize.Patch)...)
358 }
359 } else if installationType != model.WorkloadInstallationTypeNoMod {
360 patches, err := getHelmReleasePostRenderer(installationType, nil, helmEdgeID)
361 if err != nil {
362 return nil, err
363 }
364 postRenderers[0].Kustomize.Patches = append(postRenderers[0].Kustomize.Patches, patches.([]kustomize.Patch)...)
365 }
366 hr.Spec.PostRenderers = postRenderers
367 if configMap != nil {
368 configMapRaw := &apiextensionsv1.JSON{
369 Raw: configMap,
370 }
371
372 hr.Spec.Values = configMapRaw
373 }
374 helmRelease = hr
375 } else {
376 hr := &helmApiV2beta1.HelmRelease{
377 TypeMeta: metav1.TypeMeta{
378 APIVersion: helmApiV2beta1.GroupVersion.String(),
379 Kind: "HelmRelease",
380 },
381 ObjectMeta: metav1.ObjectMeta{
382 Name: name,
383 Namespace: HelmReleaseNamespace,
384 Labels: labels,
385 },
386 Spec: helmApiV2beta1.HelmReleaseSpec{
387 Interval: metav1.Duration{
388 Duration: time.Duration(2) * time.Minute,
389 },
390 ReleaseName: name,
391 TargetNamespace: namespace,
392 Chart: &helmApiV2beta1.HelmChartTemplate{
393 Spec: helmApiV2beta1.HelmChartTemplateSpec{
394 Chart: helmChart,
395 Version: version,
396 SourceRef: helmApiV2beta1.CrossNamespaceObjectReference{
397 Kind: "HelmRepository",
398 Name: helmRepository,
399 },
400 },
401 },
402 Timeout: &metav1.Duration{
403 Duration: time.Duration(HelmReleaseTimeout) * time.Minute,
404 },
405 Install: &helmApiV2beta1.Install{
406 Remediation: &helmApiV2beta1.InstallRemediation{
407 Retries: 3,
408 },
409 },
410 Upgrade: &helmApiV2beta1.Upgrade{
411 Remediation: &helmApiV2beta1.UpgradeRemediation{
412 Retries: 3,
413 },
414 },
415 },
416 }
417 postRenderers := []helmApiV2beta1.PostRenderer{
418 {
419 Kustomize: &helmApiV2beta1.Kustomize{
420 Patches: make([]kustomize.Patch, 0),
421 },
422 },
423 }
424 if installationType == model.WorkloadInstallationTypeAdvanced {
425 if len(customLabels) > 0 {
426 patches, err := getHelmReleasePostRenderer(installationType, customLabels, helmEdgeID)
427 if err != nil {
428 return nil, err
429 }
430 postRenderers[0].Kustomize.Patches = append(postRenderers[0].Kustomize.Patches, patches.([]kustomize.Patch)...)
431 }
432 } else if installationType != model.WorkloadInstallationTypeNoMod {
433 patches, err := getHelmReleasePostRenderer(installationType, nil, helmEdgeID)
434 if err != nil {
435 return nil, err
436 }
437 postRenderers[0].Kustomize.Patches = append(postRenderers[0].Kustomize.Patches, patches.([]kustomize.Patch)...)
438 }
439 hr.Spec.PostRenderers = postRenderers
440 if configMap != nil {
441 configMapRaw := &apiextensionsv1.JSON{
442 Raw: configMap,
443 }
444
445 hr.Spec.Values = configMapRaw
446 }
447 helmRelease = hr
448 }
449 return helmRelease, nil
450 }
451
452 func ToBase64StringFromHelmRelease(helmRelease client.Object) (string, error) {
453 helmReleaseByte, err := json.Marshal(helmRelease)
454 if err != nil {
455 return "", err
456 }
457 helmReleaseBase64 := utils.ToBase64(helmReleaseByte)
458 return helmReleaseBase64, nil
459 }
460
461 func ToBase64StringFromHelmRepository(helmRepository client.Object) (string, error) {
462 helmRepositoryByte, err := json.Marshal(helmRepository)
463 if err != nil {
464 return "", err
465 }
466 helmRepositoryBase64 := utils.ToBase64(helmRepositoryByte)
467 return helmRepositoryBase64, nil
468 }
469
470 func ToCreateHelmReleaseBase64String(name, helmRepository, helmChart, version, secretManagerSecret, namespace string, configMap []byte, installationType model.WorkloadInstallationType, customLabels []*model.Label, clusterVersion string, helmEdgeID string) (string, error) {
471 helmRelease, err := ToCreateHelmRelease(name, helmRepository, helmChart, version, secretManagerSecret, namespace, configMap, installationType, customLabels, clusterVersion, helmEdgeID)
472 if err != nil {
473 return "", err
474 }
475 return ToBase64StringFromHelmRelease(helmRelease)
476 }
477
478 func ToHelmChartResponse(name string, versions []*string) *model.HelmChartResponse {
479 return &model.HelmChartResponse{
480 Name: name,
481 Versions: versions,
482 }
483 }
484
485 func ToHelmChartsFromIndex(index *repo.IndexFile) []*model.HelmChart {
486 helmChartModels := make([]*model.HelmChart, 0)
487 if index == nil || index.Entries == nil {
488 return helmChartModels
489 }
490 for _, entries := range index.Entries {
491 helmChart := model.HelmChart{}
492 if !utils.IsNullOrEmpty(&entries[0].Name) {
493 helmChart.Name = entries[0].Name
494 }
495
496 if !utils.IsNullOrEmpty(&entries[0].Description) {
497 helmChart.Description = entries[0].Description
498 }
499
500 if !utils.IsNullOrEmpty(&entries[0].Version) {
501 helmChart.Version = entries[0].Version
502 }
503
504 if !utils.IsNullOrEmpty(&entries[0].AppVersion) {
505 helmChart.AppVersion = entries[0].AppVersion
506 }
507
508 if !utils.IsNullOrEmpty(&entries[0].Icon) {
509 helmChart.Icon = entries[0].Icon
510 }
511 if entries[0].Keywords != nil {
512 helmChart.Keywords = entries[0].Keywords
513 }
514 if entries[0].Sources != nil {
515 helmChart.Sources = entries[0].Sources
516 }
517 if entries[0].URLs != nil {
518 helmChart.Urls = entries[0].URLs
519 }
520 helmChart.Created = entries[0].Created.String()
521 helmChartModels = append(helmChartModels, &helmChart)
522 }
523 return helmChartModels
524 }
525
526 func convertConfigJSONtoYAML(h *helmApiV2beta1.HelmRelease) string {
527 configValuesJSON := h.Spec.Values
528 configValues := configValuesJSON.Raw
529 config := string(configValues)
530 configYaml, _ := utils.JSONToYAML(&config)
531 config = string(configYaml)
532 return config
533 }
534
535 func ConvertToConfigMapData(res []string) ([]*model.ConfigmapData, error) {
536 var configmaps = make([]*model.ConfigmapData, 0)
537 for i := range res {
538 configmap := &corev1.ConfigMap{}
539 err := json.Unmarshal([]byte(res[i]), configmap)
540 if err != nil {
541 return nil, err
542 }
543 for key, value := range configmap.Data {
544 key := key
545 value := value
546 configmaps = append(configmaps, &model.ConfigmapData{
547 Key: &key,
548 Value: &value,
549 })
550 }
551 }
552 return configmaps, nil
553 }
554
555 func ConvertToConfigMap(res []string) (*corev1.ConfigMap, error) {
556 configmap := &corev1.ConfigMap{}
557 err := json.Unmarshal([]byte(res[0]), configmap)
558 return configmap, err
559 }
560
561 func UpdateHelmWorkload(helmWorkload *model.HelmWorkload, versions []*string, secrets []*model.HelmSecrets, maps []*model.HelmWorkloadConfigmaps, labels []*model.Label) *model.HelmWorkload {
562 upgradeableVersions, downgradeableVersions, updateAvailable := ToHelmVersion(versions, helmWorkload.HelmChartVersion)
563 helmWorkload.UpdateAvailable = &updateAvailable
564 helmWorkload.UpgradeableVersions = upgradeableVersions
565 helmWorkload.DowngradeableVersions = downgradeableVersions
566 helmWorkload.Secrets = secrets
567 helmWorkload.Configmaps = maps
568 helmWorkload.Labels = labels
569
570 return helmWorkload
571 }
572
573 func ToHelmWorkload(helmWorkloadsData types.HelmWorkloadQuery) *model.HelmWorkload {
574 return &model.HelmWorkload{
575 HelmEdgeID: helmWorkloadsData.HelmEdgeID,
576 HelmChart: helmWorkloadsData.HelmChart,
577 Name: helmWorkloadsData.Name,
578 HelmRepository: helmWorkloadsData.HelmRepository,
579 CreatedAt: helmWorkloadsData.CreatedAt,
580 UpdatedAt: helmWorkloadsData.UpdatedAt,
581 HelmChartVersion: helmWorkloadsData.HelmChartVersion,
582 ConfigValues: helmWorkloadsData.HelmConfig,
583 Namespace: helmWorkloadsData.Namespace,
584 ClusterEdgeID: helmWorkloadsData.ClusterEdgeID,
585 BannerEdgeID: helmWorkloadsData.BannerEdgeID,
586 HelmRepoSecret: helmWorkloadsData.HelmRepoSecret,
587 InstalledBy: helmWorkloadsData.InstalledBy,
588 InstallationType: &helmWorkloadsData.WorkloadInstallationType,
589 Deleted: helmWorkloadsData.Deleted,
590 }
591 }
592
View as plain text