github.com/dhaiducek/policy-generator-plugin@v1.99.99/internal/plugin.go (about) 1 // Copyright Contributors to the Open Cluster Management project 2 package internal 3 4 import ( 5 "bytes" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "os" 10 "path/filepath" 11 "sort" 12 "strings" 13 "time" 14 15 "github.com/dhaiducek/policy-generator-plugin/internal/types" 16 yaml "gopkg.in/yaml.v3" 17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 19 "k8s.io/apimachinery/pkg/runtime" 20 "k8s.io/apimachinery/pkg/util/validation" 21 ) 22 23 const ( 24 configPolicyKind = "ConfigurationPolicy" 25 policyAPIGroup = "policy.open-cluster-management.io" 26 policyAPIVersion = policyAPIGroup + "/v1" 27 policyKind = "Policy" 28 policySetAPIVersion = policyAPIGroup + "/v1beta1" 29 policySetKind = "PolicySet" 30 placementBindingAPIVersion = policyAPIGroup + "/v1" 31 placementBindingKind = "PlacementBinding" 32 placementRuleAPIVersion = "apps.open-cluster-management.io/v1" 33 placementRuleKind = "PlacementRule" 34 placementAPIVersion = "cluster.open-cluster-management.io/v1beta1" 35 placementKind = "Placement" 36 maxObjectNameLength = 63 37 dnsReference = "https://kubernetes.io/docs/concepts/overview/working-with-objects/names/" + 38 "#dns-subdomain-names" 39 ) 40 41 // Plugin is used to store the PolicyGenerator configuration and the methods to generate the 42 // desired policies. 43 type Plugin struct { 44 APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` 45 Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` 46 Metadata struct { 47 Name string `json:"name,omitempty" yaml:"name,omitempty"` 48 } `json:"metadata,omitempty" yaml:"metadata,omitempty"` 49 PlacementBindingDefaults struct { 50 Name string `json:"name,omitempty" yaml:"name,omitempty"` 51 } `json:"placementBindingDefaults,omitempty" yaml:"placementBindingDefaults,omitempty"` 52 PolicyDefaults types.PolicyDefaults `json:"policyDefaults,omitempty" yaml:"policyDefaults,omitempty"` 53 PolicySetDefaults types.PolicySetDefaults `json:"policySetDefaults,omitempty" yaml:"policySetDefaults,omitempty"` 54 Policies []types.PolicyConfig `json:"policies" yaml:"policies"` 55 PolicySets []types.PolicySetConfig `json:"policySets" yaml:"policySets"` 56 // A set of all placement names that have been processed or generated 57 allPlcs map[string]bool 58 // The base of the directory tree to restrict all manifest files to be within 59 baseDirectory string 60 // This is a mapping of cluster/label selectors formatted as the return value of getCsKey to 61 // placement names. This is used to find common cluster/label selectors that can be consolidated 62 // to a single placement. 63 csToPlc map[string]string 64 outputBuffer bytes.Buffer 65 // Track placement kind (we only expect to have one kind) 66 usingPlR bool 67 // A set of processed placements from external placements (either Placement.PlacementRulePath or 68 // Placement.PlacementPath) 69 processedPlcs map[string]bool 70 // Track previous policy name for use if policies are being ordered 71 previousPolicyName string 72 } 73 74 var defaults = types.PolicyDefaults{ 75 PolicyOptions: types.PolicyOptions{ 76 Categories: []string{"CM Configuration Management"}, 77 Controls: []string{"CM-2 Baseline Configuration"}, 78 Standards: []string{"NIST SP 800-53"}, 79 }, 80 ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{ 81 ComplianceType: "musthave", 82 RemediationAction: "inform", 83 Severity: "low", 84 }, 85 } 86 87 // Config validates the input PolicyGenerator configuration, applies any missing defaults, and 88 // configures the Policy object. 89 func (p *Plugin) Config(config []byte, baseDirectory string) error { 90 dec := yaml.NewDecoder(bytes.NewReader(config)) 91 dec.KnownFields(true) // emit an error on unknown fields in the input 92 93 err := dec.Decode(p) 94 const errTemplate = "the PolicyGenerator configuration file is invalid: %w" 95 96 if err != nil { 97 return fmt.Errorf(errTemplate, addFieldNotFoundHelp(err)) 98 } 99 100 var unmarshaledConfig map[string]interface{} 101 102 err = yaml.Unmarshal(config, &unmarshaledConfig) 103 if err != nil { 104 return fmt.Errorf(errTemplate, err) 105 } 106 107 p.applyDefaults(unmarshaledConfig) 108 109 baseDirectory, err = filepath.EvalSymlinks(baseDirectory) 110 if err != nil { 111 return fmt.Errorf("failed to evaluate symlinks for the base directory: %w", err) 112 } 113 114 p.baseDirectory = baseDirectory 115 116 return p.assertValidConfig() 117 } 118 119 // Generate generates the policies, placements, and placement bindings and returns them as 120 // a single YAML file as a byte array. An error is returned if they cannot be created. 121 func (p *Plugin) Generate() ([]byte, error) { 122 // Set the default empty values to the fields that track state 123 p.allPlcs = map[string]bool{} 124 p.csToPlc = map[string]string{} 125 p.outputBuffer = bytes.Buffer{} 126 p.processedPlcs = map[string]bool{} 127 128 for i := range p.Policies { 129 err := p.createPolicy(&p.Policies[i]) 130 if err != nil { 131 return nil, err 132 } 133 } 134 135 for i := range p.PolicySets { 136 err := p.createPolicySet(&p.PolicySets[i]) 137 if err != nil { 138 return nil, err 139 } 140 } 141 142 // Keep track of which placement maps to which policy and policySet. This will be used to determine 143 // how many placement bindings are required since one binding per placement is required. 144 // plcNameToPolicyAndSetIdxs[plcName]["policy"] stores the index of policy 145 // plcNameToPolicyAndSetIdxs[plcName]["policyset"] stores the index of policyset 146 plcNameToPolicyAndSetIdxs := map[string]map[string][]int{} 147 148 for i := range p.Policies { 149 // only generate placement when GeneratePlacementWhenInSet equals to true, GeneratePlacement is true, 150 // or policy is not part of any policy sets 151 if p.Policies[i].GeneratePlacementWhenInSet || 152 (p.Policies[i].GeneratePolicyPlacement && len(p.Policies[i].PolicySets) == 0) { 153 plcName, err := p.createPolicyPlacement(p.Policies[i].Placement, p.Policies[i].Name) 154 if err != nil { 155 return nil, err 156 } 157 158 if plcNameToPolicyAndSetIdxs[plcName] == nil { 159 plcNameToPolicyAndSetIdxs[plcName] = map[string][]int{} 160 } 161 162 plcNameToPolicyAndSetIdxs[plcName]["policy"] = append(plcNameToPolicyAndSetIdxs[plcName]["policy"], i) 163 } 164 } 165 166 for i := range p.PolicySets { 167 // only generate placement when GeneratePolicySetPlacement equals to true 168 if p.PolicySets[i].GeneratePolicySetPlacement { 169 plcName, err := p.createPolicySetPlacement(p.PolicySets[i].Placement, p.PolicySets[i].Name) 170 if err != nil { 171 return nil, err 172 } 173 174 if plcNameToPolicyAndSetIdxs[plcName] == nil { 175 plcNameToPolicyAndSetIdxs[plcName] = map[string][]int{} 176 } 177 178 plcNameToPolicyAndSetIdxs[plcName]["policyset"] = append(plcNameToPolicyAndSetIdxs[plcName]["policyset"], i) 179 } 180 } 181 182 // Sort the keys of plcNameToPolicyseetsIdxs so that the policy bindings are generated in a 183 // consistent order. 184 plcNames := make([]string, len(plcNameToPolicyAndSetIdxs)) 185 i := 0 186 187 for k := range plcNameToPolicyAndSetIdxs { 188 plcNames[i] = k 189 i++ 190 } 191 192 sort.Strings(plcNames) 193 194 plcBindingCount := 0 195 196 for _, plcName := range plcNames { 197 // Determine which policies and policy sets to be included in the placement binding. 198 policyConfs := []*types.PolicyConfig{} 199 for _, i := range plcNameToPolicyAndSetIdxs[plcName]["policy"] { 200 policyConfs = append(policyConfs, &p.Policies[i]) 201 } 202 203 policySetConfs := []*types.PolicySetConfig{} 204 for _, i := range plcNameToPolicyAndSetIdxs[plcName]["policyset"] { 205 policySetConfs = append(policySetConfs, &p.PolicySets[i]) 206 } 207 208 // If there is more than one policy associated with a placement but no default binding name 209 // specified, throw an error 210 if (len(policyConfs) > 1 || len(policySetConfs) > 1) && p.PlacementBindingDefaults.Name == "" { 211 return nil, fmt.Errorf( 212 "placementBindingDefaults.name must be set but is empty (multiple policies or policy sets were found "+ 213 "for the PlacementBinding to placement %s)", 214 plcName, 215 ) 216 } 217 218 var bindingName string 219 220 existMultiple := false 221 222 // If there is only one policy or one policy set, use the policy or policy set name if there is no default 223 // binding name specified 224 if len(policyConfs) == 1 && len(policySetConfs) == 0 { 225 bindingName = "binding-" + policyConfs[0].Name 226 } else if len(policyConfs) == 0 && len(policySetConfs) == 0 { 227 bindingName = "binding-" + policySetConfs[0].Name 228 } else { 229 existMultiple = true 230 } 231 // If there are multiple policies or policy sets, use the default placement binding name 232 // but append a number to it so it's a unique name. 233 if p.PlacementBindingDefaults.Name != "" && existMultiple { 234 plcBindingCount++ 235 if plcBindingCount == 1 { 236 bindingName = p.PlacementBindingDefaults.Name 237 } else { 238 bindingName = fmt.Sprintf("%s%d", p.PlacementBindingDefaults.Name, plcBindingCount) 239 } 240 } 241 242 err := p.createPlacementBinding(bindingName, plcName, policyConfs, policySetConfs) 243 if err != nil { 244 return nil, fmt.Errorf("failed to create a placement binding: %w", err) 245 } 246 } 247 248 return p.outputBuffer.Bytes(), nil 249 } 250 251 func getPolicyDefaultBool(config map[string]interface{}, key string) (value bool, set bool) { 252 return getDefaultBool(config, "policyDefaults", key) 253 } 254 255 func getPolicySetDefaultBool(config map[string]interface{}, key string) (value bool, set bool) { 256 return getDefaultBool(config, "policySetDefaults", key) 257 } 258 259 func getDefaultBool(config map[string]interface{}, defaultKey string, key string) (value bool, set bool) { 260 defaults, ok := config[defaultKey].(map[string]interface{}) 261 if ok { 262 value, set = defaults[key].(bool) 263 264 return 265 } 266 267 return false, false 268 } 269 270 func getPolicyBool( 271 config map[string]interface{}, policyIndex int, key string, 272 ) (value bool, set bool) { 273 policy := getPolicy(config, policyIndex) 274 if policy == nil { 275 return false, false 276 } 277 278 value, set = policy[key].(bool) 279 280 return 281 } 282 283 func getPolicySetBool( 284 config map[string]interface{}, policySetIndex int, key string, 285 ) (value bool, set bool) { 286 policySet := getPolicySet(config, policySetIndex) 287 if policySet == nil { 288 return false, false 289 } 290 291 value, set = policySet[key].(bool) 292 293 return 294 } 295 296 func getArrayObject(config map[string]interface{}, key string, idx int) map[string]interface{} { 297 array, ok := config[key].([]interface{}) 298 if !ok { 299 return nil 300 } 301 302 if len(array)-1 < idx { 303 return nil 304 } 305 306 object, ok := array[idx].(map[string]interface{}) 307 if !ok { 308 return nil 309 } 310 311 return object 312 } 313 314 // getPolicy will return a policy at the specified index in the Policy Generator configuration YAML. 315 func getPolicy(config map[string]interface{}, policyIndex int) map[string]interface{} { 316 return getArrayObject(config, "policies", policyIndex) 317 } 318 319 // getPolicySet will return a policy at the specified index in the Policy Generator configuration YAML. 320 func getPolicySet(config map[string]interface{}, policySetIndex int) map[string]interface{} { 321 return getArrayObject(config, "policySets", policySetIndex) 322 } 323 324 // getEvaluationInterval will return the evaluation interval of specified policy in the Policy Generator configuration 325 // YAML. 326 func isEvaluationIntervalSet(config map[string]interface{}, policyIndex int, complianceType string) bool { 327 policy := getPolicy(config, policyIndex) 328 if policy == nil { 329 return false 330 } 331 332 evaluationInterval, ok := policy["evaluationInterval"].(map[string]interface{}) 333 if !ok { 334 return false 335 } 336 337 _, set := evaluationInterval[complianceType].(string) 338 339 return set 340 } 341 342 // isEvaluationIntervalSetManifest will return whether the evaluation interval of the specified manifest 343 // of the specified policy is set in the Policy Generator configuration YAML. 344 func isEvaluationIntervalSetManifest( 345 config map[string]interface{}, policyIndex int, manifestIndex int, complianceType string, 346 ) bool { 347 policy := getPolicy(config, policyIndex) 348 if policy == nil { 349 return false 350 } 351 352 manifests, ok := policy["manifests"].([]interface{}) 353 if !ok { 354 return false 355 } 356 357 if len(manifests)-1 < manifestIndex { 358 return false 359 } 360 361 manifest, ok := manifests[manifestIndex].(map[string]interface{}) 362 if !ok { 363 return false 364 } 365 366 evaluationInterval, ok := manifest["evaluationInterval"].(map[string]interface{}) 367 if !ok { 368 return false 369 } 370 371 _, set := evaluationInterval[complianceType].(string) 372 373 return set 374 } 375 376 func isPolicyFieldSet(config map[string]interface{}, policyIndex int, field string) bool { 377 policy := getPolicy(config, policyIndex) 378 if policy == nil { 379 return false 380 } 381 382 _, set := policy[field] 383 384 return set 385 } 386 387 func isManifestFieldSet(config map[string]interface{}, policyIdx, manifestIdx int, field string) bool { 388 policy := getPolicy(config, policyIdx) 389 if policy == nil { 390 return false 391 } 392 393 manifests, ok := policy["manifests"].([]interface{}) 394 if !ok { 395 return false 396 } 397 398 if len(manifests)-1 < manifestIdx { 399 return false 400 } 401 402 manifest, ok := manifests[manifestIdx].(map[string]interface{}) 403 if !ok { 404 return false 405 } 406 407 _, set := manifest[field] 408 409 return set 410 } 411 412 // applyDefaults applies any missing defaults under Policy.PlacementBindingDefaults, 413 // Policy.PolicyDefaults and PolicySets. It then applies the defaults and user provided 414 // defaults on each policy and policyset entry if they are not overridden by the user. The 415 // input unmarshaledConfig is used in situations where it is necessary to know if an explicit 416 // false is provided rather than rely on the default Go value on the Plugin struct. 417 func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) { 418 if len(p.Policies) == 0 { 419 return 420 } 421 422 // Set defaults to the defaults that aren't overridden 423 if p.PolicyDefaults.Categories == nil { 424 p.PolicyDefaults.Categories = defaults.Categories 425 } 426 427 if p.PolicyDefaults.ComplianceType == "" { 428 p.PolicyDefaults.ComplianceType = defaults.ComplianceType 429 } 430 431 if p.PolicyDefaults.Controls == nil { 432 p.PolicyDefaults.Controls = defaults.Controls 433 } 434 435 cpmValue, setCPM := getPolicyDefaultBool(unmarshaledConfig, "copyPolicyMetadata") 436 if setCPM { 437 p.PolicyDefaults.CopyPolicyMetadata = cpmValue 438 } else { 439 p.PolicyDefaults.CopyPolicyMetadata = true 440 } 441 442 // Policy expanders default to true unless explicitly set in the config. 443 // Gatekeeper policy expander policyDefault 444 igvValue, setIgv := getPolicyDefaultBool(unmarshaledConfig, "informGatekeeperPolicies") 445 if setIgv { 446 p.PolicyDefaults.InformGatekeeperPolicies = igvValue 447 } else { 448 p.PolicyDefaults.InformGatekeeperPolicies = true 449 } 450 // Kyverno policy expander policyDefault 451 ikvValue, setIkv := getPolicyDefaultBool(unmarshaledConfig, "informKyvernoPolicies") 452 if setIkv { 453 p.PolicyDefaults.InformKyvernoPolicies = ikvValue 454 } else { 455 p.PolicyDefaults.InformKyvernoPolicies = true 456 } 457 458 consolidatedValue, setConsolidated := getPolicyDefaultBool(unmarshaledConfig, "consolidateManifests") 459 if setConsolidated { 460 p.PolicyDefaults.ConsolidateManifests = consolidatedValue 461 } else { 462 p.PolicyDefaults.ConsolidateManifests = true 463 } 464 465 if p.PolicyDefaults.RemediationAction == "" { 466 p.PolicyDefaults.RemediationAction = defaults.RemediationAction 467 } 468 469 if p.PolicyDefaults.Severity == "" { 470 p.PolicyDefaults.Severity = defaults.Severity 471 } 472 473 if p.PolicyDefaults.Standards == nil { 474 p.PolicyDefaults.Standards = defaults.Standards 475 } 476 477 // GeneratePolicyPlacement defaults to true unless explicitly set in the config. 478 gppValue, setGpp := getPolicyDefaultBool(unmarshaledConfig, "generatePolicyPlacement") 479 if setGpp { 480 p.PolicyDefaults.GeneratePolicyPlacement = gppValue 481 } else { 482 p.PolicyDefaults.GeneratePolicyPlacement = true 483 } 484 485 // Generate temporary sets to later merge the policy sets declared in p.Policies[*] and p.PolicySets 486 plcsetToPlc := make(map[string]map[string]bool) 487 plcToPlcset := make(map[string]map[string]bool) 488 489 for _, plcset := range p.PolicySets { 490 if plcsetToPlc[plcset.Name] == nil { 491 plcsetToPlc[plcset.Name] = make(map[string]bool) 492 } 493 494 for _, plc := range plcset.Policies { 495 plcsetToPlc[plcset.Name][plc] = true 496 497 if plcToPlcset[plc] == nil { 498 plcToPlcset[plc] = make(map[string]bool) 499 } 500 501 plcToPlcset[plc][plcset.Name] = true 502 } 503 } 504 505 applyDefaultDependencyFields(p.PolicyDefaults.Dependencies, p.PolicyDefaults.Namespace) 506 applyDefaultDependencyFields(p.PolicyDefaults.ExtraDependencies, p.PolicyDefaults.Namespace) 507 508 for i := range p.Policies { 509 policy := &p.Policies[i] 510 511 if policy.PolicyAnnotations == nil { 512 annotations := map[string]string{} 513 for k, v := range p.PolicyDefaults.PolicyAnnotations { 514 annotations[k] = v 515 } 516 517 policy.PolicyAnnotations = annotations 518 } 519 520 if policy.Categories == nil { 521 policy.Categories = p.PolicyDefaults.Categories 522 } 523 524 if policy.ConfigurationPolicyAnnotations == nil { 525 annotations := map[string]string{} 526 for k, v := range p.PolicyDefaults.ConfigurationPolicyAnnotations { 527 annotations[k] = v 528 } 529 530 policy.ConfigurationPolicyAnnotations = annotations 531 } 532 533 cpmValue, setCpm := getPolicyBool(unmarshaledConfig, i, "copyPolicyMetadata") 534 if setCpm { 535 policy.CopyPolicyMetadata = cpmValue 536 } else { 537 policy.CopyPolicyMetadata = p.PolicyDefaults.CopyPolicyMetadata 538 } 539 540 if policy.Standards == nil { 541 policy.Standards = p.PolicyDefaults.Standards 542 } 543 544 if policy.Controls == nil { 545 policy.Controls = p.PolicyDefaults.Controls 546 } 547 548 if policy.ComplianceType == "" { 549 policy.ComplianceType = p.PolicyDefaults.ComplianceType 550 } 551 552 if policy.MetadataComplianceType == "" && p.PolicyDefaults.MetadataComplianceType != "" { 553 policy.MetadataComplianceType = p.PolicyDefaults.MetadataComplianceType 554 } 555 556 // Only use the policyDefault evaluationInterval value when it's not explicitly set on the policy. 557 if policy.EvaluationInterval.Compliant == "" { 558 set := isEvaluationIntervalSet(unmarshaledConfig, i, "compliant") 559 if !set { 560 policy.EvaluationInterval.Compliant = p.PolicyDefaults.EvaluationInterval.Compliant 561 } 562 } 563 564 if policy.EvaluationInterval.NonCompliant == "" { 565 set := isEvaluationIntervalSet(unmarshaledConfig, i, "noncompliant") 566 if !set { 567 policy.EvaluationInterval.NonCompliant = p.PolicyDefaults.EvaluationInterval.NonCompliant 568 } 569 } 570 571 if policy.PruneObjectBehavior == "" { 572 policy.PruneObjectBehavior = p.PolicyDefaults.PruneObjectBehavior 573 } 574 575 if policy.PolicySets == nil { 576 policy.PolicySets = p.PolicyDefaults.PolicySets 577 } 578 579 // GeneratePolicyPlacement defaults to true unless explicitly set in the config. 580 gppValue, setGpp := getPolicyBool(unmarshaledConfig, i, "generatePolicyPlacement") 581 if setGpp { 582 policy.GeneratePolicyPlacement = gppValue 583 } else { 584 policy.GeneratePolicyPlacement = p.PolicyDefaults.GeneratePolicyPlacement 585 } 586 587 // GeneratePlacementWhenInSet defaults to false unless explicitly set in the config. 588 gpsetValue, setGpset := getPolicyBool(unmarshaledConfig, i, "generatePlacementWhenInSet") 589 if setGpset { 590 policy.GeneratePlacementWhenInSet = gpsetValue 591 } else { 592 policy.GeneratePlacementWhenInSet = p.PolicyDefaults.GeneratePlacementWhenInSet 593 } 594 595 // Policy expanders default to the policy default unless explicitly set. 596 // Gatekeeper policy expander policy override 597 igvValue, setIgv := getPolicyBool(unmarshaledConfig, i, "informGatekeeperPolicies") 598 if setIgv { 599 policy.InformGatekeeperPolicies = igvValue 600 } else { 601 policy.InformGatekeeperPolicies = p.PolicyDefaults.InformGatekeeperPolicies 602 } 603 // Kyverno policy expander policy override 604 ikvValue, setIkv := getPolicyBool(unmarshaledConfig, i, "informKyvernoPolicies") 605 if setIkv { 606 policy.InformKyvernoPolicies = ikvValue 607 } else { 608 policy.InformKyvernoPolicies = p.PolicyDefaults.InformKyvernoPolicies 609 } 610 611 consolidatedValue, setConsolidated := getPolicyBool(unmarshaledConfig, i, "consolidateManifests") 612 if setConsolidated { 613 policy.ConsolidateManifests = consolidatedValue 614 } else { 615 policy.ConsolidateManifests = p.PolicyDefaults.ConsolidateManifests 616 } 617 618 disabledValue, setDisabled := getPolicyBool(unmarshaledConfig, i, "disabled") 619 if setDisabled { 620 policy.Disabled = disabledValue 621 } else { 622 policy.Disabled = p.PolicyDefaults.Disabled 623 } 624 625 ignorePending, ignorePendingIsSet := getPolicyBool(unmarshaledConfig, i, "ignorePending") 626 if ignorePendingIsSet { 627 policy.IgnorePending = ignorePending 628 } else { 629 policy.IgnorePending = p.PolicyDefaults.IgnorePending 630 } 631 632 if isPolicyFieldSet(unmarshaledConfig, i, "dependencies") { 633 applyDefaultDependencyFields(policy.Dependencies, p.PolicyDefaults.Namespace) 634 } else { 635 policy.Dependencies = p.PolicyDefaults.Dependencies 636 } 637 638 if isPolicyFieldSet(unmarshaledConfig, i, "extraDependencies") { 639 applyDefaultDependencyFields(policy.ExtraDependencies, p.PolicyDefaults.Namespace) 640 } else { 641 policy.ExtraDependencies = p.PolicyDefaults.ExtraDependencies 642 } 643 644 if !isPolicyFieldSet(unmarshaledConfig, i, "orderManifests") { 645 policy.OrderManifests = p.PolicyDefaults.OrderManifests 646 } 647 648 applyDefaultPlacementFields(&policy.Placement, p.PolicyDefaults.Placement) 649 650 // Only use defaults when the namespaceSelector is not set on the policy 651 nsSelector := policy.NamespaceSelector 652 defNsSelector := p.PolicyDefaults.NamespaceSelector 653 654 if nsSelector.Exclude == nil && nsSelector.Include == nil && 655 nsSelector.MatchLabels == nil && nsSelector.MatchExpressions == nil { 656 policy.NamespaceSelector = defNsSelector 657 } 658 659 if policy.RemediationAction == "" { 660 policy.RemediationAction = p.PolicyDefaults.RemediationAction 661 } 662 663 if policy.Severity == "" { 664 policy.Severity = p.PolicyDefaults.Severity 665 } 666 667 for j := range policy.Manifests { 668 manifest := &policy.Manifests[j] 669 670 if manifest.ComplianceType == "" { 671 manifest.ComplianceType = policy.ComplianceType 672 } 673 674 if manifest.MetadataComplianceType == "" && policy.MetadataComplianceType != "" { 675 manifest.MetadataComplianceType = policy.MetadataComplianceType 676 } 677 678 // If the manifests are consolidated to a single ConfigurationPolicy object, don't set 679 // ConfigurationPolicy options per manifest. 680 if policy.ConsolidateManifests { 681 continue 682 } 683 684 // Only use the policy's ConfigurationPolicyOptions values when they're not explicitly set in the manifest. 685 if manifest.EvaluationInterval.Compliant == "" { 686 set := isEvaluationIntervalSetManifest(unmarshaledConfig, i, j, "compliant") 687 if !set { 688 manifest.EvaluationInterval.Compliant = policy.EvaluationInterval.Compliant 689 } 690 } 691 692 if manifest.EvaluationInterval.NonCompliant == "" { 693 set := isEvaluationIntervalSetManifest(unmarshaledConfig, i, j, "noncompliant") 694 if !set { 695 manifest.EvaluationInterval.NonCompliant = policy.EvaluationInterval.NonCompliant 696 } 697 } 698 699 selector := manifest.NamespaceSelector 700 if selector.Exclude == nil && selector.Include == nil && 701 selector.MatchLabels == nil && selector.MatchExpressions == nil { 702 manifest.NamespaceSelector = policy.NamespaceSelector 703 } 704 705 if manifest.RemediationAction == "" && policy.RemediationAction != "" { 706 manifest.RemediationAction = policy.RemediationAction 707 } 708 709 if manifest.PruneObjectBehavior == "" && policy.PruneObjectBehavior != "" { 710 manifest.PruneObjectBehavior = policy.PruneObjectBehavior 711 } 712 713 if manifest.Severity == "" && manifest.Severity != "" { 714 manifest.Severity = policy.Severity 715 } 716 717 if isManifestFieldSet(unmarshaledConfig, i, j, "extraDependencies") { 718 applyDefaultDependencyFields(manifest.ExtraDependencies, p.PolicyDefaults.Namespace) 719 } else { 720 manifest.ExtraDependencies = policy.ExtraDependencies 721 } 722 723 if !isManifestFieldSet(unmarshaledConfig, i, j, "ignorePending") { 724 manifest.IgnorePending = policy.IgnorePending 725 } 726 } 727 728 for _, plcsetInPlc := range policy.PolicySets { 729 if _, ok := plcsetToPlc[plcsetInPlc]; !ok { 730 newPlcset := types.PolicySetConfig{ 731 Name: plcsetInPlc, 732 } 733 p.PolicySets = append(p.PolicySets, newPlcset) 734 plcsetToPlc[plcsetInPlc] = make(map[string]bool) 735 } 736 737 if plcToPlcset[policy.Name] == nil { 738 plcToPlcset[policy.Name] = make(map[string]bool) 739 } 740 741 plcToPlcset[policy.Name][plcsetInPlc] = true 742 743 plcsetToPlc[plcsetInPlc][policy.Name] = true 744 } 745 746 policy.PolicySets = make([]string, 0, len(plcToPlcset[policy.Name])) 747 748 for plcset := range plcToPlcset[policy.Name] { 749 policy.PolicySets = append(policy.PolicySets, plcset) 750 } 751 } 752 753 gpspValue, setGpsp := getPolicySetDefaultBool(unmarshaledConfig, "generatePolicySetPlacement") 754 if setGpsp { 755 p.PolicySetDefaults.GeneratePolicySetPlacement = gpspValue 756 } else { 757 p.PolicySetDefaults.GeneratePolicySetPlacement = true 758 } 759 760 // Sync up the declared policy sets in p.Policies[*] 761 for i := range p.PolicySets { 762 plcset := &p.PolicySets[i] 763 plcset.Policies = make([]string, 0, len(plcsetToPlc[plcset.Name])) 764 765 for plc := range plcsetToPlc[plcset.Name] { 766 plcset.Policies = append(plcset.Policies, plc) 767 } 768 769 // GeneratePolicySetPlacement defaults to true unless explicitly set in the config. 770 gpspValue, setGpsp := getPolicySetBool(unmarshaledConfig, i, "generatePolicySetPlacement") 771 if setGpsp { 772 plcset.GeneratePolicySetPlacement = gpspValue 773 } else { 774 plcset.GeneratePolicySetPlacement = p.PolicySetDefaults.GeneratePolicySetPlacement 775 } 776 777 applyDefaultPlacementFields(&plcset.Placement, p.PolicySetDefaults.Placement) 778 779 // Sort alphabetically to make it deterministic 780 sort.Strings(plcset.Policies) 781 } 782 } 783 784 func applyDefaultDependencyFields(deps []types.PolicyDependency, namespace string) { 785 for i, dep := range deps { 786 if dep.Namespace == "" { 787 deps[i].Namespace = namespace 788 } 789 790 if dep.Compliance == "" { 791 deps[i].Compliance = "Compliant" 792 } 793 794 if dep.Kind == "" { 795 deps[i].Kind = policyKind 796 } 797 798 if dep.APIVersion == "" { 799 deps[i].APIVersion = policyAPIVersion 800 } 801 } 802 } 803 804 // applyDefaultPlacementFields is a helper for applyDefaults that handles default Placement configuration 805 func applyDefaultPlacementFields(placement *types.PlacementConfig, defaultPlacement types.PlacementConfig) { 806 // Determine whether defaults are set for placement 807 plcDefaultSet := len(defaultPlacement.LabelSelector) != 0 || 808 defaultPlacement.PlacementPath != "" || 809 defaultPlacement.PlacementName != "" 810 plrDefaultSet := len(defaultPlacement.ClusterSelectors) != 0 || 811 len(defaultPlacement.ClusterSelector) != 0 || 812 defaultPlacement.PlacementRulePath != "" || 813 defaultPlacement.PlacementRuleName != "" 814 815 // If both cluster label selectors and placement path/name aren't set, then use the defaults with a 816 // priority on placement path followed by placement name. 817 if len(placement.LabelSelector) == 0 && 818 placement.PlacementPath == "" && 819 placement.PlacementName == "" && 820 plcDefaultSet { 821 if defaultPlacement.PlacementPath != "" { 822 placement.PlacementPath = defaultPlacement.PlacementPath 823 } else if defaultPlacement.PlacementName != "" { 824 placement.PlacementName = defaultPlacement.PlacementName 825 } else if len(defaultPlacement.LabelSelector) > 0 { 826 placement.LabelSelector = defaultPlacement.LabelSelector 827 } 828 } else if len(placement.ClusterSelectors) == 0 && 829 // Else if both cluster selectors and placement rule path/name aren't set, then use the defaults with a 830 // priority on placement rule path followed by placement rule name. 831 len(placement.ClusterSelector) == 0 && 832 placement.PlacementRulePath == "" && 833 placement.PlacementRuleName == "" && 834 plrDefaultSet { 835 if defaultPlacement.PlacementRulePath != "" { 836 placement.PlacementRulePath = defaultPlacement.PlacementRulePath 837 } else if defaultPlacement.PlacementRuleName != "" { 838 placement.PlacementRuleName = defaultPlacement.PlacementRuleName 839 } else if len(defaultPlacement.ClusterSelectors) > 0 { 840 placement.ClusterSelectors = defaultPlacement.ClusterSelectors 841 } else if len(defaultPlacement.ClusterSelector) > 0 { 842 placement.ClusterSelector = defaultPlacement.ClusterSelector 843 } 844 } 845 } 846 847 // assertValidConfig verifies that the user provided configuration has all the 848 // required fields. Note that this should be run only after applyDefaults is run. 849 func (p *Plugin) assertValidConfig() error { 850 if p.PolicyDefaults.Namespace == "" { 851 return errors.New("policyDefaults.namespace is empty but it must be set") 852 } 853 854 // Validate default policy placement settings 855 err := p.assertValidPlacement(p.PolicyDefaults.Placement, "policyDefaults", nil) 856 if err != nil { 857 return err 858 } 859 860 // validate placement binding names are DNS compliant 861 if p.PlacementBindingDefaults.Name != "" && 862 len(validation.IsDNS1123Subdomain(p.PlacementBindingDefaults.Name)) > 0 { 863 return fmt.Errorf( 864 "PlacementBindingDefaults.Name `%s` is not DNS compliant. See %s", 865 p.PlacementBindingDefaults.Name, 866 dnsReference, 867 ) 868 } 869 870 if len(p.Policies) == 0 { 871 return errors.New("policies is empty but it must be set") 872 } 873 874 if p.PolicyDefaults.OrderPolicies && len(p.PolicyDefaults.Dependencies) != 0 { 875 return errors.New("policyDefaults must specify only one of dependencies or orderPolicies") 876 } 877 878 for i, dep := range p.PolicyDefaults.Dependencies { 879 if dep.Name == "" { 880 return fmt.Errorf("dependency name must be set in policyDefaults dependency %v", i) 881 } 882 } 883 884 if p.PolicyDefaults.OrderManifests && p.PolicyDefaults.ConsolidateManifests { 885 return errors.New("policyDefaults may not specify both consolidateManifests and orderManifests") 886 } 887 888 if len(p.PolicyDefaults.ExtraDependencies) > 0 && p.PolicyDefaults.OrderManifests { 889 return errors.New("policyDefaults may not specify both extraDependencies and orderManifests") 890 } 891 892 for i, dep := range p.PolicyDefaults.ExtraDependencies { 893 if dep.Name == "" { 894 return fmt.Errorf("extraDependency name must be set in policyDefaults extraDependency %v", i) 895 } 896 } 897 898 seenPlc := map[string]bool{} 899 plCount := struct { 900 plc int 901 plr int 902 }{} 903 904 for i := range p.Policies { 905 policy := &p.Policies[i] 906 if policy.Name == "" { 907 return fmt.Errorf( 908 "each policy must have a name set, but did not find a name at policy array index %d", i, 909 ) 910 } 911 912 if len(validation.IsDNS1123Subdomain(policy.Name)) > 0 { 913 return fmt.Errorf( 914 "policy name `%s` is not DNS compliant. See %s", policy.Name, dnsReference, 915 ) 916 } 917 918 if seenPlc[policy.Name] { 919 return fmt.Errorf( 920 "each policy must have a unique name set, but found a duplicate name: %s", policy.Name, 921 ) 922 } 923 924 seenPlc[policy.Name] = true 925 926 if len(p.PolicyDefaults.Namespace+"."+policy.Name) > maxObjectNameLength { 927 return fmt.Errorf("the policy namespace and name cannot be more than 63 characters: %s.%s", 928 p.PolicyDefaults.Namespace, policy.Name) 929 } 930 931 if policy.EvaluationInterval.Compliant != "" && policy.EvaluationInterval.Compliant != "never" { 932 _, err := time.ParseDuration(policy.EvaluationInterval.Compliant) 933 if err != nil { 934 return fmt.Errorf( 935 "the policy %s has an invalid policy.evaluationInterval.compliant value: %w", policy.Name, err, 936 ) 937 } 938 } 939 940 if policy.EvaluationInterval.NonCompliant != "" && policy.EvaluationInterval.NonCompliant != "never" { 941 _, err := time.ParseDuration(policy.EvaluationInterval.NonCompliant) 942 if err != nil { 943 return fmt.Errorf( 944 "the policy %s has an invalid policy.evaluationInterval.noncompliant value: %w", policy.Name, err, 945 ) 946 } 947 } 948 949 if len(policy.Manifests) == 0 { 950 return fmt.Errorf( 951 "each policy must have at least one manifest, but found none in policy %s", policy.Name, 952 ) 953 } 954 955 if len(policy.Dependencies) > 0 && p.PolicyDefaults.OrderPolicies { 956 return fmt.Errorf( 957 "dependencies may not be set in policy %v when policyDefaults.orderPolicies is true", policy.Name, 958 ) 959 } 960 961 for x, dep := range policy.Dependencies { 962 if dep.Name == "" { 963 return fmt.Errorf("dependency name must be set in policy %v dependency %v", policy.Name, x) 964 } 965 } 966 967 if policy.ConsolidateManifests && policy.OrderManifests { 968 return fmt.Errorf("policy %v may not set orderManifests when consolidateManifests is true", policy.Name) 969 } 970 971 if len(policy.ExtraDependencies) > 0 && policy.OrderManifests { 972 return fmt.Errorf("extraDependencies may not be set in policy %v when orderManifests is true", policy.Name) 973 } 974 975 for x, dep := range policy.ExtraDependencies { 976 if dep.Name == "" { 977 return fmt.Errorf("extraDependency name must be set in policy %v extraDependency %v", policy.Name, x) 978 } 979 } 980 981 for j := range policy.Manifests { 982 manifest := &policy.Manifests[j] 983 984 if manifest.Path == "" { 985 return fmt.Errorf( 986 "each policy manifest entry must have path set, but did not find a path in policy %s", 987 policy.Name, 988 ) 989 } 990 991 _, err := os.Stat(manifest.Path) 992 if err != nil { 993 return fmt.Errorf( 994 "could not read the manifest path %s in policy %s", manifest.Path, policy.Name, 995 ) 996 } 997 998 err = verifyManifestPath(p.baseDirectory, manifest.Path) 999 if err != nil { 1000 return err 1001 } 1002 1003 evalInterval := manifest.EvaluationInterval 1004 1005 // Verify that consolidated manifests don't specify fields 1006 // that can't be overridden at the objectTemplate level 1007 if policy.ConsolidateManifests { 1008 errorMsgFmt := fmt.Sprintf( 1009 "the policy %s has the %%s value set on manifest[%d] but consolidateManifests is true", 1010 policy.Name, j, 1011 ) 1012 1013 if evalInterval.Compliant != "" || evalInterval.NonCompliant != "" { 1014 return fmt.Errorf(errorMsgFmt, "evaluationInterval") 1015 } 1016 1017 selector := manifest.NamespaceSelector 1018 if selector.Exclude != nil || selector.Include != nil || 1019 selector.MatchLabels != nil || selector.MatchExpressions != nil { 1020 return fmt.Errorf(errorMsgFmt, "namespaceSelector") 1021 } 1022 1023 if manifest.PruneObjectBehavior != "" { 1024 return fmt.Errorf(errorMsgFmt, "pruneObjectBehavior") 1025 } 1026 1027 if manifest.RemediationAction != "" { 1028 return fmt.Errorf(errorMsgFmt, "remediationAction") 1029 } 1030 1031 if manifest.Severity != "" { 1032 return fmt.Errorf(errorMsgFmt, "severity") 1033 } 1034 1035 if len(manifest.ExtraDependencies) != 0 { 1036 return fmt.Errorf(errorMsgFmt, "extraDependencies") 1037 } 1038 1039 if manifest.IgnorePending { 1040 return fmt.Errorf(errorMsgFmt, "ignorePending") 1041 } 1042 } 1043 1044 if evalInterval.Compliant != "" && evalInterval.Compliant != "never" { 1045 _, err := time.ParseDuration(evalInterval.Compliant) 1046 if err != nil { 1047 return fmt.Errorf( 1048 "the policy %s has an invalid policy.evaluationInterval.manifest[%d].compliant value: %w", 1049 policy.Name, 1050 j, 1051 err, 1052 ) 1053 } 1054 } 1055 1056 if evalInterval.NonCompliant != "" && evalInterval.NonCompliant != "never" { 1057 _, err := time.ParseDuration(evalInterval.NonCompliant) 1058 if err != nil { 1059 return fmt.Errorf( 1060 "the policy %s has an invalid policy.evaluationInterval.manifest[%d].noncompliant value: %w", 1061 policy.Name, 1062 j, 1063 err, 1064 ) 1065 } 1066 } 1067 1068 if len(manifest.ExtraDependencies) > 0 && policy.OrderManifests { 1069 return fmt.Errorf( 1070 "extraDependencies may not be set in policy %v manifest[%d] because orderManifests is set", 1071 policy.Name, 1072 j, 1073 ) 1074 } 1075 1076 for x, dep := range manifest.ExtraDependencies { 1077 if dep.Name == "" { 1078 return fmt.Errorf( 1079 "extraDependency name must be set in policy %v manifest[%d] extraDependency %v", 1080 policy.Name, j, x) 1081 } 1082 } 1083 } 1084 1085 err := p.assertValidPlacement(policy.Placement, fmt.Sprintf("policy %s", policy.Name), &plCount) 1086 if err != nil { 1087 return err 1088 } 1089 } 1090 1091 // Validate default policy set placement settings 1092 err = p.assertValidPlacement(p.PolicySetDefaults.Placement, "policySetDefaults", nil) 1093 if err != nil { 1094 return err 1095 } 1096 1097 seenPlcset := map[string]bool{} 1098 1099 for i := range p.PolicySets { 1100 plcset := &p.PolicySets[i] 1101 1102 if plcset.Name == "" { 1103 return fmt.Errorf( 1104 "each policySet must have a name set, but did not find a name at policySet array index %d", i, 1105 ) 1106 } 1107 1108 if len(validation.IsDNS1123Subdomain(plcset.Name)) > 0 { 1109 return fmt.Errorf( 1110 "policy set name `%s` is not DNS compliant. See %s", plcset.Name, dnsReference, 1111 ) 1112 } 1113 1114 if seenPlcset[plcset.Name] { 1115 return fmt.Errorf( 1116 "each policySet must have a unique name set, but found a duplicate name: %s", plcset.Name, 1117 ) 1118 } 1119 1120 seenPlcset[plcset.Name] = true 1121 1122 // Validate policy set Placement settings 1123 err := p.assertValidPlacement(plcset.Placement, fmt.Sprintf("policySet %s", plcset.Name), &plCount) 1124 if err != nil { 1125 return err 1126 } 1127 } 1128 1129 // Validate only one type of placement kind is in use 1130 if plCount.plc != 0 && plCount.plr != 0 { 1131 return fmt.Errorf( 1132 "may not use a mix of Placement and PlacementRule for policies and policysets; found %d Placement and "+ 1133 "%d PlacementRule", 1134 plCount.plc, plCount.plr, 1135 ) 1136 } 1137 1138 p.usingPlR = plCount.plc == 0 1139 1140 return nil 1141 } 1142 1143 // assertValidPlacement is a helper for assertValidConfig to verify placement configurations 1144 func (p *Plugin) assertValidPlacement( 1145 placement types.PlacementConfig, 1146 path string, 1147 plCount *struct { 1148 plc int 1149 plr int 1150 }, 1151 ) error { 1152 if placement.PlacementRulePath != "" && placement.PlacementPath != "" { 1153 return fmt.Errorf( 1154 "%s must provide only one of placement.placementPath or placement.placementRulePath", path, 1155 ) 1156 } 1157 1158 if (len(placement.ClusterSelectors) > 0 || len(placement.ClusterSelector) > 0) && 1159 len(placement.LabelSelector) > 0 { 1160 return fmt.Errorf( 1161 "%s must provide only one of placement.labelSelector or placement.clusterSelectors", path, 1162 ) 1163 } 1164 1165 if placement.PlacementRuleName != "" && placement.PlacementName != "" { 1166 return fmt.Errorf( 1167 "%s must provide only one of placement.placementName or placement.placementRuleName", path, 1168 ) 1169 } 1170 1171 placementOptionCount := 0 1172 if len(placement.LabelSelector) != 0 || len(placement.ClusterSelectors) != 0 || 1173 len(placement.ClusterSelector) != 0 { 1174 placementOptionCount++ 1175 } 1176 1177 if placement.PlacementRulePath != "" || placement.PlacementPath != "" { 1178 placementOptionCount++ 1179 } 1180 1181 if placement.PlacementRuleName != "" || placement.PlacementName != "" { 1182 placementOptionCount++ 1183 } 1184 1185 if placementOptionCount > 1 { 1186 return fmt.Errorf( 1187 "%s must specify only one of placement selector, placement path, or placement name", path, 1188 ) 1189 } 1190 1191 // validate placement names are DNS compliant 1192 defPlrName := placement.PlacementRuleName 1193 if defPlrName != "" && len(validation.IsDNS1123Subdomain(defPlrName)) > 0 { 1194 return fmt.Errorf( 1195 "%s placement.placementRuleName placement name `%s` is not DNS compliant. See %s", 1196 path, 1197 defPlrName, 1198 dnsReference, 1199 ) 1200 } 1201 1202 defPlcmtPlName := placement.PlacementName 1203 if defPlcmtPlName != "" && len(validation.IsDNS1123Subdomain(defPlcmtPlName)) > 0 { 1204 return fmt.Errorf( 1205 "%s placement.placementName `%s` is not DNS compliant. See %s", 1206 path, 1207 defPlcmtPlName, 1208 dnsReference, 1209 ) 1210 } 1211 1212 defPlName := placement.Name 1213 if defPlName != "" && len(validation.IsDNS1123Subdomain(defPlName)) > 0 { 1214 return fmt.Errorf( 1215 "%s placement.name `%s` is not DNS compliant. See %s", path, defPlName, dnsReference, 1216 ) 1217 } 1218 1219 if placement.PlacementRulePath != "" { 1220 _, err := os.Stat(placement.PlacementRulePath) 1221 if err != nil { 1222 return fmt.Errorf( 1223 "%s placement.placementRulePath could not read the path %s", 1224 path, placement.PlacementRulePath, 1225 ) 1226 } 1227 } 1228 1229 if placement.PlacementPath != "" { 1230 _, err := os.Stat(placement.PlacementPath) 1231 if err != nil { 1232 return fmt.Errorf( 1233 "%s placement.placementPath could not read the path %s", 1234 path, placement.PlacementPath, 1235 ) 1236 } 1237 } 1238 1239 if plCount != nil { 1240 foundPl := false 1241 1242 if len(placement.LabelSelector) != 0 || 1243 placement.PlacementPath != "" || 1244 placement.PlacementName != "" { 1245 plCount.plc++ 1246 1247 foundPl = true 1248 } 1249 1250 if len(placement.ClusterSelectors) != 0 || 1251 len(placement.ClusterSelector) != 0 || 1252 placement.PlacementRulePath != "" || 1253 placement.PlacementRuleName != "" { 1254 plCount.plr++ 1255 1256 if foundPl { 1257 return fmt.Errorf( 1258 "%s may not use both Placement and PlacementRule kinds", path, 1259 ) 1260 } 1261 } 1262 } 1263 1264 if len(placement.ClusterSelectors) > 0 && len(placement.ClusterSelector) > 0 { 1265 return fmt.Errorf("cannot use both clusterSelector and clusterSelectors in %s placement config "+ 1266 "(clusterSelector is recommended since it matches the actual placement field)", path) 1267 } 1268 1269 // Determine which selectors to use 1270 var resolvedSelectors map[string]interface{} 1271 if len(placement.ClusterSelectors) > 0 { 1272 resolvedSelectors = placement.ClusterSelectors 1273 } else if len(placement.ClusterSelector) > 0 { 1274 resolvedSelectors = placement.ClusterSelector 1275 } else if len(placement.LabelSelector) > 0 { 1276 resolvedSelectors = placement.LabelSelector 1277 } 1278 1279 _, err := p.generateSelector(resolvedSelectors) 1280 if err != nil { 1281 return fmt.Errorf("%s placement has invalid selectors: %w", path, err) 1282 } 1283 1284 return nil 1285 } 1286 1287 // createPolicy will generate the root policy based on the PolicyGenerator configuration. 1288 // The generated policy is written to the plugin's output buffer. An error is returned if the 1289 // manifests specified in the configuration are invalid or can't be read. 1290 func (p *Plugin) createPolicy(policyConf *types.PolicyConfig) error { 1291 policyTemplates, err := getPolicyTemplates(policyConf) 1292 if err != nil { 1293 return err 1294 } 1295 1296 if policyConf.PolicyAnnotations == nil { 1297 policyConf.PolicyAnnotations = map[string]string{} 1298 } 1299 1300 policyConf.PolicyAnnotations["policy.open-cluster-management.io/categories"] = strings.Join( 1301 policyConf.Categories, ",", 1302 ) 1303 policyConf.PolicyAnnotations["policy.open-cluster-management.io/controls"] = strings.Join( 1304 policyConf.Controls, ",", 1305 ) 1306 policyConf.PolicyAnnotations["policy.open-cluster-management.io/standards"] = strings.Join( 1307 policyConf.Standards, ",", 1308 ) 1309 1310 spec := map[string]interface{}{ 1311 "disabled": policyConf.Disabled, 1312 "policy-templates": policyTemplates, 1313 } 1314 1315 if p.PolicyDefaults.OrderPolicies && p.previousPolicyName != "" { 1316 policyConf.Dependencies = []types.PolicyDependency{{ 1317 Name: p.previousPolicyName, 1318 Namespace: p.PolicyDefaults.Namespace, 1319 Compliance: "Compliant", 1320 Kind: policyKind, 1321 APIVersion: policyAPIVersion, 1322 }} 1323 } 1324 1325 p.previousPolicyName = policyConf.Name 1326 1327 if len(policyConf.Dependencies) != 0 { 1328 spec["dependencies"] = policyConf.Dependencies 1329 } 1330 1331 // When copyPolicyMetadata is unset, it defaults to the behavior of true, so this leaves it out entirely when set to 1332 // true to avoid unnecessarily including it in the Policy YAML. 1333 if !policyConf.CopyPolicyMetadata { 1334 spec["copyPolicyMetadata"] = false 1335 } 1336 1337 policy := map[string]interface{}{ 1338 "apiVersion": policyAPIVersion, 1339 "kind": policyKind, 1340 "metadata": map[string]interface{}{ 1341 "annotations": policyConf.PolicyAnnotations, 1342 "name": policyConf.Name, 1343 "namespace": p.PolicyDefaults.Namespace, 1344 }, 1345 "spec": spec, 1346 } 1347 1348 // set the root policy remediation action if all the remediation actions match 1349 if rootRemediationAction := getRootRemediationAction(policyTemplates); rootRemediationAction != "" { 1350 policy["spec"].(map[string]interface{})["remediationAction"] = rootRemediationAction 1351 } 1352 1353 policyYAML, err := yaml.Marshal(policy) 1354 if err != nil { 1355 return fmt.Errorf( 1356 "an unexpected error occurred when converting the policy to YAML: %w", err, 1357 ) 1358 } 1359 1360 p.outputBuffer.Write([]byte("---\n")) 1361 p.outputBuffer.Write(policyYAML) 1362 1363 return nil 1364 } 1365 1366 // createPolicySet will generate the policyset based on the Policy Generator configuration. 1367 // The generated policyset is written to the plugin's output buffer. An error is returned if the 1368 // manifests specified in the configuration are invalid or can't be read. 1369 func (p *Plugin) createPolicySet(policySetConf *types.PolicySetConfig) error { 1370 policyset := map[string]interface{}{ 1371 "apiVersion": policySetAPIVersion, 1372 "kind": policySetKind, 1373 "metadata": map[string]interface{}{ 1374 "name": policySetConf.Name, 1375 "namespace": p.PolicyDefaults.Namespace, // policyset should be generated in the same namespace of policy 1376 }, 1377 "spec": map[string]interface{}{ 1378 "description": policySetConf.Description, 1379 "policies": policySetConf.Policies, 1380 }, 1381 } 1382 1383 policysetYAML, err := yaml.Marshal(policyset) 1384 if err != nil { 1385 return fmt.Errorf( 1386 "an unexpected error occurred when converting the policyset to YAML: %w", err, 1387 ) 1388 } 1389 1390 p.outputBuffer.Write([]byte("---\n")) 1391 p.outputBuffer.Write(policysetYAML) 1392 1393 return nil 1394 } 1395 1396 // getPlcFromPath finds the placement manifest in the input manifest file. It will return the name 1397 // of the placement, the unmarshaled placement manifest, and an error. An error is returned if the 1398 // placement manifest cannot be found or is invalid. 1399 func (p *Plugin) getPlcFromPath(plcPath string) (string, map[string]interface{}, error) { 1400 manifests, err := unmarshalManifestFile(plcPath) 1401 if err != nil { 1402 return "", nil, fmt.Errorf("failed to read the placement: %w", err) 1403 } 1404 1405 var name string 1406 var placement map[string]interface{} 1407 1408 for _, manifest := range manifests { 1409 kind, _, _ := unstructured.NestedString(manifest, "kind") 1410 if kind != placementRuleKind && kind != placementKind { 1411 continue 1412 } 1413 1414 // Validate PlacementRule Kind given in manifest 1415 if kind == placementRuleKind { 1416 if !p.usingPlR { 1417 return "", nil, fmt.Errorf( 1418 "the placement %s specified a placementRule kind but expected a placement kind", plcPath, 1419 ) 1420 } 1421 } 1422 1423 // Validate Placement Kind given in manifest 1424 if kind == placementKind { 1425 if p.usingPlR { 1426 return "", nil, fmt.Errorf( 1427 "the placement %s specified a placement kind but expected a placementRule kind", plcPath, 1428 ) 1429 } 1430 } 1431 1432 var found bool 1433 name, found, err = unstructured.NestedString(manifest, "metadata", "name") 1434 1435 if !found || err != nil { 1436 return "", nil, fmt.Errorf("the placement %s must have a name set", plcPath) 1437 } 1438 1439 var namespace string 1440 namespace, found, err = unstructured.NestedString(manifest, "metadata", "namespace") 1441 1442 if !found || err != nil { 1443 return "", nil, fmt.Errorf("the placement %s must have a namespace set", plcPath) 1444 } 1445 1446 if namespace != p.PolicyDefaults.Namespace { 1447 err = fmt.Errorf( 1448 "the placement %s must have the same namespace as the policy (%s)", 1449 plcPath, 1450 p.PolicyDefaults.Namespace, 1451 ) 1452 1453 return "", nil, err 1454 } 1455 1456 placement = manifest 1457 1458 break 1459 } 1460 1461 if name == "" { 1462 err = fmt.Errorf( 1463 "the placement manifest %s did not have a placement", plcPath, 1464 ) 1465 1466 return "", nil, err 1467 } 1468 1469 return name, placement, nil 1470 } 1471 1472 // getCsKey generates the key for the policy's cluster/label selectors to be used in 1473 // Policies.csToPlc. 1474 func getCsKey(placementConfig types.PlacementConfig) string { 1475 return fmt.Sprintf("%#v", placementConfig.ClusterSelectors) 1476 } 1477 1478 // getPlcName will generate a placement name for the policy. If the placement has 1479 // previously been generated, skip will be true. 1480 func (p *Plugin) getPlcName( 1481 defaultPlacementConfig types.PlacementConfig, 1482 placementConfig types.PlacementConfig, 1483 nameDefault string, 1484 ) (string, bool) { 1485 if placementConfig.Name != "" { 1486 // If the policy explicitly specifies a placement name, use it 1487 return placementConfig.Name, false 1488 } else if defaultPlacementConfig.Name != "" || p.PolicyDefaults.Placement.Name != "" { 1489 // Prioritize the provided default but fall back to policyDefaults 1490 defaultPlacementName := p.PolicyDefaults.Placement.Name 1491 if defaultPlacementConfig.Name != "" { 1492 defaultPlacementName = defaultPlacementConfig.Name 1493 } 1494 // If the policy doesn't explicitly specify a placement name, and there is a 1495 // default placement name set, check if one has already been generated for these 1496 // cluster/label selectors 1497 csKey := getCsKey(placementConfig) 1498 if _, ok := p.csToPlc[csKey]; ok { 1499 // Just reuse the previously created placement with the same cluster/label selectors 1500 return p.csToPlc[csKey], true 1501 } 1502 // If the policy doesn't explicitly specify a placement name, and there is a 1503 // default placement name, use that 1504 if len(p.csToPlc) == 0 { 1505 // If this is the first generated placement, just use it as is 1506 return defaultPlacementName, false 1507 } 1508 // If there is already one or more generated placements, increment the name 1509 return fmt.Sprintf("%s%d", defaultPlacementName, len(p.csToPlc)+1), false 1510 } 1511 // Default to a placement per policy 1512 return "placement-" + nameDefault, false 1513 } 1514 1515 func (p *Plugin) createPolicyPlacement(placementConfig types.PlacementConfig, nameDefault string) ( 1516 name string, err error, 1517 ) { 1518 return p.createPlacement(p.PolicyDefaults.Placement, placementConfig, nameDefault) 1519 } 1520 1521 func (p *Plugin) createPolicySetPlacement(placementConfig types.PlacementConfig, nameDefault string) ( 1522 name string, err error, 1523 ) { 1524 return p.createPlacement(p.PolicySetDefaults.Placement, placementConfig, nameDefault) 1525 } 1526 1527 // createPlacement creates a placement for the input placement config and default name by writing it to 1528 // the policy generator's output buffer. The name of the placement or an error is returned. 1529 // If the placement has already been generated, it will be reused and not added to the 1530 // policy generator's output buffer. An error is returned if the placement cannot be created. 1531 func (p *Plugin) createPlacement( 1532 defaultPlacementConfig types.PlacementConfig, 1533 placementConfig types.PlacementConfig, 1534 nameDefault string) ( 1535 name string, err error, 1536 ) { 1537 // If a placementName or placementRuleName is defined just return it 1538 if placementConfig.PlacementName != "" { 1539 name = placementConfig.PlacementName 1540 1541 return 1542 } 1543 1544 if placementConfig.PlacementRuleName != "" { 1545 name = placementConfig.PlacementRuleName 1546 1547 return 1548 } 1549 1550 plrPath := placementConfig.PlacementRulePath 1551 plcPath := placementConfig.PlacementPath 1552 var placement map[string]interface{} 1553 // If a path to a placement is provided, find the placement and reuse it. 1554 if plrPath != "" || plcPath != "" { 1555 var resolvedPlPath string 1556 if plrPath != "" { 1557 resolvedPlPath = plrPath 1558 } else { 1559 resolvedPlPath = plcPath 1560 } 1561 1562 name, placement, err = p.getPlcFromPath(resolvedPlPath) 1563 if err != nil { 1564 return 1565 } 1566 1567 // processedPlcs keeps track of which placements have been seen by name. This is so 1568 // that if the same placement path is provided for multiple policies, it's not re-included 1569 // in the generated output of the plugin. 1570 if p.processedPlcs[name] { 1571 return 1572 } 1573 1574 p.processedPlcs[name] = true 1575 } else { 1576 var skip bool 1577 name, skip = p.getPlcName(defaultPlacementConfig, placementConfig, nameDefault) 1578 if skip { 1579 return 1580 } 1581 1582 // Determine which selectors to use 1583 var resolvedSelectors map[string]interface{} 1584 if len(placementConfig.ClusterSelectors) > 0 { 1585 resolvedSelectors = placementConfig.ClusterSelectors 1586 } else if len(placementConfig.ClusterSelector) > 0 { 1587 resolvedSelectors = placementConfig.ClusterSelector 1588 } else if len(placementConfig.LabelSelector) > 0 { 1589 resolvedSelectors = placementConfig.LabelSelector 1590 } 1591 1592 // Build cluster selector object 1593 selectorObj, err := p.generateSelector(resolvedSelectors) 1594 if err != nil { 1595 return "", err 1596 } 1597 1598 if p.usingPlR { 1599 placement = map[string]interface{}{ 1600 "apiVersion": placementRuleAPIVersion, 1601 "kind": placementRuleKind, 1602 "metadata": map[string]interface{}{ 1603 "name": name, 1604 "namespace": p.PolicyDefaults.Namespace, 1605 }, 1606 "spec": map[string]interface{}{ 1607 "clusterSelector": selectorObj, 1608 }, 1609 } 1610 } else { 1611 placement = map[string]interface{}{ 1612 "apiVersion": placementAPIVersion, 1613 "kind": placementKind, 1614 "metadata": map[string]interface{}{ 1615 "name": name, 1616 "namespace": p.PolicyDefaults.Namespace, 1617 }, 1618 "spec": map[string]interface{}{ 1619 "predicates": []map[string]interface{}{ 1620 { 1621 "requiredClusterSelector": map[string]interface{}{ 1622 "labelSelector": selectorObj, 1623 }, 1624 }, 1625 }, 1626 }, 1627 } 1628 } 1629 1630 csKey := getCsKey(placementConfig) 1631 p.csToPlc[csKey] = name 1632 } 1633 1634 if p.allPlcs[name] { 1635 return "", fmt.Errorf("a duplicate placement name was detected: %s", name) 1636 } 1637 1638 p.allPlcs[name] = true 1639 1640 var placementYAML []byte 1641 1642 placementYAML, err = yaml.Marshal(placement) 1643 if err != nil { 1644 err = fmt.Errorf( 1645 "an unexpected error occurred when converting the placement to YAML: %w", err, 1646 ) 1647 1648 return 1649 } 1650 1651 p.outputBuffer.Write([]byte("---\n")) 1652 p.outputBuffer.Write(placementYAML) 1653 1654 return 1655 } 1656 1657 // generateSelector determines the type of input and creates a map of selectors to be used in either the 1658 // clusterSelector or labelSelector field 1659 func (p *Plugin) generateSelector( 1660 resolvedSelectors map[string]interface{}, 1661 ) (map[string]interface{}, error) { 1662 if resolvedSelectors == nil { 1663 return map[string]interface{}{"matchExpressions": []interface{}{}}, nil 1664 } 1665 1666 resolvedSelectorsJSON, err := json.Marshal(resolvedSelectors) 1667 if err != nil { 1668 return nil, err 1669 } 1670 1671 resolvedSelectorsLS := metav1.LabelSelector{} 1672 decoder := json.NewDecoder(bytes.NewReader(resolvedSelectorsJSON)) 1673 decoder.DisallowUnknownFields() 1674 1675 err = decoder.Decode(&resolvedSelectorsLS) 1676 if err != nil { 1677 resolvedSelectorsLS = metav1.LabelSelector{} 1678 1679 // Check if it's a legacy selector 1680 for label, value := range resolvedSelectors { 1681 valueStr, ok := value.(string) 1682 if !ok { 1683 return nil, fmt.Errorf( 1684 "the input is not a valid label selector or key-value label matching map", 1685 ) 1686 } 1687 1688 lsReq := metav1.LabelSelectorRequirement{Key: label} 1689 1690 if valueStr == "" { 1691 lsReq.Operator = metav1.LabelSelectorOpExists 1692 } else { 1693 lsReq.Operator = metav1.LabelSelectorOpIn 1694 lsReq.Values = []string{valueStr} 1695 } 1696 1697 resolvedSelectorsLS.MatchExpressions = append(resolvedSelectorsLS.MatchExpressions, lsReq) 1698 } 1699 1700 resolved, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&resolvedSelectorsLS) 1701 if err != nil { 1702 panic(err) 1703 } 1704 1705 return resolved, nil 1706 } 1707 1708 return resolvedSelectors, nil 1709 } 1710 1711 // createPlacementBinding creates a placement binding for the input placement, policies and policy sets by 1712 // writing it to the policy generator's output buffer. An error is returned if the placement binding 1713 // cannot be created. 1714 func (p *Plugin) createPlacementBinding( 1715 bindingName, plcName string, policyConfs []*types.PolicyConfig, policySetConfs []*types.PolicySetConfig, 1716 ) error { 1717 subjects := make([]map[string]string, 0, len(policyConfs)+len(policySetConfs)) 1718 1719 for _, policyConf := range policyConfs { 1720 subject := map[string]string{ 1721 "apiGroup": policyAPIGroup, 1722 "kind": policyKind, 1723 "name": policyConf.Name, 1724 } 1725 subjects = append(subjects, subject) 1726 } 1727 1728 for _, policySetConf := range policySetConfs { 1729 subject := map[string]string{ 1730 "apiGroup": policyAPIGroup, 1731 "kind": policySetKind, 1732 "name": policySetConf.Name, 1733 } 1734 subjects = append(subjects, subject) 1735 } 1736 1737 var resolvedPlcKind string 1738 var resolvedPlcAPIVersion string 1739 1740 if p.usingPlR { 1741 resolvedPlcKind = placementRuleKind 1742 resolvedPlcAPIVersion = placementRuleAPIVersion 1743 } else { 1744 resolvedPlcKind = placementKind 1745 resolvedPlcAPIVersion = placementAPIVersion 1746 } 1747 1748 binding := map[string]interface{}{ 1749 "apiVersion": placementBindingAPIVersion, 1750 "kind": placementBindingKind, 1751 "metadata": map[string]interface{}{ 1752 "name": bindingName, 1753 "namespace": p.PolicyDefaults.Namespace, 1754 }, 1755 "placementRef": map[string]string{ 1756 // Remove the version at the end 1757 "apiGroup": strings.Split(resolvedPlcAPIVersion, "/")[0], 1758 "name": plcName, 1759 "kind": resolvedPlcKind, 1760 }, 1761 "subjects": subjects, 1762 } 1763 1764 bindingYAML, err := yaml.Marshal(binding) 1765 if err != nil { 1766 return fmt.Errorf( 1767 "an unexpected error occurred when converting the placement binding to YAML: %w", err, 1768 ) 1769 } 1770 1771 p.outputBuffer.Write([]byte("---\n")) 1772 p.outputBuffer.Write(bindingYAML) 1773 1774 return nil 1775 }