...

Source file src/edge-infra.dev/pkg/edge/api/graph/mapper/helm_mapper_resolvers_test.go

Documentation: edge-infra.dev/pkg/edge/api/graph/mapper

     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  // Test ToHelmRepositoriesModels
    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  // Test ToHelmReleasesModels
    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, "") //"2.0.1"
    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  // Test ToHelmReleasesStatusModels
    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, "") //2.0.1
    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  // Test ToHelmStatus: testing if true
    89  func TestToHelmStatusTrue(t *testing.T) {
    90  	result := ToHelmStatus("True")
    91  	assert.Equal(t, result, "Succeeded")
    92  }
    93  
    94  // Test ToHelmStatus: testing if false
    95  func TestToHelmStatusFail(t *testing.T) {
    96  	result := ToHelmStatus("False")
    97  	assert.Equal(t, result, "Failed")
    98  }
    99  
   100  // Test ToPodStatus: testing if true
   101  func TestToPodStatusTrue(t *testing.T) {
   102  	result := ToPodStatus("True")
   103  	assert.Equal(t, result, status.Running)
   104  }
   105  
   106  // Test ToPodStatus: testing if false
   107  func TestToPodStatusFalse(t *testing.T) {
   108  	result := ToPodStatus("False")
   109  	assert.Equal(t, result, status.Error)
   110  }
   111  
   112  // Test ToCreateHelmRelease
   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  // Test ToCreateHelmRelease: with namespace field
   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  // Test ToCreateHelmRelease: with namespace and configMap field
   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  // Test ToHelmChartResponse
   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]) //nolint
   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  // Test Post Renderer Specs for Helm Release Object
   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" { //nolint:nestif
   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  				// patch target length should match list of expected Patch Targets
   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  			// patch target length should match list of expected Patch Targets
   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