Skip to content

Commit fc71481

Browse files
author
Yu Cao
authored
Add placement generation for policy sets. (#34)
Refactor `createPlacement` and `createPlacementBinding` methods to generate placement and placement binding when policy sets are defined. Signed-off-by: Yu Cao <[email protected]>
1 parent 35defb7 commit fc71481

File tree

3 files changed

+357
-61
lines changed

3 files changed

+357
-61
lines changed

examples/policyGenerator.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,9 @@ policySets:
6262
description: this is a kyverno policy set.
6363
policies:
6464
- pre-exists-kyverno-policy
65+
placement:
66+
placementRulePath: input/placementrule.yaml
6567
- name: policyset-gatekeeper
66-
description: this is a gatekeeper policy set.
68+
description: this is a gatekeeper policy set.
69+
placement:
70+
placementRulePath: input/placementrule.yaml

internal/plugin.go

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -114,65 +114,92 @@ func (p *Plugin) Generate() ([]byte, error) {
114114
}
115115
}
116116

117-
// Keep track of which placement maps to which policy. This will be used to determine
117+
// Keep track of which placement maps to which policy and policySet. This will be used to determine
118118
// how many placement bindings are required since one binding per placement is required.
119-
plcNameToPolicyIdxs := map[string][]int{}
119+
// plcNameToPolicyAndSetIdxs[plcName]["policy"] stores the index of policy
120+
// plcNameToPolicyAndSetIdxs[plcName]["policyset"] stores the index of policyset
121+
plcNameToPolicyAndSetIdxs := map[string]map[string][]int{}
120122
for i := range p.Policies {
121123
// only generate placement when GeneratePlacementWhenInSet equals to true or policy is not
122124
// part of any policy sets
123125
if p.Policies[i].GeneratePlacementWhenInSet || len(p.Policies[i].PolicySets) == 0 {
124-
plcName, err := p.createPlacement(&p.Policies[i])
126+
plcName, err := p.createPlacement(&p.Policies[i].Placement, p.Policies[i].Name)
125127
if err != nil {
126128
return nil, err
127129
}
128-
plcNameToPolicyIdxs[plcName] = append(plcNameToPolicyIdxs[plcName], i)
130+
if plcNameToPolicyAndSetIdxs[plcName] == nil {
131+
plcNameToPolicyAndSetIdxs[plcName] = map[string][]int{}
132+
}
133+
plcNameToPolicyAndSetIdxs[plcName]["policy"] = append(plcNameToPolicyAndSetIdxs[plcName]["policy"], i)
129134
}
130135
}
131136

