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

     1  // Copyright Contributors to the Open Cluster Management project
     2  package internal
     3  
     4  import (
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/dhaiducek/policy-generator-plugin/internal/types"
    12  )
    13  
    14  func createConfigMap(t *testing.T, tmpDir, filename string) {
    15  	t.Helper()
    16  
    17  	manifestsPath := path.Join(tmpDir, filename)
    18  	yamlContent := `
    19  apiVersion: v1
    20  kind: ConfigMap
    21  metadata:
    22    name: my-configmap
    23  data:
    24    game.properties: enemies=potato
    25  `
    26  
    27  	err := os.WriteFile(manifestsPath, []byte(yamlContent), 0o666)
    28  	if err != nil {
    29  		t.Fatalf("Failed to write %s", manifestsPath)
    30  	}
    31  }
    32  
    33  func createIamPolicyManifest(t *testing.T, tmpDir, filename string) {
    34  	t.Helper()
    35  
    36  	manifestsPath := path.Join(tmpDir, filename)
    37  	yamlContent := `
    38  apiVersion: policy.open-cluster-management.io/v1
    39  kind: IamPolicy
    40  metadata:
    41    name: policy-limitclusteradmin-example
    42  spec:
    43    severity: medium
    44    namespaceSelector:
    45      include: ["*"]
    46      exclude: ["kube-*", "openshift-*"]
    47    remediationAction: enforce
    48    maxClusterRoleBindingUsers: 5
    49  `
    50  
    51  	err := os.WriteFile(manifestsPath, []byte(yamlContent), 0o666)
    52  	if err != nil {
    53  		t.Fatalf("Failed to write %s", manifestsPath)
    54  	}
    55  }
    56  
    57  func TestConfig(t *testing.T) {
    58  	t.Parallel()
    59  	tmpDir := t.TempDir()
    60  	createConfigMap(t, tmpDir, "configmap.yaml")
    61  	configMapPath := path.Join(tmpDir, "configmap.yaml")
    62  	createConfigMap(t, tmpDir, "configmap2.yaml")
    63  	configMapPath2 := path.Join(tmpDir, "configmap.yaml")
    64  	createConfigMap(t, tmpDir, "configmap3.yaml")
    65  	configMapPath3 := path.Join(tmpDir, "configmap.yaml")
    66  	exampleConfig := fmt.Sprintf(
    67  		`
    68  apiVersion: policy.open-cluster-management.io/v1
    69  kind: PolicyGenerator
    70  metadata:
    71    name: policy-generator-name
    72  placementBindingDefaults:
    73    name: my-placement-binding
    74  policyDefaults:
    75    controls:
    76      - PR.DS-1 Data-at-rest
    77    metadataComplianceType: musthave
    78    namespace: my-policies
    79    namespaceSelector:
    80      include:
    81        - default
    82      exclude:
    83        - my-protected-ns
    84    placement:
    85      clusterSelectors:
    86        cloud: red hat
    87    remediationAction: enforce
    88    severity: medium
    89  policies:
    90  - name: policy-app-config
    91    disabled: false
    92    manifests:
    93      - path: %s
    94    namespaceSelector:
    95      include:
    96        - app-ns
    97    remediationAction: inform
    98  - name: policy-app-config2
    99    metadataComplianceType: mustonlyhave
   100    disabled: true
   101    manifests:
   102      - path: %s
   103        metadataComplianceType: musthave
   104      - path: %s
   105    placement:
   106      clusterSelectors:
   107        cloud: weather
   108  `,
   109  		configMapPath,
   110  		configMapPath2,
   111  		configMapPath3,
   112  	)
   113  
   114  	p := Plugin{}
   115  
   116  	err := p.Config([]byte(exampleConfig), tmpDir)
   117  	if err != nil {
   118  		t.Fatal(err.Error())
   119  	}
   120  
   121  	assertEqual(t, p.Metadata.Name, "policy-generator-name")
   122  	assertEqual(t, p.PlacementBindingDefaults.Name, "my-placement-binding")
   123  	assertReflectEqual(t, p.PolicyDefaults.Categories, []string{"CM Configuration Management"})
   124  	assertEqual(t, p.PolicyDefaults.ComplianceType, "musthave")
   125  	assertReflectEqual(t, p.PolicyDefaults.Controls, []string{"PR.DS-1 Data-at-rest"})
   126  	assertEqual(t, p.PolicyDefaults.Namespace, "my-policies")
   127  
   128  	expectedNsSelector := types.NamespaceSelector{
   129  		Exclude: []string{"my-protected-ns"}, Include: []string{"default"},
   130  	}
   131  
   132  	assertReflectEqual(t, p.PolicyDefaults.NamespaceSelector, expectedNsSelector)
   133  	assertEqual(t, p.PolicyDefaults.Placement.PlacementRulePath, "")
   134  	assertEqual(t, p.PolicyDefaults.Placement.PlacementPath, "")
   135  	assertReflectEqual(
   136  		t,
   137  		p.PolicyDefaults.Placement.ClusterSelectors,
   138  		map[string]interface{}{"cloud": "red hat"},
   139  	)
   140  	assertEqual(t, len(p.PolicyDefaults.Placement.LabelSelector), 0)
   141  	assertEqual(t, p.PolicyDefaults.RemediationAction, "enforce")
   142  	assertEqual(t, p.PolicyDefaults.Severity, "medium")
   143  	assertReflectEqual(t, p.PolicyDefaults.Standards, []string{"NIST SP 800-53"})
   144  	assertEqual(t, len(p.Policies), 2)
   145  
   146  	policy1 := p.Policies[0]
   147  	assertReflectEqual(t, policy1.Categories, []string{"CM Configuration Management"})
   148  	assertEqual(t, policy1.ComplianceType, "musthave")
   149  	assertEqual(t, policy1.MetadataComplianceType, "musthave")
   150  	assertReflectEqual(t, policy1.Controls, []string{"PR.DS-1 Data-at-rest"})
   151  	assertEqual(t, policy1.Disabled, false)
   152  	assertEqual(t, len(policy1.Manifests), 1)
   153  	assertEqual(t, policy1.Manifests[0].Path, configMapPath)
   154  	assertEqual(t, policy1.Manifests[0].MetadataComplianceType, "musthave")
   155  	assertEqual(t, policy1.Name, "policy-app-config")
   156  
   157  	p1ExpectedNsSelector := types.NamespaceSelector{
   158  		Exclude: nil, Include: []string{"app-ns"},
   159  	}
   160  
   161  	assertReflectEqual(t, policy1.NamespaceSelector, p1ExpectedNsSelector)
   162  	assertReflectEqual(
   163  		t,
   164  		policy1.Placement.ClusterSelectors,
   165  		map[string]interface{}{"cloud": "red hat"},
   166  	)
   167  	assertEqual(t, policy1.RemediationAction, "inform")
   168  	assertEqual(t, policy1.Severity, "medium")
   169  	assertReflectEqual(t, policy1.Standards, []string{"NIST SP 800-53"})
   170  
   171  	policy2 := p.Policies[1]
   172  	assertReflectEqual(t, policy2.Categories, []string{"CM Configuration Management"})
   173  	assertEqual(t, policy2.ComplianceType, "musthave")
   174  	assertEqual(t, policy2.MetadataComplianceType, "mustonlyhave")
   175  	assertReflectEqual(t, policy2.Controls, []string{"PR.DS-1 Data-at-rest"})
   176  	assertEqual(t, policy2.Disabled, true)
   177  	assertEqual(t, len(policy2.Manifests), 2)
   178  	assertEqual(t, policy2.Manifests[0].Path, configMapPath2)
   179  	assertEqual(t, policy2.Manifests[0].MetadataComplianceType, "musthave")
   180  	assertEqual(t, policy2.Manifests[1].Path, configMapPath3)
   181  	assertEqual(t, policy2.Manifests[1].MetadataComplianceType, "mustonlyhave")
   182  	assertEqual(t, policy2.Name, "policy-app-config2")
   183  	assertReflectEqual(t, policy2.NamespaceSelector, expectedNsSelector)
   184  	assertReflectEqual(
   185  		t,
   186  		policy2.Placement.ClusterSelectors,
   187  		map[string]interface{}{"cloud": "weather"},
   188  	)
   189  	assertEqual(t, policy2.RemediationAction, "enforce")
   190  	assertEqual(t, policy2.Severity, "medium")
   191  	assertReflectEqual(t, policy2.Standards, []string{"NIST SP 800-53"})
   192  }
   193  
   194  func TestConfigAllDefaults(t *testing.T) {
   195  	t.Parallel()
   196  	tmpDir := t.TempDir()
   197  	createConfigMap(t, tmpDir, "configmap.yaml")
   198  	configMapPath := path.Join(tmpDir, "configmap.yaml")
   199  	defaultsConfig := fmt.Sprintf(
   200  		`
   201  apiVersion: policy.open-cluster-management.io/v1
   202  kind: PolicyGenerator
   203  metadata:
   204    name: policy-generator-name
   205  policyDefaults:
   206    namespace: my-policies
   207  policies:
   208  - name: policy-app-config
   209    manifests:
   210      - path: %s
   211  `,
   212  		configMapPath,
   213  	)
   214  	p := Plugin{}
   215  
   216  	err := p.Config([]byte(defaultsConfig), tmpDir)
   217  	if err != nil {
   218  		t.Fatal(err.Error())
   219  	}
   220  
   221  	assertEqual(t, p.Metadata.Name, "policy-generator-name")
   222  	assertEqual(t, p.PlacementBindingDefaults.Name, "")
   223  	assertReflectEqual(t, p.PolicyDefaults.Categories, []string{"CM Configuration Management"})
   224  	assertEqual(t, p.PolicyDefaults.Disabled, false)
   225  	assertEqual(t, p.PolicyDefaults.ComplianceType, "musthave")
   226  	assertEqual(t, p.PolicyDefaults.MetadataComplianceType, "")
   227  	assertReflectEqual(t, p.PolicyDefaults.Controls, []string{"CM-2 Baseline Configuration"})
   228  	assertEqual(t, p.PolicyDefaults.Namespace, "my-policies")
   229  
   230  	expectedNsSelector := types.NamespaceSelector{Exclude: nil, Include: nil}
   231  
   232  	assertEqual(t, p.PolicyDefaults.InformGatekeeperPolicies, true)
   233  	assertEqual(t, p.PolicyDefaults.InformKyvernoPolicies, true)
   234  	assertReflectEqual(t, p.PolicyDefaults.NamespaceSelector, expectedNsSelector)
   235  	assertEqual(t, p.PolicyDefaults.Placement.PlacementRulePath, "")
   236  	assertEqual(t, len(p.PolicyDefaults.Placement.ClusterSelectors), 0)
   237  	assertEqual(t, p.PolicyDefaults.Placement.PlacementPath, "")
   238  	assertEqual(t, len(p.PolicyDefaults.Placement.LabelSelector), 0)
   239  	assertEqual(t, p.PolicyDefaults.RemediationAction, "inform")
   240  	assertEqual(t, p.PolicyDefaults.Severity, "low")
   241  	assertReflectEqual(t, p.PolicyDefaults.Standards, []string{"NIST SP 800-53"})
   242  	assertEqual(t, len(p.Policies), 1)
   243  
   244  	policy := p.Policies[0]
   245  	assertReflectEqual(t, policy.Categories, []string{"CM Configuration Management"})
   246  	assertEqual(t, policy.Disabled, false)
   247  	assertEqual(t, policy.ComplianceType, "musthave")
   248  	assertEqual(t, policy.MetadataComplianceType, "")
   249  	assertReflectEqual(t, policy.Controls, []string{"CM-2 Baseline Configuration"})
   250  	assertEqual(t, policy.Disabled, false)
   251  	assertEqual(t, len(policy.Manifests), 1)
   252  	assertEqual(t, policy.Manifests[0].Path, configMapPath)
   253  	assertEqual(t, policy.Name, "policy-app-config")
   254  	assertReflectEqual(t, policy.NamespaceSelector, expectedNsSelector)
   255  	assertEqual(t, len(policy.Placement.ClusterSelectors), 0)
   256  	assertEqual(t, policy.Placement.PlacementRulePath, "")
   257  	assertEqual(t, len(policy.Placement.LabelSelector), 0)
   258  	assertEqual(t, policy.Placement.PlacementPath, "")
   259  	assertEqual(t, policy.RemediationAction, "inform")
   260  	assertEqual(t, policy.Severity, "low")
   261  	assertReflectEqual(t, policy.Standards, []string{"NIST SP 800-53"})
   262  	assertEqual(t, policy.InformGatekeeperPolicies, true)
   263  	assertEqual(t, policy.InformKyvernoPolicies, true)
   264  }
   265  
   266  func TestConfigNoNamespace(t *testing.T) {
   267  	t.Parallel()
   268  	const config = `
   269  apiVersion: policy.open-cluster-management.io/v1
   270  kind: PolicyGenerator
   271  metadata:
   272    name: policy-generator-name
   273  policies:
   274  - name: policy-app-config
   275    manifests:
   276      - path: input/configmap.yaml
   277  `
   278  
   279  	p := Plugin{}
   280  
   281  	err := p.Config([]byte(config), "")
   282  	if err == nil {
   283  		t.Fatal("Expected an error but did not get one")
   284  	}
   285  
   286  	expected := "policyDefaults.namespace is empty but it must be set"
   287  	assertEqual(t, err.Error(), expected)
   288  }
   289  
   290  func TestConfigInvalidPolicyName(t *testing.T) {
   291  	t.Parallel()
   292  	tmpDir := t.TempDir()
   293  	createConfigMap(t, tmpDir, "configmap.yaml")
   294  	configMapPath := path.Join(tmpDir, "configmap.yaml")
   295  	policyNS := "my-policies-my-policies-my-policies"
   296  	policyName := "policy-app-config-policy-app-config-policy-app-config"
   297  	defaultsConfig := fmt.Sprintf(
   298  		`
   299  apiVersion: policy.open-cluster-management.io/v1
   300  kind: PolicyGenerator
   301  metadata:
   302    name: policy-generator-name
   303  policyDefaults:
   304    namespace: %s
   305  policies:
   306  - name: %s
   307    manifests:
   308      - path: %s
   309  `,
   310  		policyNS, policyName, configMapPath,
   311  	)
   312  
   313  	p := Plugin{}
   314  
   315  	err := p.Config([]byte(defaultsConfig), tmpDir)
   316  	if err == nil {
   317  		t.Fatal("Expected an error but did not get one")
   318  	}
   319  
   320  	expected := fmt.Sprintf(
   321  		"the policy namespace and name cannot be more than 63 characters: %s.%s", policyNS, policyName,
   322  	)
   323  	assertEqual(t, err.Error(), expected)
   324  }
   325  
   326  func TestConfigNoPolicies(t *testing.T) {
   327  	t.Parallel()
   328  	const config = `
   329  apiVersion: policy.open-cluster-management.io/v1
   330  kind: PolicyGenerator
   331  metadata:
   332    name: policy-generator-name
   333  policyDefaults:
   334    namespace: my-policies
   335  `
   336  
   337  	p := Plugin{}
   338  
   339  	err := p.Config([]byte(config), "")
   340  	if err == nil {
   341  		t.Fatal("Expected an error but did not get one")
   342  	}
   343  
   344  	expected := "policies is empty but it must be set"
   345  	assertEqual(t, err.Error(), expected)
   346  }
   347  
   348  func TestConfigInvalidPath(t *testing.T) {
   349  	t.Parallel()
   350  	tmpDir := t.TempDir()
   351  	createConfigMap(t, tmpDir, "configmap.yaml")
   352  	configMapPath := path.Join(tmpDir, "configmap.yaml")
   353  	policyNS := "my-policies"
   354  	policyName := "policy-app-config"
   355  	defaultsConfig := fmt.Sprintf(
   356  		`
   357  apiVersion: policy.open-cluster-management.io/v1
   358  kind: PolicyGenerator
   359  metadata:
   360    name: policy-generator-name
   361  policyDefaults:
   362    namespace: %s
   363  policies:
   364  - name: %s
   365    manifests:
   366      - path: %s
   367  `,
   368  		policyNS, policyName, configMapPath,
   369  	)
   370  
   371  	p := Plugin{}
   372  	// Provide a base directory that isn't in the same directory tree as tmpDir.
   373  	baseDir := t.TempDir()
   374  
   375  	err := p.Config([]byte(defaultsConfig), baseDir)
   376  	if err == nil {
   377  		t.Fatal("Expected an error but did not get one")
   378  	}
   379  
   380  	expected := fmt.Sprintf(
   381  		"the manifest path %s is not in the same directory tree as the kustomization.yaml file", configMapPath,
   382  	)
   383  	assertEqual(t, err.Error(), expected)
   384  }
   385  
   386  func TestConfigMultiplePlacementsClusterSelectorAndPlRPath(t *testing.T) {
   387  	t.Parallel()
   388  	tmpDir := t.TempDir()
   389  	createConfigMap(t, tmpDir, "configmap.yaml")
   390  	config := fmt.Sprintf(`
   391  apiVersion: policy.open-cluster-management.io/v1
   392  kind: PolicyGenerator
   393  metadata:
   394    name: policy-generator-name
   395  policyDefaults:
   396    namespace: my-policies
   397  policies:
   398  - name: policy-app-config
   399    placement:
   400      clusterSelectors:
   401        cloud: red hat
   402      placementRulePath: path/to/plr.yaml
   403    manifests:
   404      - path: %s
   405  `,
   406  		path.Join(tmpDir, "configmap.yaml"),
   407  	)
   408  	p := Plugin{}
   409  
   410  	err := p.Config([]byte(config), tmpDir)
   411  	if err == nil {
   412  		t.Fatal("Expected an error but did not get one")
   413  	}
   414  
   415  	expected := "policy policy-app-config must specify only one of " +
   416  		"placement selector, placement path, or placement name"
   417  	assertEqual(t, err.Error(), expected)
   418  }
   419  
   420  func TestConfigMultiplePlacementsClusterSelectorAndPlRName(t *testing.T) {
   421  	t.Parallel()
   422  	tmpDir := t.TempDir()
   423  	createConfigMap(t, tmpDir, "configmap.yaml")
   424  	config := fmt.Sprintf(`
   425  apiVersion: policy.open-cluster-management.io/v1
   426  kind: PolicyGenerator
   427  metadata:
   428    name: policy-generator-name
   429  policyDefaults:
   430    namespace: my-policies
   431  policies:
   432  - name: policy-app-config
   433    placement:
   434      clusterSelectors:
   435        cloud: red hat
   436      placementRuleName: plrexistingname
   437    manifests:
   438      - path: %s
   439  `,
   440  		path.Join(tmpDir, "configmap.yaml"),
   441  	)
   442  	p := Plugin{}
   443  
   444  	err := p.Config([]byte(config), tmpDir)
   445  	if err == nil {
   446  		t.Fatal("Expected an error but did not get one")
   447  	}
   448  
   449  	expected := "policy policy-app-config must specify only one of " +
   450  		"placement selector, placement path, or placement name"
   451  	assertEqual(t, err.Error(), expected)
   452  }
   453  
   454  func TestConfigMultiplePlacementsLabelSelectorAndPlRPath(t *testing.T) {
   455  	t.Parallel()
   456  	tmpDir := t.TempDir()
   457  	createConfigMap(t, tmpDir, "configmap.yaml")
   458  	config := fmt.Sprintf(`
   459  apiVersion: policy.open-cluster-management.io/v1
   460  kind: PolicyGenerator
   461  metadata:
   462    name: policy-generator-name
   463  policyDefaults:
   464    namespace: my-policies
   465  policies:
   466  - name: policy-app-config
   467    placement:
   468      labelSelector:
   469        cloud: red hat
   470      placementRulePath: path/to/plr.yaml
   471    manifests:
   472      - path: %s
   473  `,
   474  		path.Join(tmpDir, "configmap.yaml"),
   475  	)
   476  	p := Plugin{}
   477  
   478  	err := p.Config([]byte(config), tmpDir)
   479  	if err == nil {
   480  		t.Fatal("Expected an error but did not get one")
   481  	}
   482  
   483  	expected := "policy policy-app-config must specify only one of " +
   484  		"placement selector, placement path, or placement name"
   485  	assertEqual(t, err.Error(), expected)
   486  }
   487  
   488  func TestConfigMultiplePlacementsLabelSelectorAndPlRName(t *testing.T) {
   489  	t.Parallel()
   490  	tmpDir := t.TempDir()
   491  	createConfigMap(t, tmpDir, "configmap.yaml")
   492  	config := fmt.Sprintf(`
   493  apiVersion: policy.open-cluster-management.io/v1
   494  kind: PolicyGenerator
   495  metadata:
   496    name: policy-generator-name
   497  policyDefaults:
   498    namespace: my-policies
   499  policies:
   500  - name: policy-app-config
   501    placement:
   502      labelSelector:
   503        cloud: red hat
   504      placementRuleName: plrexistingname
   505    manifests:
   506      - path: %s
   507  `,
   508  		path.Join(tmpDir, "configmap.yaml"),
   509  	)
   510  	p := Plugin{}
   511  
   512  	err := p.Config([]byte(config), tmpDir)
   513  	if err == nil {
   514  		t.Fatal("Expected an error but did not get one")
   515  	}
   516  
   517  	expected := "policy policy-app-config must specify only one of " +
   518  		"placement selector, placement path, or placement name"
   519  	assertEqual(t, err.Error(), expected)
   520  }
   521  
   522  func TestConfigMultiplePlacementsLabelSelectorAndPlPath(t *testing.T) {
   523  	t.Parallel()
   524  	tmpDir := t.TempDir()
   525  	createConfigMap(t, tmpDir, "configmap.yaml")
   526  	config := fmt.Sprintf(`
   527  apiVersion: policy.open-cluster-management.io/v1
   528  kind: PolicyGenerator
   529  metadata:
   530    name: policy-generator-name
   531  policyDefaults:
   532    namespace: my-policies
   533  policies:
   534  - name: policy-app-config
   535    placement:
   536      labelSelector:
   537        cloud: red hat
   538      placementPath: path/to/pl.yaml
   539    manifests:
   540      - path: %s
   541  `,
   542  		path.Join(tmpDir, "configmap.yaml"),
   543  	)
   544  	p := Plugin{}
   545  
   546  	err := p.Config([]byte(config), tmpDir)
   547  	if err == nil {
   548  		t.Fatal("Expected an error but did not get one")
   549  	}
   550  
   551  	expected := "policy policy-app-config must specify only one of " +
   552  		"placement selector, placement path, or placement name"
   553  	assertEqual(t, err.Error(), expected)
   554  }
   555  
   556  func TestConfigMultiplePlacementsLabelSelectorAndPlName(t *testing.T) {
   557  	t.Parallel()
   558  	tmpDir := t.TempDir()
   559  	createConfigMap(t, tmpDir, "configmap.yaml")
   560  	config := fmt.Sprintf(`
   561  apiVersion: policy.open-cluster-management.io/v1
   562  kind: PolicyGenerator
   563  metadata:
   564    name: policy-generator-name
   565  policyDefaults:
   566    namespace: my-policies
   567  policies:
   568  - name: policy-app-config
   569    placement:
   570      labelSelector:
   571        cloud: red hat
   572      placementName: plexistingname
   573    manifests:
   574      - path: %s
   575  `,
   576  		path.Join(tmpDir, "configmap.yaml"),
   577  	)
   578  	p := Plugin{}
   579  
   580  	err := p.Config([]byte(config), tmpDir)
   581  	if err == nil {
   582  		t.Fatal("Expected an error but did not get one")
   583  	}
   584  
   585  	expected := "policy policy-app-config must specify only one of " +
   586  		"placement selector, placement path, or placement name"
   587  	assertEqual(t, err.Error(), expected)
   588  }
   589  
   590  func TestConfigMultipleDefaultPlacementLabels(t *testing.T) {
   591  	t.Parallel()
   592  	const config = `
   593  apiVersion: policy.open-cluster-management.io/v1
   594  kind: PolicyGenerator
   595  metadata:
   596    name: policy-generator-name
   597  policyDefaults:
   598    namespace: my-policies
   599    placement:
   600      clusterSelectors:
   601        cloud: red hat
   602      labelSelector:
   603        cloud: red hat
   604  policies:
   605  - name: policy-app-config
   606    manifests:
   607      - path: input/configmap.yaml
   608  `
   609  
   610  	p := Plugin{}
   611  
   612  	err := p.Config([]byte(config), "")
   613  	if err == nil {
   614  		t.Fatal("Expected an error but did not get one")
   615  	}
   616  
   617  	expected := "policyDefaults must provide only one of " +
   618  		"placement.labelSelector or placement.clusterSelectors"
   619  	assertEqual(t, err.Error(), expected)
   620  }
   621  
   622  func TestConfigMultiplePolicyPlacementLabels(t *testing.T) {
   623  	t.Parallel()
   624  	tmpDir := t.TempDir()
   625  	createConfigMap(t, tmpDir, "configmap.yaml")
   626  	config := fmt.Sprintf(`
   627  apiVersion: policy.open-cluster-management.io/v1
   628  kind: PolicyGenerator
   629  metadata:
   630    name: policy-generator-name
   631  policyDefaults:
   632    namespace: my-policies
   633  policies:
   634  - name: policy-app-config
   635    placement:
   636      clusterSelectors:
   637        cloud: red hat
   638      labelSelector:
   639        cloud: red hat
   640    manifests:
   641      - path: %s
   642  `,
   643  		path.Join(tmpDir, "configmap.yaml"),
   644  	)
   645  
   646  	p := Plugin{}
   647  
   648  	err := p.Config([]byte(config), tmpDir)
   649  	if err == nil {
   650  		t.Fatal("Expected an error but did not get one")
   651  	}
   652  
   653  	expected := "policy policy-app-config must provide only one of " +
   654  		"placement.labelSelector or placement.clusterSelectors"
   655  	assertEqual(t, err.Error(), expected)
   656  }
   657  
   658  func TestConfigMultipleDefaultPlacementPaths(t *testing.T) {
   659  	t.Parallel()
   660  	const config = `
   661  apiVersion: policy.open-cluster-management.io/v1
   662  kind: PolicyGenerator
   663  metadata:
   664    name: policy-generator-name
   665  policyDefaults:
   666    namespace: my-policies
   667    placement:
   668      placementPath: path/to/pl.yaml
   669      placementRulePath: path/to/plr.yaml
   670  policies:
   671  - name: policy-app-config
   672    placement:
   673      clusterSelectors:
   674        cloud: red hat
   675      placementRulePath: path/to/plr.yaml
   676    manifests:
   677      - path: input/configmap.yaml
   678  `
   679  
   680  	p := Plugin{}
   681  
   682  	err := p.Config([]byte(config), "")
   683  	if err == nil {
   684  		t.Fatal("Expected an error but did not get one")
   685  	}
   686  
   687  	expected := "policyDefaults must provide only one of " +
   688  		"placement.placementPath or placement.placementRulePath"
   689  	assertEqual(t, err.Error(), expected)
   690  }
   691  
   692  func TestConfigMultipleDefaultPlacementName(t *testing.T) {
   693  	t.Parallel()
   694  	const config = `
   695  apiVersion: policy.open-cluster-management.io/v1
   696  kind: PolicyGenerator
   697  metadata:
   698    name: policy-generator-name
   699  policyDefaults:
   700    namespace: my-policies
   701    placement:
   702      placementName: plExistingName
   703      placementRuleName: plrExistingName
   704  policies:
   705  - name: policy-app-config
   706    placement:
   707      clusterSelectors:
   708        cloud: red hat
   709      placementRuleName: plrExistingName
   710    manifests:
   711      - path: input/configmap.yaml
   712  `
   713  
   714  	p := Plugin{}
   715  
   716  	err := p.Config([]byte(config), "")
   717  	if err == nil {
   718  		t.Fatal("Expected an error but did not get one")
   719  	}
   720  
   721  	expected := "policyDefaults must provide only one of " +
   722  		"placement.placementName or placement.placementRuleName"
   723  	assertEqual(t, err.Error(), expected)
   724  }
   725  
   726  func TestConfigMultipleDefaultAndPolicyPlacements(t *testing.T) {
   727  	t.Parallel()
   728  	tmpDir := t.TempDir()
   729  	plFileName := "plr.yaml"
   730  	cmFileName := "configmap.yaml"
   731  
   732  	createConfigMap(t, tmpDir, cmFileName)
   733  
   734  	err := os.WriteFile(path.Join(tmpDir, plFileName), []byte{}, 0o666)
   735  	if err != nil {
   736  		t.Fatalf("Failed to write %s", plFileName)
   737  	}
   738  
   739  	config := fmt.Sprintf(`
   740  apiVersion: policy.open-cluster-management.io/v1
   741  kind: PolicyGenerator
   742  metadata:
   743    name: policy-generator-name
   744  policyDefaults:
   745    namespace: my-policies
   746    placement:
   747      placementPath: %s
   748  policies:
   749  - name: policy-app-config
   750    placement:
   751      clusterSelectors:
   752        cloud: red hat
   753    manifests:
   754    - path: %s
   755  `,
   756  		path.Join(tmpDir, plFileName),
   757  		path.Join(tmpDir, cmFileName),
   758  	)
   759  	p := Plugin{}
   760  
   761  	err = p.Config([]byte(config), tmpDir)
   762  	if err == nil {
   763  		t.Fatal("Expected an error but did not get one")
   764  	}
   765  
   766  	expected := "policy policy-app-config must specify only one of " +
   767  		"placement selector, placement path, or placement name"
   768  	assertEqual(t, err.Error(), expected)
   769  }
   770  
   771  func TestConfigMultipleDefaultAndPolicyPlacementNames(t *testing.T) {
   772  	t.Parallel()
   773  	tmpDir := t.TempDir()
   774  	createConfigMap(t, tmpDir, "configmap.yaml")
   775  	config := fmt.Sprintf(`
   776  apiVersion: policy.open-cluster-management.io/v1
   777  kind: PolicyGenerator
   778  metadata:
   779    name: policy-generator-name
   780  policyDefaults:
   781    namespace: my-policies
   782    placement:
   783      placementName: plexistingname
   784  policies:
   785  - name: policy-app-config
   786    placement:
   787      clusterSelectors:
   788        cloud: red hat
   789    manifests:
   790    - path: %s
   791  `,
   792  		path.Join(tmpDir, "configmap.yaml"),
   793  	)
   794  
   795  	p := Plugin{}
   796  
   797  	err := p.Config([]byte(config), tmpDir)
   798  	if err == nil {
   799  		t.Fatal("Expected an error but did not get one")
   800  	}
   801  
   802  	expected := "policy policy-app-config must specify only one of " +
   803  		"placement selector, placement path, or placement name"
   804  	assertEqual(t, err.Error(), expected)
   805  }
   806  
   807  func TestConfigPlacementInvalidMixture(t *testing.T) {
   808  	t.Parallel()
   809  	tmpDir := t.TempDir()
   810  	createConfigMap(t, tmpDir, "configmap.yaml")
   811  	config := fmt.Sprintf(`
   812  apiVersion: policy.open-cluster-management.io/v1
   813  kind: PolicyGenerator
   814  metadata:
   815    name: policy-generator-name
   816  policyDefaults:
   817    namespace: my-policies
   818  policies:
   819  - name: policy-app-config-1
   820    placement:
   821      clusterSelectors:
   822        cloud: red hat
   823    manifests:
   824      - path: %s
   825  - name: policy-app-config-2
   826    placement:
   827      labelSelector:
   828        cloud: red hat
   829    manifests:
   830      - path: %s
   831  `,
   832  		path.Join(tmpDir, "configmap.yaml"), path.Join(tmpDir, "configmap.yaml"),
   833  	)
   834  	p := Plugin{}
   835  
   836  	err := p.Config([]byte(config), tmpDir)
   837  	if err == nil {
   838  		t.Fatal("Expected an error but did not get one")
   839  	}
   840  
   841  	expected := "may not use a mix of Placement and PlacementRule for " +
   842  		"policies and policysets; found 1 Placement and 1 PlacementRule"
   843  	assertEqual(t, err.Error(), expected)
   844  }
   845  
   846  func TestConfigPlacementPathNotFound(t *testing.T) {
   847  	t.Parallel()
   848  	tmpDir := t.TempDir()
   849  	createConfigMap(t, tmpDir, "configmap.yaml")
   850  	config := fmt.Sprintf(`
   851  apiVersion: policy.open-cluster-management.io/v1
   852  kind: PolicyGenerator
   853  metadata:
   854    name: policy-generator-name
   855  policyDefaults:
   856    namespace: my-policies
   857  policies:
   858  - name: policy-app-config
   859    placement:
   860      placementPath: path/to/pl.yaml
   861    manifests:
   862      - path: %s
   863  `,
   864  		path.Join(tmpDir, "configmap.yaml"),
   865  	)
   866  	p := Plugin{}
   867  
   868  	err := p.Config([]byte(config), tmpDir)
   869  	if err == nil {
   870  		t.Fatal("Expected an error but did not get one")
   871  	}
   872  
   873  	expected := "policy policy-app-config placement.placementPath could not read the path path/to/pl.yaml"
   874  	assertEqual(t, err.Error(), expected)
   875  }
   876  
   877  func TestConfigDuplicateNames(t *testing.T) {
   878  	t.Parallel()
   879  	tmpDir := t.TempDir()
   880  	createConfigMap(t, tmpDir, "configmap.yaml")
   881  	createConfigMap(t, tmpDir, "configmap2.yaml")
   882  	config := fmt.Sprintf(`
   883  apiVersion: policy.open-cluster-management.io/v1
   884  kind: PolicyGenerator
   885  metadata:
   886    name: policy-generator-name
   887  placementBindingDefaults:
   888    name: my-pb
   889  policyDefaults:
   890    namespace: my-policies
   891  policies:
   892  - name: policy-app-config
   893    manifests:
   894      - path: %s
   895  - name: policy-app-config
   896    manifests:
   897      - path: %s
   898  `,
   899  		path.Join(tmpDir, "configmap.yaml"),
   900  		path.Join(tmpDir, "configmap2.yaml"),
   901  	)
   902  	p := Plugin{}
   903  
   904  	err := p.Config([]byte(config), tmpDir)
   905  	if err == nil {
   906  		t.Fatal("Expected an error but did not get one")
   907  	}
   908  
   909  	expected := "each policy must have a unique name set, " +
   910  		"but found a duplicate name: policy-app-config"
   911  	assertEqual(t, err.Error(), expected)
   912  }
   913  
   914  func TestConfigInvalidEvalInterval(t *testing.T) {
   915  	t.Parallel()
   916  	tmpDir := t.TempDir()
   917  	createConfigMap(t, tmpDir, "configmap.yaml")
   918  
   919  	tests := []struct {
   920  		// Individual values can't be used for compliant/noncompliant since an empty string means
   921  		// to not inherit from the policy defaults.
   922  		defaultEvalInterval  string
   923  		policyEvalInterval   string
   924  		manifestEvalInterval string
   925  		expectedMsg          string
   926  	}{
   927  		{
   928  			`{"compliant": "not a duration"}`,
   929  			"",
   930  			"",
   931  			`the policy policy-app has an invalid policy.evaluationInterval.compliant value: time: invalid duration ` +
   932  				`"not a duration"`,
   933  		},
   934  		{
   935  			`{"noncompliant": "not a duration"}`,
   936  			"",
   937  			"",
   938  			`the policy policy-app has an invalid policy.evaluationInterval.noncompliant value: time: invalid ` +
   939  				`duration "not a duration"`,
   940  		},
   941  		{
   942  			"",
   943  			`{"compliant": "not a duration"}`,
   944  			"",
   945  			`the policy policy-app has an invalid policy.evaluationInterval.compliant value: time: invalid duration ` +
   946  				`"not a duration"`,
   947  		},
   948  		{
   949  			"",
   950  			`{"noncompliant": "not a duration"}`,
   951  			"",
   952  			`the policy policy-app has an invalid policy.evaluationInterval.noncompliant value: time: invalid ` +
   953  				`duration "not a duration"`,
   954  		},
   955  		{
   956  			"",
   957  			"",
   958  			`{"compliant": "not a duration"}`,
   959  			`the policy policy-app has the evaluationInterval value set on manifest[0] but consolidateManifests is ` +
   960  				`true`,
   961  		},
   962  		{
   963  			"",
   964  			"",
   965  			`{"noncompliant": "not a duration"}`,
   966  			`the policy policy-app has the evaluationInterval value set on manifest[0] but consolidateManifests is ` +
   967  				`true`,
   968  		},
   969  		{
   970  			"",
   971  			`{"compliant": "10d5h1m"}`,
   972  			"",
   973  			`the policy policy-app has an invalid policy.evaluationInterval.compliant value: time: unknown unit "d" ` +
   974  				`in duration "10d5h1m"`,
   975  		},
   976  		{
   977  			"",
   978  			`{"noncompliant": "1w2d"}`,
   979  			"",
   980  			`the policy policy-app has an invalid policy.evaluationInterval.noncompliant value: time: unknown unit ` +
   981  				`"w" in duration "1w2d"`,
   982  		},
   983  	}
   984  
   985  	for _, test := range tests {
   986  		test := test
   987  
   988  		t.Run(
   989  			fmt.Sprintf("expected=%s", test.expectedMsg),
   990  			func(t *testing.T) {
   991  				t.Parallel()
   992  				config := fmt.Sprintf(`
   993  apiVersion: policy.open-cluster-management.io/v1
   994  kind: PolicyGenerator
   995  metadata:
   996    name: policy-generator-name
   997  policyDefaults:
   998    namespace: my-policies
   999    evaluationInterval: %s
  1000  policies:
  1001  - name: policy-app
  1002    evaluationInterval: %s
  1003    manifests:
  1004      - path: %s
  1005        evaluationInterval: %s
  1006  `,
  1007  					test.defaultEvalInterval,
  1008  					test.policyEvalInterval,
  1009  					path.Join(tmpDir, "configmap.yaml"),
  1010  					test.manifestEvalInterval,
  1011  				)
  1012  
  1013  				p := Plugin{}
  1014  				err := p.Config([]byte(config), tmpDir)
  1015  				if err == nil {
  1016  					t.Fatal("Expected an error but did not get one")
  1017  				}
  1018  
  1019  				assertEqual(t, err.Error(), test.expectedMsg)
  1020  			},
  1021  		)
  1022  	}
  1023  }
  1024  
  1025  func TestConfigInvalidManifestKey(t *testing.T) {
  1026  	t.Parallel()
  1027  	tmpDir := t.TempDir()
  1028  	createConfigMap(t, tmpDir, "configmap.yaml")
  1029  
  1030  	tests := map[string]struct {
  1031  		// Individual values can't be used for compliant/noncompliant since an empty string means
  1032  		// to not inherit from the policy defaults.
  1033  		keyName     string
  1034  		defaultKey  string
  1035  		policyKey   string
  1036  		manifestKey string
  1037  		expectedMsg string
  1038  	}{
  1039  		"pruneObjectBehavior specified in manifest": {
  1040  			"pruneObjectBehavior",
  1041  			"",
  1042  			"",
  1043  			"None",
  1044  			`the policy policy-app has the pruneObjectBehavior value set` +
  1045  				` on manifest[0] but consolidateManifests is true`,
  1046  		},
  1047  		"namespaceSelector specified in manifest": {
  1048  			"namespaceSelector",
  1049  			"",
  1050  			"",
  1051  			`{"include": ["test"]}`,
  1052  			`the policy policy-app has the namespaceSelector value set` +
  1053  				` on manifest[0] but consolidateManifests is true`,
  1054  		},
  1055  		"remediationAction specified in manifest": {
  1056  			"remediationAction",
  1057  			"",
  1058  			"",
  1059  			"enforce",
  1060  			`the policy policy-app has the remediationAction value set` +
  1061  				` on manifest[0] but consolidateManifests is true`,
  1062  		},
  1063  		"severity specified in manifest": {
  1064  			"severity",
  1065  			"",
  1066  			"",
  1067  			"critical",
  1068  			`the policy policy-app has the severity value set` +
  1069  				` on manifest[0] but consolidateManifests is true`,
  1070  		},
  1071  	}
  1072  
  1073  	for testName, test := range tests {
  1074  		test := test
  1075  
  1076  		t.Run(
  1077  			testName,
  1078  			func(t *testing.T) {
  1079  				t.Parallel()
  1080  				config := fmt.Sprintf(`
  1081  apiVersion: policy.open-cluster-management.io/v1
  1082  kind: PolicyGenerator
  1083  metadata:
  1084    name: policy-generator-name
  1085  policyDefaults:
  1086    namespace: my-policies
  1087    %s: %s
  1088  policies:
  1089  - name: policy-app
  1090    %s: %s
  1091    manifests:
  1092      - path: %s
  1093        %s: %s
  1094  `,
  1095  					test.keyName, test.defaultKey,
  1096  					test.keyName, test.policyKey,
  1097  					path.Join(tmpDir, "configmap.yaml"),
  1098  					test.keyName, test.manifestKey,
  1099  				)
  1100  
  1101  				p := Plugin{}
  1102  				err := p.Config([]byte(config), tmpDir)
  1103  				if err == nil {
  1104  					t.Fatal("Expected an error but did not get one")
  1105  				}
  1106  
  1107  				assertEqual(t, err.Error(), test.expectedMsg)
  1108  			},
  1109  		)
  1110  	}
  1111  }
  1112  
  1113  func TestConfigNoManifests(t *testing.T) {
  1114  	t.Parallel()
  1115  	const config = `
  1116  apiVersion: policy.open-cluster-management.io/v1
  1117  kind: PolicyGenerator
  1118  metadata:
  1119    name: policy-generator-name
  1120  policyDefaults:
  1121    namespace: my-policies
  1122  policies:
  1123  - name: policy-app-config
  1124  `
  1125  
  1126  	p := Plugin{}
  1127  
  1128  	err := p.Config([]byte(config), "")
  1129  	if err == nil {
  1130  		t.Fatal("Expected an error but did not get one")
  1131  	}
  1132  
  1133  	expected := "each policy must have at least one manifest, " +
  1134  		"but found none in policy policy-app-config"
  1135  	assertEqual(t, err.Error(), expected)
  1136  }
  1137  
  1138  func TestConfigManifestNotFound(t *testing.T) {
  1139  	t.Parallel()
  1140  	tmpDir := t.TempDir()
  1141  	manifestPath := path.Join(tmpDir, "configmap.yaml")
  1142  	config := fmt.Sprintf(
  1143  		`
  1144  apiVersion: policy.open-cluster-management.io/v1
  1145  kind: PolicyGenerator
  1146  metadata:
  1147    name: policy-generator-name
  1148  policyDefaults:
  1149    namespace: my-policies
  1150  policies:
  1151  - name: policy-app-config
  1152    manifests:
  1153      - path: %s
  1154  `,
  1155  		manifestPath,
  1156  	)
  1157  	p := Plugin{}
  1158  
  1159  	err := p.Config([]byte(config), tmpDir)
  1160  	if err == nil {
  1161  		t.Fatal("Expected an error but did not get one")
  1162  	}
  1163  
  1164  	expected := fmt.Sprintf(
  1165  		"could not read the manifest path %s in policy policy-app-config", manifestPath,
  1166  	)
  1167  	assertEqual(t, err.Error(), expected)
  1168  }
  1169  
  1170  func TestConfigNoPolicyName(t *testing.T) {
  1171  	t.Parallel()
  1172  	tmpDir := t.TempDir()
  1173  	createConfigMap(t, tmpDir, "configmap.yaml")
  1174  	config := fmt.Sprintf(
  1175  		`
  1176  apiVersion: policy.open-cluster-management.io/v1
  1177  kind: PolicyGenerator
  1178  metadata:
  1179    name: policy-generator-name
  1180  policyDefaults:
  1181    namespace: my-policies
  1182  policies:
  1183  - manifests:
  1184      - path: %s
  1185  `,
  1186  		path.Join(tmpDir, "configmap.yaml"),
  1187  	)
  1188  	p := Plugin{}
  1189  
  1190  	err := p.Config([]byte(config), tmpDir)
  1191  	if err == nil {
  1192  		t.Fatal("Expected an error but did not get one")
  1193  	}
  1194  
  1195  	expected := "each policy must have a name set, but did not find a name at policy array index 0"
  1196  	assertEqual(t, err.Error(), expected)
  1197  }
  1198  
  1199  func TestConfigPlrNotFound(t *testing.T) {
  1200  	t.Parallel()
  1201  	tmpDir := t.TempDir()
  1202  	createConfigMap(t, tmpDir, "configmap.yaml")
  1203  	plrPath := path.Join(tmpDir, "plr.yaml")
  1204  	config := fmt.Sprintf(
  1205  		`
  1206  apiVersion: policy.open-cluster-management.io/v1
  1207  kind: PolicyGenerator
  1208  metadata:
  1209    name: policy-generator-name
  1210  policyDefaults:
  1211    namespace: my-policies
  1212    placement:
  1213      placementRulePath: %s
  1214  policies:
  1215  - name: policy-app-config
  1216    manifests:
  1217      - path: %s
  1218  `,
  1219  		plrPath,
  1220  		path.Join(tmpDir, "configmap.yaml"),
  1221  	)
  1222  	p := Plugin{}
  1223  
  1224  	err := p.Config([]byte(config), tmpDir)
  1225  	if err == nil {
  1226  		t.Fatal("Expected an error but did not get one")
  1227  	}
  1228  
  1229  	expected := fmt.Sprintf("policyDefaults placement.placementRulePath could not read the path %s", plrPath)
  1230  	assertEqual(t, err.Error(), expected)
  1231  }
  1232  
  1233  func TestPolicySetConfig(t *testing.T) {
  1234  	t.Parallel()
  1235  	tmpDir := t.TempDir()
  1236  	createConfigMap(t, tmpDir, "configmap.yaml")
  1237  
  1238  	testCases := []testCase{
  1239  		{
  1240  			name: "policySet must have a name set",
  1241  			setupFunc: func(p *Plugin) {
  1242  				p.PolicySets = []types.PolicySetConfig{
  1243  					{
  1244  						PolicySetOptions: types.PolicySetOptions{
  1245  							Placement: types.PlacementConfig{
  1246  								Name:             "policyset-placement",
  1247  								ClusterSelectors: map[string]interface{}{"my": "app"},
  1248  							},
  1249  						},
  1250  					},
  1251  				}
  1252  			},
  1253  			expectedErrMsg: "each policySet must have a name set, but did not find a name at policySet array index 0",
  1254  		},
  1255  		{
  1256  			name: "policySet must be unique",
  1257  			setupFunc: func(p *Plugin) {
  1258  				p.PolicySets = []types.PolicySetConfig{
  1259  					{
  1260  						Name: "my-policyset",
  1261  					},
  1262  					{
  1263  						Name: "my-policyset",
  1264  					},
  1265  				}
  1266  			},
  1267  			expectedErrMsg: "each policySet must have a unique name set, but found a duplicate name: my-policyset",
  1268  		},
  1269  		{
  1270  			name: "policySet must provide only one of placementRulePath or placementPath",
  1271  			setupFunc: func(p *Plugin) {
  1272  				p.PolicySets = []types.PolicySetConfig{
  1273  					{
  1274  						Name: "my-policyset",
  1275  						PolicySetOptions: types.PolicySetOptions{
  1276  							Placement: types.PlacementConfig{
  1277  								PlacementPath:     "../config/plc.yaml",
  1278  								PlacementRulePath: "../config/plr.yaml",
  1279  							},
  1280  						},
  1281  					},
  1282  				}
  1283  			},
  1284  			expectedErrMsg: "policySet my-policyset must provide only one of " +
  1285  				"placement.placementPath or placement.placementRulePath",
  1286  		},
  1287  		{
  1288  			name: "policySet must provide only one of placementRuleName or placementName",
  1289  			setupFunc: func(p *Plugin) {
  1290  				p.PolicySets = []types.PolicySetConfig{
  1291  					{
  1292  						Name: "my-policyset",
  1293  						PolicySetOptions: types.PolicySetOptions{
  1294  							Placement: types.PlacementConfig{
  1295  								PlacementName:     "plExistingName",
  1296  								PlacementRuleName: "plrExistingName",
  1297  							},
  1298  						},
  1299  					},
  1300  				}
  1301  			},
  1302  			expectedErrMsg: "policySet my-policyset must provide only one of " +
  1303  				"placement.placementName or placement.placementRuleName",
  1304  		},
  1305  		{
  1306  			name: "policySet must provide only one of labelSelector or clusterSelectors",
  1307  			setupFunc: func(p *Plugin) {
  1308  				p.PolicySets = []types.PolicySetConfig{
  1309  					{
  1310  						Name: "my-policyset",
  1311  						PolicySetOptions: types.PolicySetOptions{
  1312  							Placement: types.PlacementConfig{
  1313  								LabelSelector:    map[string]interface{}{"cloud": "red hat"},
  1314  								ClusterSelectors: map[string]interface{}{"cloud": "red hat"},
  1315  							},
  1316  						},
  1317  					},
  1318  				}
  1319  			},
  1320  			expectedErrMsg: "policySet my-policyset must provide only one of placement.labelSelector or " +
  1321  				"placement.clusterSelectors",
  1322  		},
  1323  		{
  1324  			name: "policySet may not specify a cluster selector and placement path together",
  1325  			setupFunc: func(p *Plugin) {
  1326  				p.PolicySets = []types.PolicySetConfig{
  1327  					{
  1328  						Name: "my-policyset",
  1329  						PolicySetOptions: types.PolicySetOptions{
  1330  							Placement: types.PlacementConfig{
  1331  								PlacementPath:    "../config/plc.yaml",
  1332  								ClusterSelectors: map[string]interface{}{"cloud": "red hat"},
  1333  							},
  1334  						},
  1335  					},
  1336  				}
  1337  			},
  1338  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1339  				"placement name",
  1340  		},
  1341  		{
  1342  			name: "policySet may not specify a cluster selector and placement name together",
  1343  			setupFunc: func(p *Plugin) {
  1344  				p.PolicySets = []types.PolicySetConfig{
  1345  					{
  1346  						Name: "my-policyset",
  1347  						PolicySetOptions: types.PolicySetOptions{
  1348  							Placement: types.PlacementConfig{
  1349  								PlacementName:    "plexistingname",
  1350  								ClusterSelectors: map[string]interface{}{"cloud": "red hat"},
  1351  							},
  1352  						},
  1353  					},
  1354  				}
  1355  			},
  1356  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1357  				"placement name",
  1358  		},
  1359  		{
  1360  			name: "policySet may not specify a label selector and placement path together",
  1361  			setupFunc: func(p *Plugin) {
  1362  				p.PolicySets = []types.PolicySetConfig{
  1363  					{
  1364  						Name: "my-policyset",
  1365  						PolicySetOptions: types.PolicySetOptions{
  1366  							Placement: types.PlacementConfig{
  1367  								PlacementPath: "../config/plc.yaml",
  1368  								LabelSelector: map[string]interface{}{"cloud": "red hat"},
  1369  							},
  1370  						},
  1371  					},
  1372  				}
  1373  			},
  1374  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1375  				"placement name",
  1376  		},
  1377  		{
  1378  			name: "policySet may not specify a label selector and placement name together",
  1379  			setupFunc: func(p *Plugin) {
  1380  				p.PolicySets = []types.PolicySetConfig{
  1381  					{
  1382  						Name: "my-policyset",
  1383  						PolicySetOptions: types.PolicySetOptions{
  1384  							Placement: types.PlacementConfig{
  1385  								PlacementName: "plexistingname",
  1386  								LabelSelector: map[string]interface{}{"cloud": "red hat"},
  1387  							},
  1388  						},
  1389  					},
  1390  				}
  1391  			},
  1392  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1393  				"placement name",
  1394  		},
  1395  		{
  1396  			name: "policySet may not specify a cluster selector and placementrule path together",
  1397  			setupFunc: func(p *Plugin) {
  1398  				p.PolicySets = []types.PolicySetConfig{
  1399  					{
  1400  						Name: "my-policyset",
  1401  						PolicySetOptions: types.PolicySetOptions{
  1402  							Placement: types.PlacementConfig{
  1403  								PlacementRulePath: "../config/plc.yaml",
  1404  								ClusterSelectors:  map[string]interface{}{"cloud": "red hat"},
  1405  							},
  1406  						},
  1407  					},
  1408  				}
  1409  			},
  1410  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1411  				"placement name",
  1412  		},
  1413  		{
  1414  			name: "policySet may not specify a cluster selector and placementrule name together",
  1415  			setupFunc: func(p *Plugin) {
  1416  				p.PolicySets = []types.PolicySetConfig{
  1417  					{
  1418  						Name: "my-policyset",
  1419  						PolicySetOptions: types.PolicySetOptions{
  1420  							Placement: types.PlacementConfig{
  1421  								PlacementRuleName: "plrexistingname",
  1422  								ClusterSelectors:  map[string]interface{}{"cloud": "red hat"},
  1423  							},
  1424  						},
  1425  					},
  1426  				}
  1427  			},
  1428  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1429  				"placement name",
  1430  		},
  1431  		{
  1432  			name: "policySet may not specify a label selector and placementrule path together",
  1433  			setupFunc: func(p *Plugin) {
  1434  				p.PolicySets = []types.PolicySetConfig{
  1435  					{
  1436  						Name: "my-policyset",
  1437  						PolicySetOptions: types.PolicySetOptions{
  1438  							Placement: types.PlacementConfig{
  1439  								PlacementRulePath: "../config/plc.yaml",
  1440  								LabelSelector:     map[string]interface{}{"cloud": "red hat"},
  1441  							},
  1442  						},
  1443  					},
  1444  				}
  1445  			},
  1446  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1447  				"placement name",
  1448  		},
  1449  		{
  1450  			name: "policySet may not specify a label selector and placementrule name together",
  1451  			setupFunc: func(p *Plugin) {
  1452  				p.PolicySets = []types.PolicySetConfig{
  1453  					{
  1454  						Name: "my-policyset",
  1455  						PolicySetOptions: types.PolicySetOptions{
  1456  							Placement: types.PlacementConfig{
  1457  								PlacementRuleName: "plrexistingname",
  1458  								LabelSelector:     map[string]interface{}{"cloud": "red hat"},
  1459  							},
  1460  						},
  1461  					},
  1462  				}
  1463  			},
  1464  			expectedErrMsg: "policySet my-policyset must specify only one of placement selector, placement path, or " +
  1465  				"placement name",
  1466  		},
  1467  		{
  1468  			name: "policySet placementrule path not resolvable",
  1469  			setupFunc: func(p *Plugin) {
  1470  				p.PolicySets = []types.PolicySetConfig{
  1471  					{
  1472  						Name: "my-policyset",
  1473  						PolicySetOptions: types.PolicySetOptions{
  1474  							Placement: types.PlacementConfig{
  1475  								PlacementRulePath: "../config/plc.yaml",
  1476  							},
  1477  						},
  1478  					},
  1479  				}
  1480  			},
  1481  			expectedErrMsg: "policySet my-policyset placement.placementRulePath " +
  1482  				"could not read the path ../config/plc.yaml",
  1483  		},
  1484  		{
  1485  			name: "policySet placement path not resolvable",
  1486  			setupFunc: func(p *Plugin) {
  1487  				p.PolicySets = []types.PolicySetConfig{
  1488  					{
  1489  						Name: "my-policyset",
  1490  						PolicySetOptions: types.PolicySetOptions{
  1491  							Placement: types.PlacementConfig{
  1492  								PlacementPath: "../config/plc.yaml",
  1493  							},
  1494  						},
  1495  					},
  1496  				}
  1497  			},
  1498  			expectedErrMsg: "policySet my-policyset placement.placementPath could not read the path ../config/plc.yaml",
  1499  		},
  1500  		{
  1501  			name: "Placement and PlacementRule can't be mixed",
  1502  			setupFunc: func(p *Plugin) {
  1503  				p.Policies[0].Placement = types.PlacementConfig{
  1504  					LabelSelector: map[string]interface{}{"cloud": "red hat"},
  1505  				}
  1506  				p.PolicySets = []types.PolicySetConfig{
  1507  					{
  1508  						Name: "my-policyset",
  1509  						PolicySetOptions: types.PolicySetOptions{
  1510  							Placement: types.PlacementConfig{
  1511  								ClusterSelectors: map[string]interface{}{"cloud": "red hat"},
  1512  							},
  1513  						},
  1514  					},
  1515  				}
  1516  			},
  1517  			expectedErrMsg: "may not use a mix of Placement and PlacementRule for policies and policysets; found 1 " +
  1518  				"Placement and 1 PlacementRule",
  1519  		},
  1520  	}
  1521  
  1522  	for _, tc := range testCases {
  1523  		tc := tc // capture range variable
  1524  		t.Run(tc.name, func(t *testing.T) {
  1525  			t.Parallel()
  1526  			p := Plugin{}
  1527  			var err error
  1528  			p.baseDirectory, err = filepath.EvalSymlinks(tmpDir)
  1529  			if err != nil {
  1530  				t.Fatal(err.Error())
  1531  			}
  1532  			p.PlacementBindingDefaults.Name = "my-placement-binding"
  1533  			p.PolicyDefaults.Placement.Name = "my-placement-rule"
  1534  			p.PolicyDefaults.Namespace = "my-policies"
  1535  			policyConf1 := types.PolicyConfig{
  1536  				Name: "policy-app-config",
  1537  				Manifests: []types.Manifest{
  1538  					{
  1539  						Path: path.Join(tmpDir, "configmap.yaml"),
  1540  					},
  1541  				},
  1542  			}
  1543  			policyConf2 := types.PolicyConfig{
  1544  				Name: "policy-app-config2",
  1545  				Manifests: []types.Manifest{
  1546  					{
  1547  						Path: path.Join(tmpDir, "configmap.yaml"),
  1548  					},
  1549  				},
  1550  			}
  1551  			p.Policies = append(p.Policies, policyConf1, policyConf2)
  1552  			tc.setupFunc(&p)
  1553  			p.applyDefaults(map[string]interface{}{})
  1554  			err = p.assertValidConfig()
  1555  			if err == nil {
  1556  				t.Fatal("Expected an error but did not get one")
  1557  			}
  1558  			assertEqual(t, err.Error(), tc.expectedErrMsg)
  1559  		})
  1560  	}
  1561  }
  1562  
  1563  func TestDisabled(t *testing.T) {
  1564  	t.Parallel()
  1565  	tmpDir := t.TempDir()
  1566  	createConfigMap(t, tmpDir, "configmap.yaml")
  1567  	configMapPath := path.Join(tmpDir, "configmap.yaml")
  1568  	defaultsConfig := fmt.Sprintf(
  1569  		`
  1570  apiVersion: policy.open-cluster-management.io/v1
  1571  kind: PolicyGenerator
  1572  metadata:
  1573    name: policy-generator-name
  1574  policyDefaults:
  1575    namespace: my-policies
  1576    disabled: true
  1577  policies:
  1578  - name: policy-app-config
  1579    disabled: false
  1580    manifests:
  1581      - path: %s
  1582    namespaceSelector:
  1583      include:
  1584        - app-ns
  1585    remediationAction: inform
  1586  - name: policy-app-config2
  1587    manifests:
  1588      - path: %s
  1589  `,
  1590  		configMapPath,
  1591  		configMapPath,
  1592  	)
  1593  	p := Plugin{}
  1594  
  1595  	err := p.Config([]byte(defaultsConfig), tmpDir)
  1596  	if err != nil {
  1597  		t.Fatal(err.Error())
  1598  	}
  1599  
  1600  	assertEqual(t, p.PolicyDefaults.Disabled, true)
  1601  
  1602  	enabledPolicy := p.Policies[0]
  1603  	assertEqual(t, enabledPolicy.Disabled, false)
  1604  
  1605  	disabledPolicy := p.Policies[1]
  1606  	assertEqual(t, disabledPolicy.Disabled, true)
  1607  }
  1608  
  1609  func TestConflictingPlacementSelectors(t *testing.T) {
  1610  	t.Parallel()
  1611  	tmpDir := t.TempDir()
  1612  	createConfigMap(t, tmpDir, "configmap.yaml")
  1613  	configMapPath := path.Join(tmpDir, "configmap.yaml")
  1614  	policyNS := "my-policies"
  1615  	policyName := "policy-app"
  1616  	defaultsConfig := fmt.Sprintf(
  1617  		`
  1618  apiVersion: policy.open-cluster-management.io/v1
  1619  kind: PolicyGenerator
  1620  metadata:
  1621    name: policy-generator-name
  1622  policyDefaults:
  1623    namespace: %s
  1624    placement:
  1625      labelSelector:
  1626        matchExpressions:
  1627        - key: cloud
  1628          operator: In
  1629          values:
  1630            - red hat
  1631            - hello
  1632        cloud: red hat
  1633        clusterID: 1234-5678
  1634  policies:
  1635  - name: %s
  1636    manifests:
  1637      - path: %s
  1638  `,
  1639  		policyNS, policyName, configMapPath,
  1640  	)
  1641  
  1642  	p := Plugin{}
  1643  
  1644  	err := p.Config([]byte(defaultsConfig), tmpDir)
  1645  	if err == nil {
  1646  		t.Fatal("Expected an error but did not get one")
  1647  	}
  1648  
  1649  	expected := "policyDefaults placement has invalid selectors: " +
  1650  		"the input is not a valid label selector or key-value label matching map"
  1651  	assertEqual(t, err.Error(), expected)
  1652  }