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 }