132-
// Sort the keys of plcNameToPolicyIdxs so that the policy bindings are generated in a
137+
for i := range p.PolicySets {
138+
plcName, err := p.createPlacement(&p.PolicySets[i].Placement, p.PolicySets[i].Name)
139+
if err != nil {
140+
return nil, err
141+
}
142+
if plcNameToPolicyAndSetIdxs[plcName] == nil {
143+
plcNameToPolicyAndSetIdxs[plcName] = map[string][]int{}
144+
}
145+
plcNameToPolicyAndSetIdxs[plcName]["policyset"] =
146+
append(plcNameToPolicyAndSetIdxs[plcName]["policyset"], i)
147+
}
148+
149+
// Sort the keys of plcNameToPolicyseetsIdxs so that the policy bindings are generated in a
133150
// consistent order.
134-
plcNames := make([]string, len(plcNameToPolicyIdxs))
151+
plcNames := make([]string, len(plcNameToPolicyAndSetIdxs))
135152
i := 0
136-
for k := range plcNameToPolicyIdxs {
153+
for k := range plcNameToPolicyAndSetIdxs {
137154
plcNames[i] = k
138155
i++
139156
}
140157
sort.Strings(plcNames)
141158

142159
plcBindingCount := 0
143160
for _, plcName := range plcNames {
144-
// Determine which policies to be included in the placement binding.
161+
// Determine which policies and policy sets to be included in the placement binding.
145162
policyConfs := []*types.PolicyConfig{}
146-
for _, i := range plcNameToPolicyIdxs[plcName] {
163+
for _, i := range plcNameToPolicyAndSetIdxs[plcName]["policy"] {
147164
policyConfs = append(policyConfs, &p.Policies[i])
148165
}
166+
policySetConfs := []*types.PolicySetConfig{}
167+
for _, i := range plcNameToPolicyAndSetIdxs[plcName]["policyset"] {
168+
policySetConfs = append(policySetConfs, &p.PolicySets[i])
169+
}
149170

150171
// If there is more than one policy associated with a placement but no default binding name
151172
// specified, throw an error
152-
if len(policyConfs) > 1 && p.PlacementBindingDefaults.Name == "" {
173+
if (len(policyConfs) > 1 || len(policySetConfs) > 1) && p.PlacementBindingDefaults.Name == "" {
153174
return nil, fmt.Errorf(
154-
"placementBindingDefaults.name must be set but is empty (multiple policies were found for the PlacementBinding to placement %s)",
175+
"placementBindingDefaults.name must be set but is empty (multiple policies or policy sets were found for the PlacementBinding to placement %s)",
155176
plcName,
156177
)
157178
}
158179

159180
var bindingName string
160-
// If there is only one policy, use the policy name if there is no default
181+
existMultiple := false
182+
// If there is only one policy or one policy set, use the policy or policy set name if there is no default
161183
// binding name specified
162-
if len(policyConfs) == 1 && p.PlacementBindingDefaults.Name == "" {
184+
if len(policyConfs) == 1 && len(policySetConfs) == 0 {
163185
bindingName = "binding-" + policyConfs[0].Name
186+
} else if len(policyConfs) == 0 && len(policySetConfs) == 0 {
187+
bindingName = "binding-" + policySetConfs[0].Name
164188
} else {
189+
existMultiple = true
190+
}
191+
// If there are multiple policies or policy sets, use the default placement binding name
192+
// but append a number to it so it's a unique name.
193+
if p.PlacementBindingDefaults.Name != "" && existMultiple {
165194
plcBindingCount++
166-
// If there are multiple policies, use the default placement binding name
167-
// but append a number to it so it's a unique name.
168195
if plcBindingCount == 1 {
169196
bindingName = p.PlacementBindingDefaults.Name
170197
} else {
171198
bindingName = fmt.Sprintf("%s%d", p.PlacementBindingDefaults.Name, plcBindingCount)
172199
}
173200
}
174201

175-
err := p.createPlacementBinding(bindingName, plcName, policyConfs)
202+
err := p.createPlacementBinding(bindingName, plcName, policyConfs, policySetConfs)
176203
if err != nil {
177204
return nil, fmt.Errorf("failed to create a placement binding: %w", err)
178205
}
@@ -271,6 +298,7 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) {
271298
if p.PolicyDefaults.Standards == nil {
272299
p.PolicyDefaults.Standards = defaults.Standards
273300
}
301+
274302
// Generate temporary sets to later merge the policy sets declared in p.Policies[*] and p.PolicySets
275303
plcsetToPlc := make(map[string]map[string]bool)
276304
plcToPlcset := make(map[string]map[string]bool)
@@ -717,21 +745,21 @@ func (p *Plugin) getPlcFromPath(plcPath string) (string, map[string]interface{},
717745

718746
// getCsKey generates the key for the policy's cluster/label selectors to be used in
719747
// Policies.csToPlc.
720-
func getCsKey(policyConf *types.PolicyConfig) string {
721-
return fmt.Sprintf("%#v", policyConf.Placement.ClusterSelectors)
748+
func getCsKey(placementConfig *types.PlacementConfig) string {
749+
return fmt.Sprintf("%#v", placementConfig.ClusterSelectors)
722750
}
723751

724752
// getPlcName will generate a placement name for the policy. If the placement has
725753
// previously been generated, skip will be true.
726-
func (p *Plugin) getPlcName(policyConf *types.PolicyConfig) (name string, skip bool) {
727-
if policyConf.Placement.Name != "" {
754+
func (p *Plugin) getPlcName(placementConfig *types.PlacementConfig, nameDefault string) (string, bool) {
755+
if placementConfig.Name != "" {
728756
// If the policy explicitly specifies a placement name, use it
729-
return policyConf.Placement.Name, false
757+
return placementConfig.Name, false
730758
} else if p.PolicyDefaults.Placement.Name != "" {
731759
// If the policy doesn't explicitly specify a placement name, and there is a
732760
// default placement name set, check if one has already been generated for these
733761
// cluster/label selectors
734-
csKey := getCsKey(policyConf)
762+
csKey := getCsKey(placementConfig)
735763
if _, ok := p.csToPlc[csKey]; ok {
736764
// Just reuse the previously created placement with the same cluster/label selectors
737765
return p.csToPlc[csKey], true
@@ -746,18 +774,18 @@ func (p *Plugin) getPlcName(policyConf *types.PolicyConfig) (name string, skip b
746774
return fmt.Sprintf("%s%d", p.PolicyDefaults.Placement.Name, len(p.csToPlc)+1), false
747775
}
748776
// Default to a placement per policy
749-
return "placement-" + policyConf.Name, false
777+
return "placement-" + nameDefault, false
750778
}
751779

752-
// createPlacement creates a placement for the input policy configuration by writing it to
780+
// createPlacement creates a placement for the input placement config and default name by writing it to
753781
// the policy generator's output buffer. The name of the placement or an error is returned.
754782
// If the placement has already been generated, it will be reused and not added to the
755783
// policy generator's output buffer. An error is returned if the placement cannot be created.
756-
func (p *Plugin) createPlacement(policyConf *types.PolicyConfig) (
784+
func (p *Plugin) createPlacement(placementConfig *types.PlacementConfig, nameDefault string) (
757785
name string, err error,
758786
) {
759-
plrPath := policyConf.Placement.PlacementRulePath
760-
plcPath := policyConf.Placement.PlacementPath
787+
plrPath := placementConfig.PlacementRulePath
788+
plcPath := placementConfig.PlacementPath
761789
var placement map[string]interface{}
762790
// If a path to a placement is provided, find the placement and reuse it.
763791
if plrPath != "" || plcPath != "" {
@@ -782,17 +810,17 @@ func (p *Plugin) createPlacement(policyConf *types.PolicyConfig) (
782810
p.processedPlcs[name] = true
783811
} else {
784812
var skip bool
785-
name, skip = p.getPlcName(policyConf)
813+
name, skip = p.getPlcName(placementConfig, nameDefault)
786814
if skip {
787815
return
788816
}
789817

790818
// Determine which selectors to use
791819
var resolvedSelectors map[string]string
792-
if len(policyConf.Placement.ClusterSelectors) > 0 {
793-
resolvedSelectors = policyConf.Placement.ClusterSelectors
794-
} else if len(policyConf.Placement.LabelSelector) > 0 {
795-
resolvedSelectors = policyConf.Placement.LabelSelector
820+
if len(placementConfig.ClusterSelectors) > 0 {
821+
resolvedSelectors = placementConfig.ClusterSelectors
822+
} else if len(placementConfig.LabelSelector) > 0 {
823+
resolvedSelectors = placementConfig.LabelSelector
796824
}
797825

798826
// Sort the keys so that the match expressions can be ordered based on the label name
@@ -855,7 +883,7 @@ func (p *Plugin) createPlacement(policyConf *types.PolicyConfig) (
855883
}
856884
}
857885

858-
csKey := getCsKey(policyConf)
886+
csKey := getCsKey(placementConfig)
859887
p.csToPlc[csKey] = name
860888
}
861889

@@ -880,13 +908,13 @@ func (p *Plugin) createPlacement(policyConf *types.PolicyConfig) (
880908
return
881909
}
882910

883-
// createPlacementBinding creates a placement binding for the input placement and policies by
911+
// createPlacementBinding creates a placement binding for the input placement, policies and policy sets by
884912
// writing it to the policy generator's output buffer. An error is returned if the placement binding
885913
// cannot be created.
886914
func (p *Plugin) createPlacementBinding(
887-
bindingName, plcName string, policyConfs []*types.PolicyConfig,
915+
bindingName, plcName string, policyConfs []*types.PolicyConfig, policySetConfs []*types.PolicySetConfig,
888916
) error {
889-
subjects := make([]map[string]string, 0, len(policyConfs))
917+
subjects := make([]map[string]string, 0, len(policyConfs)+len(policySetConfs))
890918
for _, policyConf := range policyConfs {
891919
subject := map[string]string{
892920
// Remove the version at the end
@@ -897,6 +925,16 @@ func (p *Plugin) createPlacementBinding(
897925
subjects = append(subjects, subject)
898926
}
899927

928+
for _, policySetConf := range policySetConfs {
929+
subject := map[string]string{
930+
// Remove the version at the end
931+
"apiGroup": strings.Split(policyAPIVersion, "/")[0],
932+
"kind": policySetKind,
933+
"name": policySetConf.Name,
934+
}
935+
subjects = append(subjects, subject)
936+
}
937+
900938
var resolvedPlcKind string
901939
var resolvedPlcAPIVersion string
902940
if p.usingPlR {

0 commit comments

Comments
 (0)