github.com/dhaiducek/policy-generator-plugin@v1.99.99/internal/utils_test.go (about)

     1  // Copyright Contributors to the Open Cluster Management project
     2  package internal
     3  
     4  import (
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"reflect"
    13  	"testing"
    14  
    15  	"github.com/dhaiducek/policy-generator-plugin/internal/types"
    16  	"github.com/google/go-cmp/cmp"
    17  	"github.com/google/go-cmp/cmp/cmpopts"
    18  	"gopkg.in/yaml.v3"
    19  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    20  )
    21  
    22  func assertEqual(t *testing.T, a interface{}, b interface{}) {
    23  	t.Helper()
    24  
    25  	diff := cmp.Diff(a, b, cmpopts.EquateErrors())
    26  
    27  	if diff != "" {
    28  		t.Fatalf(diff)
    29  	}
    30  }
    31  
    32  func assertEqualYaml(t *testing.T, a, b []byte) {
    33  	t.Helper()
    34  
    35  	aDec := yaml.NewDecoder(bytes.NewReader(a))
    36  	bDec := yaml.NewDecoder(bytes.NewReader(b))
    37  
    38  	for {
    39  		aOut := map[string]interface{}{}
    40  		aErr := aDec.Decode(aOut)
    41  
    42  		bOut := map[string]interface{}{}
    43  		bErr := bDec.Decode(bOut)
    44  
    45  		aDone := errors.Is(aErr, io.EOF)
    46  		bDone := errors.Is(bErr, io.EOF)
    47  
    48  		if aDone && bDone {
    49  			return // both inputs had the same number of documents
    50  		} else if aDone && !bDone {
    51  			t.Fatalf("extra yaml doc in second input")
    52  		} else if !aDone && bDone {
    53  			t.Fatalf("missing yaml doc in second input")
    54  		}
    55  
    56  		assertEqual(t, aErr, bErr)
    57  		assertEqual(t, aOut, bOut)
    58  	}
    59  }
    60  
    61  func assertReflectEqual(t *testing.T, a interface{}, b interface{}) {
    62  	t.Helper()
    63  
    64  	if !reflect.DeepEqual(a, b) {
    65  		t.Fatalf("%s != %s", a, b)
    66  	}
    67  }
    68  
    69  func assertSelectorEqual(t *testing.T, a map[string]interface{}, b types.NamespaceSelector) {
    70  	t.Helper()
    71  
    72  	if !compareSelectors(a, b) {
    73  		t.Fatalf("%s != %s", a, b)
    74  	}
    75  }
    76  
    77  func compareStringArrays(a []interface{}, b []string) bool {
    78  	// Account for when b is []string(nil)
    79  	if len(a) == 0 && len(b) == 0 {
    80  		return true
    81  	}
    82  
    83  	// Create a string array from []interface{}
    84  	aTyped := make([]string, len(a))
    85  	for i, val := range a {
    86  		aTyped[i] = val.(string)
    87  	}
    88  
    89  	return reflect.DeepEqual(aTyped, b)
    90  }
    91  
    92  func compareSelectors(a map[string]interface{}, b types.NamespaceSelector) bool {
    93  	if includeA, ok := a["include"].([]interface{}); ok {
    94  		if !compareStringArrays(includeA, b.Include) {
    95  			return false
    96  		}
    97  	} else if len(b.Include) != 0 {
    98  		return false
    99  	}
   100  
   101  	if excludeA, ok := a["exclude"].([]interface{}); ok {
   102  		if !compareStringArrays(excludeA, b.Exclude) {
   103  			return false
   104  		}
   105  	} else if len(b.Exclude) != 0 {
   106  		return false
   107  	}
   108  
   109  	if matchLabelsA, ok := a["matchLabels"].(map[string]string); ok {
   110  		if !reflect.DeepEqual(matchLabelsA, b.MatchLabels) {
   111  			return false
   112  		}
   113  	} else if matchLabelsA != nil && b.MatchLabels != nil {
   114  		return false
   115  	}
   116  
   117  	if matchExpressionsA, ok := a["matchExpressions"].([]interface{}); ok {
   118  		if a["matchExpressions"] != b.MatchExpressions {
   119  			if b.MatchExpressions == nil {
   120  				return false
   121  			}
   122  
   123  			if len(matchExpressionsA) != len(*b.MatchExpressions) {
   124  				return false
   125  			}
   126  
   127  			for i := range matchExpressionsA {
   128  				meA := matchExpressionsA[i]
   129  				valuesA := meA.(map[string]interface{})["values"].([]interface{})
   130  				meB := (*b.MatchExpressions)[i]
   131  
   132  				if meA.(map[string]interface{})["key"].(string) != meB.Key ||
   133  					meA.(map[string]interface{})["operator"].(string) != string(meB.Operator) ||
   134  					!compareStringArrays(valuesA, meB.Values) {
   135  					return false
   136  				}
   137  			}
   138  		}
   139  	} else if matchExpressionsA != nil && b.MatchExpressions != nil {
   140  		return false
   141  	}
   142  
   143  	return true
   144  }
   145  
   146  func TestGetPolicyTemplate(t *testing.T) {
   147  	t.Parallel()
   148  	tmpDir := t.TempDir()
   149  	manifestFiles := []types.Manifest{}
   150  	manifestFilesMustNotHave := []types.Manifest{}
   151  
   152  	for i, enemy := range []string{"goldfish", "potato"} {
   153  		manifestPath := path.Join(tmpDir, fmt.Sprintf("configmap%d.yaml", i))
   154  		manifestYAML := fmt.Sprintf(
   155  			`
   156  apiVersion: v1
   157  kind: ConfigMap
   158  metadata:
   159    name: my-configmap
   160  data:
   161    game.properties: enemies=%s
   162  `,
   163  			enemy,
   164  		)
   165  
   166  		err := os.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
   167  		if err != nil {
   168  			t.Fatalf("Failed to write %s", manifestPath)
   169  		}
   170  
   171  		// The applyDefaults method would normally fill in ComplianceType on each manifest entry.
   172  		manifestFiles = append(
   173  			manifestFiles, types.Manifest{
   174  				ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   175  					ComplianceType:         "musthave",
   176  					MetadataComplianceType: "mustonlyhave",
   177  				},
   178  				Path: manifestPath,
   179  			},
   180  		)
   181  
   182  		manifestFilesMustNotHave = append(
   183  			manifestFilesMustNotHave,
   184  			types.Manifest{
   185  				ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   186  					ComplianceType:         "mustnothave",
   187  					MetadataComplianceType: "musthave",
   188  				},
   189  				Path: manifestPath,
   190  			},
   191  		)
   192  	}
   193  
   194  	// Write a bogus file to ensure it is not picked up when creating the policy
   195  	// template
   196  	bogusFilePath := path.Join(tmpDir, "README.md")
   197  
   198  	err := os.WriteFile(bogusFilePath, []byte("# My Manifests"), 0o666)
   199  	if err != nil {
   200  		t.Fatalf("Failed to write %s", bogusFilePath)
   201  	}
   202  
   203  	// Test both passing in individual files and a flat directory
   204  	tests := []struct {
   205  		ExpectedComplianceType         string
   206  		ExpectedMetadataComplianceType string
   207  		Manifests                      []types.Manifest
   208  	}{
   209  		{
   210  			ExpectedComplianceType:         "musthave",
   211  			ExpectedMetadataComplianceType: "mustonlyhave",
   212  			Manifests:                      manifestFiles,
   213  		},
   214  		{
   215  			ExpectedComplianceType:         "mustnothave",
   216  			ExpectedMetadataComplianceType: "musthave",
   217  			Manifests:                      manifestFilesMustNotHave,
   218  		},
   219  		// The applyDefaults method would normally fill in ComplianceType on each manifest entry.
   220  		{
   221  			ExpectedComplianceType:         "musthave",
   222  			ExpectedMetadataComplianceType: "",
   223  			Manifests: []types.Manifest{{
   224  				ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{ComplianceType: "musthave"},
   225  				Path:                       tmpDir,
   226  			}},
   227  		},
   228  	}
   229  	// test ConsolidateManifests = true (default case)
   230  	// policyTemplates will have only one policyTemplate
   231  	// and two objTemplate under this policyTemplate
   232  	for _, test := range tests {
   233  		policyConf := types.PolicyConfig{
   234  			PolicyOptions: types.PolicyOptions{
   235  				ConsolidateManifests: true,
   236  			},
   237  			ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   238  				ComplianceType:    "musthave",
   239  				RemediationAction: "inform",
   240  				Severity:          "low",
   241  			},
   242  			Manifests: test.Manifests,
   243  			Name:      "policy-app-config",
   244  		}
   245  
   246  		policyTemplates, err := getPolicyTemplates(&policyConf)
   247  		if err != nil {
   248  			t.Fatalf("Failed to get the policy templates: %v", err)
   249  		}
   250  
   251  		assertEqual(t, len(policyTemplates), 1)
   252  
   253  		policyTemplate := policyTemplates[0]
   254  		objdef := policyTemplate["objectDefinition"].(map[string]interface{})
   255  
   256  		assertEqual(t, objdef["metadata"].(map[string]interface{})["name"].(string), "policy-app-config")
   257  
   258  		spec, ok := objdef["spec"].(map[string]interface{})
   259  		if !ok {
   260  			t.Fatal("The spec field is an invalid format")
   261  		}
   262  
   263  		assertEqual(t, spec["remediationAction"], "inform")
   264  		assertEqual(t, spec["severity"], "low")
   265  
   266  		objTemplates, ok := spec["object-templates"].([]map[string]interface{})
   267  		if !ok {
   268  			t.Fatal("The object-templates field is an invalid format")
   269  		}
   270  
   271  		assertEqual(t, len(objTemplates), 2)
   272  		assertEqual(t, objTemplates[0]["complianceType"], test.ExpectedComplianceType)
   273  
   274  		if test.ExpectedMetadataComplianceType != "" {
   275  			assertEqual(t, objTemplates[0]["metadataComplianceType"], test.ExpectedMetadataComplianceType)
   276  		} else {
   277  			assertEqual(t, objTemplates[0]["metadataComplianceType"], nil)
   278  		}
   279  
   280  		kind1, ok := objTemplates[0]["objectDefinition"].(map[string]interface{})["kind"]
   281  		if !ok {
   282  			t.Fatal("The objectDefinition field is an invalid format")
   283  		}
   284  
   285  		assertEqual(t, kind1, "ConfigMap")
   286  		assertEqual(t, objTemplates[1]["complianceType"], test.ExpectedComplianceType)
   287  
   288  		if test.ExpectedMetadataComplianceType != "" {
   289  			assertEqual(t, objTemplates[0]["metadataComplianceType"], test.ExpectedMetadataComplianceType)
   290  		} else {
   291  			assertEqual(t, objTemplates[0]["metadataComplianceType"], nil)
   292  		}
   293  
   294  		kind2, ok := objTemplates[1]["objectDefinition"].(map[string]interface{})["kind"]
   295  		if !ok {
   296  			t.Fatal("The objectDefinition field is an invalid format")
   297  		}
   298  
   299  		assertEqual(t, kind2, "ConfigMap")
   300  	}
   301  }
   302  
   303  func TestGetPolicyTemplateKustomize(t *testing.T) {
   304  	t.Parallel()
   305  	kustomizeDir := t.TempDir()
   306  	configStrings := []string{"tomato", "potato"}
   307  
   308  	manifestsDir := path.Join(kustomizeDir, "manifests")
   309  
   310  	err := os.Mkdir(manifestsDir, 0o777)
   311  	if err != nil {
   312  		t.Fatalf("Failed to create the directory structure %s: %v", manifestsDir, err)
   313  	}
   314  
   315  	for i, enemy := range configStrings {
   316  		manifestPath := path.Join(manifestsDir, fmt.Sprintf("configmap%d.yaml", i))
   317  		manifestYAML := fmt.Sprintf(
   318  			`
   319  apiVersion: v1
   320  kind: ConfigMap
   321  metadata:
   322    name: my-configmap-%d
   323  data:
   324    game.properties: enemies=%s
   325  `,
   326  			i, enemy,
   327  		)
   328  
   329  		err := os.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
   330  		if err != nil {
   331  			t.Fatalf("Failed to write %s", manifestPath)
   332  		}
   333  	}
   334  
   335  	kustomizeManifests := map[string]string{
   336  		path.Join(kustomizeDir, "kustomization.yml"): `
   337  resources:
   338  - manifests/
   339  
   340  labels:
   341  - pairs:
   342      cool: "true"
   343  `,
   344  		path.Join(manifestsDir, "kustomization.yml"): fmt.Sprintf(`
   345  resources:
   346  - %s
   347  - %s
   348  `, "configmap0.yaml", "configmap1.yaml"),
   349  	}
   350  
   351  	for kustomizePath, kustomizeYAML := range kustomizeManifests {
   352  		err := os.WriteFile(kustomizePath, []byte(kustomizeYAML), 0o666)
   353  		if err != nil {
   354  			t.Fatalf("Failed to write %s", kustomizePath)
   355  		}
   356  	}
   357  
   358  	// Write a bogus directory to verify it's not picked up
   359  	bogusDirectory := path.Join(kustomizeDir, "this-other-dir")
   360  
   361  	err = os.Mkdir(bogusDirectory, 0o777)
   362  	if err != nil {
   363  		t.Fatalf("Failed to create the directory structure %s: %v", bogusDirectory, err)
   364  	}
   365  
   366  	// Write a bogus file to verify it is not picked up when creating the policy template
   367  	bogusFilePath := path.Join(kustomizeDir, "this-other-file.yaml")
   368  
   369  	err = os.WriteFile(bogusFilePath, []byte("# My Manifests"), 0o666)
   370  	if err != nil {
   371  		t.Fatalf("Failed to write %s", bogusFilePath)
   372  	}
   373  
   374  	tests := []struct {
   375  		ExpectedComplianceType string
   376  		ManifestPath           string
   377  		ErrMsg                 string
   378  	}{
   379  		{
   380  			ExpectedComplianceType: "musthave",
   381  			ManifestPath:           kustomizeDir,
   382  		},
   383  		{
   384  			ExpectedComplianceType: "musthave",
   385  			ManifestPath:           "not-a-directory",
   386  			ErrMsg:                 "failed to read the manifest path not-a-directory",
   387  		},
   388  	}
   389  	for _, test := range tests {
   390  		policyConf := types.PolicyConfig{
   391  			ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   392  				ComplianceType:    "musthave",
   393  				RemediationAction: "inform",
   394  				Severity:          "low",
   395  			},
   396  			Manifests: []types.Manifest{{
   397  				ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   398  					ComplianceType: "musthave",
   399  				},
   400  				Path: test.ManifestPath,
   401  			}},
   402  			PolicyOptions: types.PolicyOptions{
   403  				ConsolidateManifests: true,
   404  			},
   405  			Name: "policy-kustomize",
   406  		}
   407  
   408  		policyTemplates, err := getPolicyTemplates(&policyConf)
   409  		if err != nil {
   410  			if test.ErrMsg != "" {
   411  				assertEqual(t, err.Error(), test.ErrMsg)
   412  
   413  				continue
   414  			}
   415  
   416  			t.Fatalf("Failed to get the policy templates: %v", err)
   417  		}
   418  
   419  		assertEqual(t, len(policyTemplates), 1)
   420  
   421  		policyTemplate := policyTemplates[0]
   422  		objdef := policyTemplate["objectDefinition"].(map[string]interface{})
   423  
   424  		assertEqual(t, objdef["metadata"].(map[string]interface{})["name"].(string), "policy-kustomize")
   425  
   426  		spec, ok := objdef["spec"].(map[string]interface{})
   427  		if !ok {
   428  			t.Fatal("The spec field is an invalid format")
   429  		}
   430  
   431  		assertEqual(t, spec["remediationAction"], "inform")
   432  		assertEqual(t, spec["severity"], "low")
   433  
   434  		objTemplates, ok := spec["object-templates"].([]map[string]interface{})
   435  		if !ok {
   436  			t.Fatal("The object-templates field is an invalid format")
   437  		}
   438  
   439  		assertEqual(t, len(objTemplates), 2)
   440  		assertEqual(t, objTemplates[0]["complianceType"], test.ExpectedComplianceType)
   441  
   442  		kind1, ok := objTemplates[0]["objectDefinition"].(map[string]interface{})["kind"]
   443  		if !ok {
   444  			t.Fatal("The objectDefinition field is an invalid format")
   445  		}
   446  
   447  		assertEqual(t, kind1, "ConfigMap")
   448  		assertEqual(t, objTemplates[1]["complianceType"], test.ExpectedComplianceType)
   449  
   450  		kind2, ok := objTemplates[1]["objectDefinition"].(map[string]interface{})["kind"]
   451  		if !ok {
   452  			t.Fatal("The objectDefinition field is an invalid format")
   453  		}
   454  
   455  		assertEqual(t, kind2, "ConfigMap")
   456  	}
   457  }
   458  
   459  func TestGetPolicyTemplateNoConsolidate(t *testing.T) {
   460  	t.Parallel()
   461  	tmpDir := t.TempDir()
   462  	manifestFiles := []types.Manifest{}
   463  
   464  	for i, enemy := range []string{"goldfish", "potato"} {
   465  		manifestPath := path.Join(tmpDir, fmt.Sprintf("configmap%d.yaml", i))
   466  		manifestYAML := fmt.Sprintf(
   467  			`
   468  apiVersion: v1
   469  kind: ConfigMap
   470  metadata:
   471    name: my-configmap
   472  data:
   473    game.properties: enemies=%s
   474  ---
   475  apiVersion: v1
   476  kind: ConfigMap
   477  metadata:
   478    name: my-configmap2
   479  data:
   480    game.properties: enemies=%s
   481  `,
   482  			enemy,
   483  			enemy,
   484  		)
   485  
   486  		err := os.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
   487  		if err != nil {
   488  			t.Fatalf("Failed to write %s", manifestPath)
   489  		}
   490  
   491  		// The applyDefaults method would normally fill in ComplianceType on each manifest entry.
   492  		manifestFiles = append(
   493  			manifestFiles, types.Manifest{
   494  				ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   495  					ComplianceType:         "musthave",
   496  					MetadataComplianceType: "mustonlyhave",
   497  				},
   498  				Path: manifestPath,
   499  			},
   500  		)
   501  	}
   502  
   503  	// Write a bogus file to ensure it is not picked up when creating the policy
   504  	// template
   505  	bogusFilePath := path.Join(tmpDir, "README.md")
   506  
   507  	err := os.WriteFile(bogusFilePath, []byte("# My Manifests"), 0o666)
   508  	if err != nil {
   509  		t.Fatalf("Failed to write %s", bogusFilePath)
   510  	}
   511  
   512  	// Test both passing in individual files and a flat directory
   513  	tests := []struct {
   514  		Manifests []types.Manifest
   515  	}{
   516  		{Manifests: manifestFiles},
   517  		// The applyDefaults method would normally fill in ComplianceType on each manifest entry.
   518  		{
   519  			Manifests: []types.Manifest{{
   520  				ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   521  					ComplianceType:         "musthave",
   522  					MetadataComplianceType: "mustonlyhave",
   523  				},
   524  				Path: tmpDir,
   525  			}},
   526  		},
   527  	}
   528  
   529  	// test ConsolidateManifests = false case
   530  	// policyTemplates will skip the consolidation and have four policyTemplate
   531  	// and each policyTemplate has only one objTemplate
   532  	for _, test := range tests {
   533  		policyConf := types.PolicyConfig{
   534  			PolicyOptions: types.PolicyOptions{
   535  				ConsolidateManifests: false,
   536  			},
   537  			ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   538  				ComplianceType:         "musthave",
   539  				MetadataComplianceType: "musthave",
   540  				RemediationAction:      "inform",
   541  				Severity:               "low",
   542  			},
   543  			Manifests: test.Manifests,
   544  			Name:      "policy-app-config",
   545  		}
   546  
   547  		policyTemplates, err := getPolicyTemplates(&policyConf)
   548  		if err != nil {
   549  			t.Fatalf("Failed to get the policy templates: %v", err)
   550  		}
   551  
   552  		assertEqual(t, len(policyTemplates), 4)
   553  
   554  		for i := 0; i < len(policyTemplates); i++ {
   555  			policyTemplate := policyTemplates[i]
   556  			objdef := policyTemplate["objectDefinition"].(map[string]interface{})
   557  			name := "policy-app-config"
   558  
   559  			if i > 0 {
   560  				name += fmt.Sprintf("%d", i+1)
   561  			}
   562  
   563  			assertEqual(t, objdef["metadata"].(map[string]interface{})["name"].(string), name)
   564  
   565  			spec, ok := objdef["spec"].(map[string]interface{})
   566  			if !ok {
   567  				t.Fatal("The spec field is an invalid format")
   568  			}
   569  
   570  			assertEqual(t, spec["remediationAction"], "inform")
   571  			assertEqual(t, spec["severity"], "low")
   572  
   573  			objTemplates, ok := spec["object-templates"].([]map[string]interface{})
   574  			if !ok {
   575  				t.Fatal("The object-templates field is an invalid format")
   576  			}
   577  
   578  			assertEqual(t, len(objTemplates), 1)
   579  			assertEqual(t, objTemplates[0]["complianceType"], "musthave")
   580  			assertEqual(t, objTemplates[0]["metadataComplianceType"], "mustonlyhave")
   581  
   582  			kind1, ok := objTemplates[0]["objectDefinition"].(map[string]interface{})["kind"]
   583  			if !ok {
   584  				t.Fatal("The objectDefinition field is an invalid format")
   585  			}
   586  
   587  			assertEqual(t, kind1, "ConfigMap")
   588  		}
   589  	}
   590  }
   591  
   592  func TestIsPolicyTypeManifest(t *testing.T) {
   593  	t.Parallel()
   594  
   595  	tests := map[string]struct {
   596  		manifest                 map[string]interface{}
   597  		informGatekeeperPolicies bool
   598  		wantIsPolicy             bool
   599  		wantIsOcmPolicy          bool
   600  		wantErr                  string
   601  	}{
   602  		"valid RandomPolicy": {
   603  			manifest: map[string]interface{}{
   604  				"apiVersion": policyAPIVersion,
   605  				"kind":       "RandomPolicy",
   606  				"metadata": map[string]interface{}{
   607  					"name": "foo",
   608  				},
   609  			},
   610  			wantIsPolicy:    true,
   611  			wantIsOcmPolicy: true,
   612  			wantErr:         "",
   613  		},
   614  		"valid ConfigurationPolicy": {
   615  			manifest: map[string]interface{}{
   616  				"apiVersion": policyAPIVersion,
   617  				"kind":       "ConfigurationPolicy",
   618  				"metadata": map[string]interface{}{
   619  					"name": "foo",
   620  				},
   621  			},
   622  			wantIsPolicy:    true,
   623  			wantIsOcmPolicy: true,
   624  			wantErr:         "",
   625  		},
   626  		"valid Gatekeeper Constraint with expander": {
   627  			manifest: map[string]interface{}{
   628  				"apiVersion": "constraints.gatekeeper.sh",
   629  				"kind":       "Foo",
   630  				"metadata": map[string]interface{}{
   631  					"name": "foo",
   632  				},
   633  			},
   634  			informGatekeeperPolicies: true,
   635  			wantIsPolicy:             false,
   636  			wantIsOcmPolicy:          false,
   637  			wantErr:                  "",
   638  		},
   639  		"valid Gatekeeper ConstraintTemplate with expander": {
   640  			manifest: map[string]interface{}{
   641  				"apiVersion": "templates.gatekeeper.sh",
   642  				"kind":       "ConstraintTemplate",
   643  				"metadata": map[string]interface{}{
   644  					"name": "foo",
   645  				},
   646  			},
   647  			informGatekeeperPolicies: true,
   648  			wantIsPolicy:             false,
   649  			wantIsOcmPolicy:          false,
   650  			wantErr:                  "",
   651  		},
   652  		"valid Gatekeeper Constraint without expander": {
   653  			manifest: map[string]interface{}{
   654  				"apiVersion": "constraints.gatekeeper.sh",
   655  				"kind":       "Foo",
   656  				"metadata": map[string]interface{}{
   657  					"name": "foo",
   658  				},
   659  			},
   660  			wantIsPolicy:    true,
   661  			wantIsOcmPolicy: false,
   662  			wantErr:         "",
   663  		},
   664  		"valid Gatekeeper ConstraintTemplate without expander": {
   665  			manifest: map[string]interface{}{
   666  				"apiVersion": "templates.gatekeeper.sh",
   667  				"kind":       "ConstraintTemplate",
   668  				"metadata": map[string]interface{}{
   669  					"name": "foo",
   670  				},
   671  			},
   672  			wantIsPolicy:    true,
   673  			wantIsOcmPolicy: false,
   674  			wantErr:         "",
   675  		},
   676  		"valid Policy": {
   677  			manifest: map[string]interface{}{
   678  				"apiVersion": policyAPIVersion,
   679  				"kind":       "Policy",
   680  				"metadata": map[string]interface{}{
   681  					"name": "foo",
   682  				},
   683  			},
   684  			wantIsPolicy:    false,
   685  			wantIsOcmPolicy: false,
   686  			wantErr: "providing a root Policy kind is not supported by the generator; " +
   687  				"the manifest should be applied to the hub cluster directly",
   688  		},
   689  		"valid PlacementRule": {
   690  			manifest: map[string]interface{}{
   691  				"apiVersion": "apps.open-cluster-management.io/v1",
   692  				"kind":       "PlacementRule",
   693  				"metadata": map[string]interface{}{
   694  					"name": "foo",
   695  				},
   696  			},
   697  			wantIsPolicy:    false,
   698  			wantIsOcmPolicy: false,
   699  			wantErr:         "",
   700  		},
   701  		"wrong ApiVersion": {
   702  			manifest: map[string]interface{}{
   703  				"apiVersion": "fake.test.io/v3alpha2",
   704  				"kind":       "RandomPolicy",
   705  				"metadata": map[string]interface{}{
   706  					"name": "foo",
   707  				},
   708  			},
   709  			wantIsPolicy:    false,
   710  			wantIsOcmPolicy: false,
   711  			wantErr:         "",
   712  		},
   713  		"invalid kind": {
   714  			manifest: map[string]interface{}{
   715  				"apiVersion": policyAPIVersion,
   716  				"kind":       []interface{}{"foo", "bar", "baz"},
   717  				"metadata": map[string]interface{}{
   718  					"name": "foo",
   719  				},
   720  			},
   721  			wantIsPolicy:    false,
   722  			wantIsOcmPolicy: false,
   723  			wantErr:         "invalid or not found kind",
   724  		},
   725  		"missing apiVersion": {
   726  			manifest: map[string]interface{}{
   727  				"kind": "ConfigurationPolicy",
   728  				"metadata": map[string]interface{}{
   729  					"name": "foo",
   730  				},
   731  			},
   732  			wantIsPolicy:    false,
   733  			wantIsOcmPolicy: false,
   734  			wantErr:         "invalid or not found apiVersion",
   735  		},
   736  		"missing name in ConfigurationPolicy": {
   737  			manifest: map[string]interface{}{
   738  				"apiVersion": policyAPIVersion,
   739  				"kind":       "ConfigurationPolicy",
   740  				"metadata": map[string]interface{}{
   741  					"namespace": "foo",
   742  				},
   743  			},
   744  			wantIsPolicy:    true,
   745  			wantIsOcmPolicy: true,
   746  			wantErr:         "invalid or not found metadata.name",
   747  		},
   748  		"missing name in non-policy": {
   749  			manifest: map[string]interface{}{
   750  				"apiVersion": "apps.open-cluster-management.io/v1",
   751  				"kind":       "PlacementRule",
   752  				"metadata": map[string]interface{}{
   753  					"name": "foo",
   754  				},
   755  			},
   756  			wantIsPolicy:    false,
   757  			wantIsOcmPolicy: false,
   758  			wantErr:         "",
   759  		},
   760  	}
   761  
   762  	for name, test := range tests {
   763  		test := test
   764  
   765  		t.Run(name, func(t *testing.T) {
   766  			t.Parallel()
   767  
   768  			gotIsPolicy, gotIsOcmPolicy, gotErr := isPolicyTypeManifest(test.manifest, test.informGatekeeperPolicies)
   769  			if gotErr != nil {
   770  				assertEqual(t, gotErr.Error(), test.wantErr)
   771  			}
   772  			assertEqual(t, gotIsPolicy, test.wantIsPolicy)
   773  			assertEqual(t, gotIsOcmPolicy, test.wantIsOcmPolicy)
   774  		})
   775  	}
   776  }
   777  
   778  func TestGetPolicyTemplateFromPolicyTypeManifest(t *testing.T) {
   779  	t.Parallel()
   780  	tmpDir := t.TempDir()
   781  	manifestFiles := []types.Manifest{}
   782  
   783  	createIamPolicyManifest(t, tmpDir, "iamKindManifest.yaml")
   784  	// Test manifest is non-root IAM policy type.
   785  	IamManifestPath := path.Join(tmpDir, "iamKindManifest.yaml")
   786  
   787  	manifestFiles = append(
   788  		manifestFiles, types.Manifest{Path: IamManifestPath},
   789  	)
   790  
   791  	// Test both passing in individual files and a flat directory.
   792  	tests := []struct {
   793  		Manifests []types.Manifest
   794  	}{
   795  		{Manifests: manifestFiles},
   796  		{
   797  			Manifests: []types.Manifest{{Path: tmpDir}},
   798  		},
   799  	}
   800  
   801  	for _, test := range tests {
   802  		policyConf := types.PolicyConfig{
   803  			Manifests: test.Manifests,
   804  			Name:      "policy-limitclusteradmin",
   805  			ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
   806  				RemediationAction: "inform",
   807  				Severity:          "low",
   808  			},
   809  		}
   810  
   811  		policyTemplates, err := getPolicyTemplates(&policyConf)
   812  		if err != nil {
   813  			t.Fatalf("Failed to get the policy templates: %v", err)
   814  		}
   815  
   816  		assertEqual(t, len(policyTemplates), 1)
   817  
   818  		IamPolicyTemplate := policyTemplates[0]
   819  		IamObjdef := IamPolicyTemplate["objectDefinition"].(map[string]interface{})
   820  		assertEqual(t, IamObjdef["apiVersion"], "policy.open-cluster-management.io/v1")
   821  		// kind will not be overridden by "ConfigurationPolicy".
   822  		assertEqual(t, IamObjdef["kind"], "IamPolicy")
   823  		assertEqual(t, IamObjdef["metadata"].(map[string]interface{})["name"], "policy-limitclusteradmin-example")
   824  
   825  		IamSpec, ok := IamObjdef["spec"].(map[string]interface{})
   826  		if !ok {
   827  			t.Fatal("The spec field is an invalid format")
   828  		}
   829  
   830  		// remediationAction will not be overridden by policyConf.
   831  		assertEqual(t, IamSpec["remediationAction"], "enforce")
   832  		// severity will not be overridden by policyConf.
   833  		assertEqual(t, IamSpec["severity"], "medium")
   834  		assertEqual(t, IamSpec["maxClusterRoleBindingUsers"], 5)
   835  
   836  		namespaceSelector, ok := IamSpec["namespaceSelector"].(map[string]interface{})
   837  		if !ok {
   838  			t.Fatal("The namespaceSelector field is an invalid format")
   839  		}
   840  
   841  		assertReflectEqual(t, namespaceSelector["include"], []interface{}{"*"})
   842  		assertReflectEqual(t, namespaceSelector["exclude"], []interface{}{"kube-*", "openshift-*"})
   843  	}
   844  }
   845  
   846  func TestGetPolicyTemplatePatches(t *testing.T) {
   847  	t.Parallel()
   848  	tmpDir := t.TempDir()
   849  	manifestPath := path.Join(tmpDir, "configmap.yaml")
   850  	manifestYAML := `
   851  apiVersion: v1
   852  kind: ConfigMap
   853  metadata:
   854    name: my-configmap
   855  data:
   856    game.properties: enemies=potato
   857  `
   858  
   859  	err := os.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
   860  	if err != nil {
   861  		t.Fatalf("Failed to write %s", manifestPath)
   862  	}
   863  
   864  	patches := []map[string]interface{}{
   865  		{
   866  			"metadata": map[string]interface{}{
   867  				"labels": map[string]string{"chandler": "bing"},
   868  			},
   869  		},
   870  		{
   871  			"metadata": map[string]interface{}{
   872  				"annotations": map[string]string{"monica": "geller"},
   873  			},
   874  		},
   875  	}
   876  	manifests := []types.Manifest{
   877  		{Path: manifestPath, Patches: patches},
   878  	}
   879  	policyConf := types.PolicyConfig{
   880  		Manifests: manifests,
   881  		Name:      "policy-app-config",
   882  	}
   883  
   884  	policyTemplates, err := getPolicyTemplates(&policyConf)
   885  	if err != nil {
   886  		t.Fatalf("Failed to get the policy templates: %v", err)
   887  	}
   888  
   889  	assertEqual(t, len(policyTemplates), 1)
   890  
   891  	policyTemplate := policyTemplates[0]
   892  	objdef := policyTemplate["objectDefinition"].(map[string]interface{})
   893  	assertEqual(t, objdef["metadata"].(map[string]interface{})["name"].(string), "policy-app-config")
   894  
   895  	spec, ok := objdef["spec"].(map[string]interface{})
   896  	if !ok {
   897  		t.Fatal("The spec field is an invalid format")
   898  	}
   899  
   900  	objTemplates, ok := spec["object-templates"].([]map[string]interface{})
   901  	if !ok {
   902  		t.Fatal("The object-templates field is an invalid format")
   903  	}
   904  
   905  	assertEqual(t, len(objTemplates), 1)
   906  
   907  	objDef, ok := objTemplates[0]["objectDefinition"].(map[string]interface{})
   908  	if !ok {
   909  		t.Fatal("The objectDefinition field is an invalid format")
   910  	}
   911  
   912  	metadata, ok := objDef["metadata"].(map[string]interface{})
   913  	if !ok {
   914  		t.Fatal("The metadata field is an invalid format")
   915  	}
   916  
   917  	labels, ok := metadata["labels"].(map[string]interface{})
   918  	if !ok {
   919  		t.Fatal("The labels field is an invalid format")
   920  	}
   921  
   922  	assertReflectEqual(t, labels, map[string]interface{}{"chandler": "bing"})
   923  
   924  	annotations, ok := metadata["annotations"].(map[string]interface{})
   925  	if !ok {
   926  		t.Fatal("The annotations field is an invalid format")
   927  	}
   928  
   929  	assertReflectEqual(t, annotations, map[string]interface{}{"monica": "geller"})
   930  }
   931  
   932  func TestGetPolicyTemplateMetadataPatches(t *testing.T) {
   933  	t.Parallel()
   934  	tmpDir := t.TempDir()
   935  	manifestPath := path.Join(tmpDir, "patch-configmap.yaml")
   936  	manifestYAML := `
   937  ---
   938  apiVersion: v1
   939  kind: configmap
   940  metadata:
   941    name: test-configmap
   942    namespace: test-namespace
   943  data:
   944    image: "quay.io/potatos1"
   945  `
   946  
   947  	err := os.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
   948  	if err != nil {
   949  		t.Fatalf("Failed to write %s", manifestPath)
   950  	}
   951  
   952  	patches := []map[string]interface{}{
   953  		{
   954  			"metadata": map[string]interface{}{
   955  				"name":      "patch-configmap",
   956  				"namespace": "patch-namespace",
   957  			},
   958  			"data": map[string]interface{}{
   959  				"image": "quay.io/potatos2",
   960  			},
   961  		},
   962  	}
   963  
   964  	manifests := []types.Manifest{
   965  		{Path: manifestPath, Patches: patches},
   966  	}
   967  	policyConf := types.PolicyConfig{
   968  		Manifests: manifests,
   969  		Name:      "policy-app-config",
   970  	}
   971  
   972  	policyTemplates, err := getPolicyTemplates(&policyConf)
   973  	if err != nil {
   974  		t.Fatalf("Failed to get the policy templates: %v ", err)
   975  	}
   976  
   977  	assertEqual(t, len(policyTemplates), 1)
   978  
   979  	policyTemplate := policyTemplates[0]
   980  	objdef := policyTemplate["objectDefinition"].(map[string]interface{})
   981  
   982  	assertEqual(t, objdef["metadata"].(map[string]interface{})["name"].(string), "policy-app-config")
   983  
   984  	spec, ok := objdef["spec"].(map[string]interface{})
   985  	if !ok {
   986  		t.Fatal("The spec field is an invalid format")
   987  	}
   988  
   989  	objTemplates, ok := spec["object-templates"].([]map[string]interface{})
   990  	if !ok {
   991  		t.Fatal("The object-templates field is an invalid format")
   992  	}
   993  
   994  	assertEqual(t, len(objTemplates), 1)
   995  
   996  	objDef, ok := objTemplates[0]["objectDefinition"].(map[string]interface{})
   997  	if !ok {
   998  		t.Fatal("The objectDefinition field is an invalid format")
   999  	}
  1000  
  1001  	metadata, ok := objDef["metadata"].(map[string]interface{})
  1002  	if !ok {
  1003  		t.Fatal("The metadata field is an invalid format")
  1004  	}
  1005  
  1006  	name, ok := metadata["name"].(string)
  1007  	if !ok {
  1008  		t.Fatal("The metadata.name field is an invalid format")
  1009  	}
  1010  
  1011  	assertEqual(t, name, "patch-configmap")
  1012  
  1013  	namespace, ok := metadata["namespace"].(string)
  1014  	if !ok {
  1015  		t.Fatal("The metadata.namespace field is an invalid format")
  1016  	}
  1017  
  1018  	assertEqual(t, namespace, "patch-namespace")
  1019  
  1020  	data, ok := objDef["data"].(map[string]interface{})
  1021  	if !ok {
  1022  		t.Fatal("The data field is an invalid format")
  1023  	}
  1024  
  1025  	image, ok := data["image"].(string)
  1026  	if !ok {
  1027  		t.Fatal("The data.image field is an invalid format")
  1028  	}
  1029  
  1030  	assertEqual(t, image, "quay.io/potatos2")
  1031  }
  1032  
  1033  func TestGetPolicyTemplateMetadataPatchesFail(t *testing.T) {
  1034  	t.Parallel()
  1035  	tmpDir := t.TempDir()
  1036  	manifestPath := path.Join(tmpDir, "multi-configmaps.yaml")
  1037  	manifestYAML := `
  1038  ---
  1039  apiVersion: v1
  1040  kind: configmap
  1041  metadata:
  1042    name: test-configmap
  1043    namespace: test-namespace
  1044  data:
  1045    image: "quay.io/potatos1"
  1046  ---
  1047  apiVersion: v1
  1048  kind: configmap
  1049  metadata:
  1050    name: test2-configmap
  1051    namespace: test2-namespace
  1052  data:
  1053    image: "quay.io/potatos1"
  1054  `
  1055  
  1056  	err := os.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
  1057  	if err != nil {
  1058  		t.Fatalf("Failed to write %s", manifestPath)
  1059  	}
  1060  
  1061  	patches := []map[string]interface{}{
  1062  		{
  1063  			"metadata": map[string]interface{}{
  1064  				"name":      "patch-configmap",
  1065  				"namespace": "patch-namespace",
  1066  			},
  1067  			"data": map[string]interface{}{
  1068  				"image": "quay.io/potatos2",
  1069  			},
  1070  		},
  1071  	}
  1072  
  1073  	manifests := []types.Manifest{
  1074  		{Path: manifestPath, Patches: patches},
  1075  	}
  1076  	policyConf := types.PolicyConfig{
  1077  		Manifests: manifests,
  1078  		Name:      "policy-app-config",
  1079  	}
  1080  
  1081  	_, err = getPolicyTemplates(&policyConf)
  1082  	assertEqual(t, err != nil, true)
  1083  }
  1084  
  1085  func TestGetPolicyTemplateKyverno(t *testing.T) {
  1086  	t.Parallel()
  1087  	tmpDir := t.TempDir()
  1088  	manifestPath := path.Join(tmpDir, "kyverno.yaml")
  1089  	manifestYAML := `
  1090  apiVersion: kyverno.io/v1
  1091  kind: ClusterPolicy
  1092  metadata:
  1093    name: my-awesome-policy`
  1094  
  1095  	err := os.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
  1096  	if err != nil {
  1097  		t.Fatalf("Failed to write %s", manifestPath)
  1098  	}
  1099  
  1100  	policyConf := types.PolicyConfig{
  1101  		PolicyOptions: types.PolicyOptions{
  1102  			InformKyvernoPolicies: true,
  1103  		},
  1104  		ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
  1105  			ComplianceType:    "musthave",
  1106  			RemediationAction: "enforce",
  1107  			Severity:          "low",
  1108  		},
  1109  		Manifests: []types.Manifest{{Path: manifestPath}},
  1110  		Name:      "policy-kyverno-config",
  1111  	}
  1112  
  1113  	policyTemplates, err := getPolicyTemplates(&policyConf)
  1114  	if err != nil {
  1115  		t.Fatalf("Failed to get the policy templates: %v", err)
  1116  	}
  1117  
  1118  	assertEqual(t, len(policyTemplates), 2)
  1119  
  1120  	// This is not an in-depth test since the Kyverno expansion is tested elsewhere. This is
  1121  	// to test that glue code is working as expected.
  1122  	expandedPolicyTemplate := policyTemplates[1]
  1123  	objdef := expandedPolicyTemplate["objectDefinition"].(map[string]interface{})
  1124  
  1125  	spec, ok := objdef["spec"].(map[string]interface{})
  1126  	if !ok {
  1127  		t.Fatal("The spec field is an invalid format")
  1128  	}
  1129  
  1130  	objTemplates, ok := spec["object-templates"].([]map[string]interface{})
  1131  	if !ok {
  1132  		t.Fatal("The object-templates field is an invalid format")
  1133  	}
  1134  
  1135  	assertEqual(t, len(objTemplates), 2)
  1136  	assertEqual(t, objTemplates[0]["complianceType"], "mustnothave")
  1137  	assertEqual(t, objTemplates[0]["metadataComplianceType"], nil)
  1138  
  1139  	kind1, ok := objTemplates[0]["objectDefinition"].(map[string]interface{})["kind"]
  1140  	if !ok {
  1141  		t.Fatal("The objectDefinition field is an invalid format")
  1142  	}
  1143  
  1144  	assertEqual(t, kind1, "ClusterPolicyReport")
  1145  
  1146  	assertEqual(t, objTemplates[1]["complianceType"], "mustnothave")
  1147  	assertEqual(t, objTemplates[1]["metadataComplianceType"], nil)
  1148  
  1149  	kind2, ok := objTemplates[1]["objectDefinition"].(map[string]interface{})["kind"]
  1150  	if !ok {
  1151  		t.Fatal("The objectDefinition field is an invalid format")
  1152  	}
  1153  
  1154  	assertEqual(t, kind2, "PolicyReport")
  1155  }
  1156  
  1157  func TestGetPolicyTemplateNoManifests(t *testing.T) {
  1158  	t.Parallel()
  1159  	tmpDir := t.TempDir()
  1160  	policyConf := types.PolicyConfig{
  1161  		ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
  1162  			ComplianceType:    "musthave",
  1163  			RemediationAction: "inform",
  1164  			Severity:          "low",
  1165  		},
  1166  		Manifests: []types.Manifest{{Path: tmpDir}},
  1167  		Name:      "policy-app-config",
  1168  	}
  1169  
  1170  	_, err := getPolicyTemplates(&policyConf)
  1171  	if err == nil {
  1172  		t.Fatal("Expected an error but did not get one")
  1173  	}
  1174  
  1175  	expected := "the policy policy-app-config must specify at least one non-empty manifest file"
  1176  	assertEqual(t, err.Error(), expected)
  1177  }
  1178  
  1179  func TestGetPolicyTemplateInvalidPath(t *testing.T) {
  1180  	t.Parallel()
  1181  	tmpDir := t.TempDir()
  1182  	manifestPath := path.Join(tmpDir, "does-not-exist.yaml")
  1183  	policyConf := types.PolicyConfig{
  1184  		ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
  1185  			ComplianceType:    "musthave",
  1186  			RemediationAction: "inform",
  1187  			Severity:          "low",
  1188  		},
  1189  		Manifests: []types.Manifest{{
  1190  			ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
  1191  				ComplianceType: "musthave",
  1192  			},
  1193  			Path: manifestPath,
  1194  		}},
  1195  		Name: "policy-app-config",
  1196  	}
  1197  
  1198  	_, err := getPolicyTemplates(&policyConf)
  1199  	if err == nil {
  1200  		t.Fatal("Expected an error but did not get one")
  1201  	}
  1202  
  1203  	expected := fmt.Sprintf("failed to read the manifest path %s", manifestPath)
  1204  	assertEqual(t, err.Error(), expected)
  1205  }
  1206  
  1207  func TestGetPolicyTemplateInvalidManifest(t *testing.T) {
  1208  	t.Parallel()
  1209  	tmpDir := t.TempDir()
  1210  	manifestPath := path.Join(tmpDir, "configmap.yaml")
  1211  	// Ensure an error is returned when there is an invalid manifest file
  1212  	err := os.WriteFile(manifestPath, []byte("$i am not YAML!"), 0o666)
  1213  	if err != nil {
  1214  		t.Fatalf("Failed to write %s", manifestPath)
  1215  	}
  1216  
  1217  	policyConf := types.PolicyConfig{
  1218  		ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{
  1219  			ComplianceType:    "musthave",
  1220  			RemediationAction: "inform",
  1221  			Severity:          "low",
  1222  		},
  1223  		Manifests: []types.Manifest{{Path: manifestPath}},
  1224  		Name:      "policy-app-config",
  1225  	}
  1226  
  1227  	_, err = getPolicyTemplates(&policyConf)
  1228  	if err == nil {
  1229  		t.Fatal("Expected an error but did not get one")
  1230  	}
  1231  
  1232  	expected := fmt.Sprintf(
  1233  		"failed to decode the manifest file at %s: the input manifests must be in the format of "+
  1234  			"YAML objects", manifestPath,
  1235  	)
  1236  	assertEqual(t, err.Error(), expected)
  1237  }
  1238  
  1239  func TestUnmarshalManifestFile(t *testing.T) {
  1240  	t.Parallel()
  1241  	tmpDir := t.TempDir()
  1242  	manifestsPath := path.Join(tmpDir, "configmaps.yaml")
  1243  	yamlContent := `
  1244  apiVersion: v1
  1245  kind: ConfigMap
  1246  metadata:
  1247    name: my-configmap
  1248  data:
  1249    game.properties: |
  1250      enemies=goldfish
  1251  ---
  1252  apiVersion: v1
  1253  kind: ConfigMap
  1254  metadata:
  1255    name: my-configmap2
  1256  data:
  1257    game.properties: |
  1258      enemies=potato
  1259  `
  1260  
  1261  	err := os.WriteFile(manifestsPath, []byte(yamlContent), 0o666)
  1262  	if err != nil {
  1263  		t.Fatalf("Failed to write %s", manifestsPath)
  1264  	}
  1265  
  1266  	manifests, err := unmarshalManifestFile(manifestsPath)
  1267  	if err != nil {
  1268  		t.Fatalf("Failed to unmarshal the YAML content, got: %v", err)
  1269  	}
  1270  
  1271  	assertEqual(t, len(manifests), 2)
  1272  
  1273  	name1, _, _ := unstructured.NestedString((manifests)[0], "metadata", "name")
  1274  	assertEqual(t, name1, "my-configmap")
  1275  
  1276  	name2, _, _ := unstructured.NestedString((manifests)[1], "metadata", "name")
  1277  	assertEqual(t, name2, "my-configmap2")
  1278  }
  1279  
  1280  func TestUnmarshalManifestFileNilYaml(t *testing.T) {
  1281  	t.Parallel()
  1282  	tmpDir := t.TempDir()
  1283  	manifestsPath := path.Join(tmpDir, "configmaps.yaml")
  1284  	yamlContent := `
  1285  ---
  1286  ---
  1287  apiVersion: v1
  1288  kind: ConfigMap
  1289  metadata:
  1290    name: my-configmap
  1291  data:
  1292    game.properties: |
  1293      enemies=goldfish
  1294  ---
  1295  apiVersion: v1
  1296  kind: ConfigMap
  1297  metadata:
  1298    name: my-configmap2
  1299  data:
  1300    game.properties: |
  1301      enemies=potato
  1302  ---
  1303  ---
  1304  `
  1305  
  1306  	err := os.WriteFile(manifestsPath, []byte(yamlContent), 0o666)
  1307  	if err != nil {
  1308  		t.Fatalf("Failed to write %s", manifestsPath)
  1309  	}
  1310  
  1311  	manifests, err := unmarshalManifestFile(manifestsPath)
  1312  	if err != nil {
  1313  		t.Fatalf("Failed to unmarshal the YAML content, got: %v", err)
  1314  	}
  1315  
  1316  	assertEqual(t, len(manifests), 2)
  1317  
  1318  	name1, _, _ := unstructured.NestedString((manifests)[0], "metadata", "name")
  1319  	assertEqual(t, name1, "my-configmap")
  1320  
  1321  	name2, _, _ := unstructured.NestedString((manifests)[1], "metadata", "name")
  1322  	assertEqual(t, name2, "my-configmap2")
  1323  }
  1324  
  1325  func TestUnmarshalManifestFileUnreadable(t *testing.T) {
  1326  	t.Parallel()
  1327  	tmpDir := t.TempDir()
  1328  	manifestsPath := path.Join(tmpDir, "configmaps.yaml")
  1329  
  1330  	_, err := unmarshalManifestFile(manifestsPath)
  1331  	if err == nil {
  1332  		t.Fatal("Expected an error but did not get one")
  1333  	}
  1334  
  1335  	expected := fmt.Sprintf("failed to read the manifest file %s", manifestsPath)
  1336  	assertEqual(t, err.Error(), expected)
  1337  }
  1338  
  1339  func TestUnmarshalManifestFileInvalidYAML(t *testing.T) {
  1340  	t.Parallel()
  1341  	tmpDir := t.TempDir()
  1342  	manifestPath := path.Join(tmpDir, "configmaps.yaml")
  1343  	yamlContent := `$I am not YAML`
  1344  
  1345  	err := os.WriteFile(manifestPath, []byte(yamlContent), 0o666)
  1346  	if err != nil {
  1347  		t.Fatalf("Failed to write %s", manifestPath)
  1348  	}
  1349  
  1350  	_, err = unmarshalManifestFile(manifestPath)
  1351  	if err == nil {
  1352  		t.Fatal("Expected an error but did not get one")
  1353  	}
  1354  }
  1355  
  1356  func TestUnmarshalManifestFileNotObject(t *testing.T) {
  1357  	t.Parallel()
  1358  	tmpDir := t.TempDir()
  1359  	manifestPath := path.Join(tmpDir, "configmaps.yaml")
  1360  	yamlContent := `- i am an array`
  1361  
  1362  	err := os.WriteFile(manifestPath, []byte(yamlContent), 0o666)
  1363  	if err != nil {
  1364  		t.Fatalf("Failed to write %s", manifestPath)
  1365  	}
  1366  
  1367  	_, err = unmarshalManifestFile(manifestPath)
  1368  	if err == nil {
  1369  		t.Fatal("Expected an error but did not get one")
  1370  	}
  1371  
  1372  	expected := fmt.Sprintf(
  1373  		"failed to decode the manifest file at %s: the input manifests must be in the format of "+
  1374  			"YAML objects", manifestPath,
  1375  	)
  1376  	assertEqual(t, err.Error(), expected)
  1377  }
  1378  
  1379  //nolint:paralleltest
  1380  func TestVerifyManifestPath(t *testing.T) {
  1381  	baseDirectory, err := filepath.EvalSymlinks(t.TempDir())
  1382  	if err != nil {
  1383  		t.Fatalf("Failed to evaluate symlinks for the base directory: %v", err)
  1384  	}
  1385  
  1386  	cwd, err := os.Getwd()
  1387  	if err != nil {
  1388  		t.Fatalf("Failed to get the current working directory: %v", err)
  1389  	}
  1390  
  1391  	defer func() {
  1392  		err := os.Chdir(cwd)
  1393  		if err != nil {
  1394  			// panic since this could affect other tests that haven't yet run
  1395  			panic(fmt.Sprintf("Couldn't go back to the original working directory: %v", err))
  1396  		}
  1397  	}()
  1398  
  1399  	// Set up directory structure, with 'workingdir' as target directory:
  1400  	// baseDirectory (t.TempDir())
  1401  	// ├── workingdir
  1402  	// │   └── subdir
  1403  	// └── otherdir
  1404  	workingDir := path.Join(baseDirectory, "workingdir")
  1405  	subDir := path.Join(workingDir, "subdir")
  1406  	otherDir := path.Join(baseDirectory, "otherdir")
  1407  
  1408  	err = os.MkdirAll(subDir, 0o777)
  1409  	if err != nil {
  1410  		t.Fatalf("Failed to create the directory structure %s: %v", subDir, err)
  1411  	}
  1412  
  1413  	err = os.Mkdir(otherDir, 0o777)
  1414  	if err != nil {
  1415  		t.Fatalf("Failed to create the directory structure %s: %v", otherDir, err)
  1416  	}
  1417  
  1418  	// Create files in baseDirectory/workingdir and baseDirectory/otherdir
  1419  	manifestPath := path.Join(workingDir, "configmap.yaml")
  1420  	yamlContent := "---\nkind: ConfigMap"
  1421  
  1422  	err = os.WriteFile(manifestPath, []byte(yamlContent), 0o666)
  1423  	if err != nil {
  1424  		t.Fatalf("Failed to write %s", manifestPath)
  1425  	}
  1426  
  1427  	otherManifestPath := path.Join(otherDir, "configmap.yaml")
  1428  
  1429  	err = os.WriteFile(otherManifestPath, []byte(yamlContent), 0o666)
  1430  	if err != nil {
  1431  		t.Fatalf("Failed to write %s", otherManifestPath)
  1432  	}
  1433  
  1434  	err = os.Chdir(workingDir)
  1435  	if err != nil {
  1436  		t.Fatalf("Failed to change the working directory to %s: %v", workingDir, err)
  1437  	}
  1438  
  1439  	grandParentDir := path.Join("..", "..")
  1440  	relOtherManifestPath := path.Join("..", "otherdir", "configmap.yaml")
  1441  
  1442  	tests := []struct {
  1443  		ManifestPath   string
  1444  		ExpectedErrMsg string
  1445  	}{
  1446  		{manifestPath, ""},
  1447  		{"configmap.yaml", ""},
  1448  		{"subdir", ""},
  1449  		{
  1450  			"..",
  1451  			"the manifest path .. is not in the same directory tree as the kustomization.yaml file",
  1452  		},
  1453  		{
  1454  			grandParentDir,
  1455  			fmt.Sprintf(
  1456  				"the manifest path %s is not in the same directory tree as the kustomization.yaml file",
  1457  				grandParentDir,
  1458  			),
  1459  		},
  1460  		{
  1461  			baseDirectory,
  1462  			fmt.Sprintf(
  1463  				"the manifest path %s is not in the same directory tree as the kustomization.yaml file",
  1464  				baseDirectory,
  1465  			),
  1466  		},
  1467  		{
  1468  			workingDir,
  1469  			fmt.Sprintf(
  1470  				"the manifest path %s may not refer to the same directory as the kustomization.yaml file",
  1471  				workingDir,
  1472  			),
  1473  		},
  1474  		{
  1475  			".",
  1476  			fmt.Sprintf(
  1477  				"the manifest path %s may not refer to the same directory as the kustomization.yaml file",
  1478  				".",
  1479  			),
  1480  		},
  1481  		{
  1482  			otherManifestPath,
  1483  			fmt.Sprintf(
  1484  				"the manifest path %s is not in the same directory tree as the kustomization.yaml file",
  1485  				otherManifestPath,
  1486  			),
  1487  		},
  1488  		{
  1489  			relOtherManifestPath,
  1490  			fmt.Sprintf(
  1491  				"the manifest path %s is not in the same directory tree as the kustomization.yaml file",
  1492  				relOtherManifestPath,
  1493  			),
  1494  		},
  1495  		{
  1496  			otherDir,
  1497  			fmt.Sprintf(
  1498  				"the manifest path %s is not in the same directory tree as the kustomization.yaml file", otherDir,
  1499  			),
  1500  		},
  1501  	}
  1502  
  1503  	for _, test := range tests {
  1504  		test := test
  1505  		//nolint:paralleltest
  1506  		t.Run(
  1507  			"manifestPath="+test.ManifestPath,
  1508  			func(t *testing.T) {
  1509  				err := verifyManifestPath(workingDir, test.ManifestPath)
  1510  				if err == nil {
  1511  					assertEqual(t, "", test.ExpectedErrMsg)
  1512  				} else {
  1513  					assertEqual(t, err.Error(), test.ExpectedErrMsg)
  1514  				}
  1515  			},
  1516  		)
  1517  	}
  1518  }
  1519  
  1520  func TestProcessKustomizeDir(t *testing.T) {
  1521  	baseDirectory, err := filepath.EvalSymlinks(t.TempDir())
  1522  	if err != nil {
  1523  		t.Fatalf("Failed to evaluate symlinks for the base directory: %v", err)
  1524  	}
  1525  
  1526  	// Set up directory structure, with 'workingdir' as target directory:
  1527  	// baseDirectory (t.TempDir())
  1528  	// └── kustomizedir
  1529  	kustomizeDir := path.Join(baseDirectory, "kustomizedir")
  1530  
  1531  	err = os.Mkdir(kustomizeDir, 0o777)
  1532  	if err != nil {
  1533  		t.Fatalf("Failed to create the directory structure %s: %v", kustomizeDir, err)
  1534  	}
  1535  
  1536  	// Create files in baseDirectory/kustomizedir
  1537  	manifestPaths := map[string]string{
  1538  		"kustomization.yaml": `
  1539  resources:
  1540  - configmap.yaml
  1541  - https://github.com/open-cluster-management-io/policy-generator-plugin/examples/input-kustomize/?ref=main
  1542  
  1543  namespace: kustomize-test
  1544  `,
  1545  		"configmap.yaml": `
  1546  apiVersion: v1
  1547  kind: ConfigMap
  1548  metadata:
  1549    name: my-configmap
  1550  data:
  1551    game.properties: |
  1552      enemies=goldfish
  1553  `,
  1554  	}
  1555  
  1556  	for filename, content := range manifestPaths {
  1557  		manifestPath := path.Join(kustomizeDir, filename)
  1558  
  1559  		err = os.WriteFile(manifestPath, []byte(content), 0o666)
  1560  		if err != nil {
  1561  			t.Fatalf("Failed to write %s", manifestPath)
  1562  		}
  1563  	}
  1564  
  1565  	manifests, err := processKustomizeDir(kustomizeDir)
  1566  	if err != nil {
  1567  		t.Fatalf(fmt.Sprintf("Unexpected error: %s", err))
  1568  	}
  1569  
  1570  	assertEqual(t, len(manifests), 3)
  1571  
  1572  	for _, manifest := range manifests {
  1573  		if metadata, ok := manifest["metadata"]; ok {
  1574  			ns := metadata.(map[string]interface{})["namespace"]
  1575  			assertEqual(t, ns, "kustomize-test")
  1576  		}
  1577  	}
  1578  }
  1579  
  1580  func TestGetRootRemediationAction(t *testing.T) {
  1581  	t.Parallel()
  1582  
  1583  	policyTemplates := []map[string]interface{}{{
  1584  		"objectDefinition": map[string]interface{}{
  1585  			"apiVersion": policyAPIVersion,
  1586  			"kind":       configPolicyKind,
  1587  			"metadata": map[string]interface{}{
  1588  				"name": "my-template",
  1589  			},
  1590  			"spec": map[string]interface{}{
  1591  				"remediationAction": "inform",
  1592  				"severity":          "low",
  1593  			},
  1594  		},
  1595  	}}
  1596  
  1597  	expected := getRootRemediationAction(policyTemplates)
  1598  	assertEqual(t, "inform", expected)
  1599  
  1600  	objDef := policyTemplates[0]["objectDefinition"].(map[string]interface{})
  1601  	objDef["spec"].(map[string]interface{})["remediationAction"] = "enforce"
  1602  	expected = getRootRemediationAction(policyTemplates)
  1603  	assertEqual(t, "enforce", expected)
  1604  }