mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
- fix any/all during policy mutation; - add unit tests (#2388)
Signed-off-by: Shuting Zhao <shutting06@gmail.com>
This commit is contained in:
parent
e288ed7fd2
commit
efdb7881ef
2 changed files with 160 additions and 15 deletions
|
@ -88,28 +88,92 @@ func GenerateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, log logr.Logg
|
|||
func checkForGVKFormatPatch(policy *kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
|
||||
patches = make([][]byte, 0)
|
||||
for i, rule := range policy.Spec.Rules {
|
||||
kindList := []string{}
|
||||
for _, k := range rule.MatchResources.Kinds {
|
||||
kindList = append(kindList, common.GetFormatedKind(k))
|
||||
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/resources/kinds", strconv.Itoa(i)), rule.MatchResources.Kinds, log)
|
||||
if err == nil && patchByte != nil {
|
||||
patches = append(patches, patchByte)
|
||||
} else if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to GVK for rule '%s/%s/%d/match': %v", policy.Name, rule.Name, i, err))
|
||||
}
|
||||
jsonPatch := struct {
|
||||
Path string `json:"path"`
|
||||
Op string `json:"op"`
|
||||
Value []string `json:"value"`
|
||||
}{
|
||||
fmt.Sprintf("/spec/rules/%s/match/resources/kinds", strconv.Itoa(i)),
|
||||
"replace",
|
||||
kindList,
|
||||
|
||||
for j, matchAll := range rule.MatchResources.All {
|
||||
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/all/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(j)), matchAll.ResourceDescription.Kinds, log)
|
||||
if err == nil && patchByte != nil {
|
||||
patches = append(patches, patchByte)
|
||||
} else if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/match/all/%d': %v", policy.Name, rule.Name, i, j, err))
|
||||
}
|
||||
}
|
||||
patchByte, err := json.Marshal(jsonPatch)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to convert policy '%s': %v", policy.Name, err))
|
||||
|
||||
for k, matchAny := range rule.MatchResources.Any {
|
||||
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/match/any/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(k)), matchAny.ResourceDescription.Kinds, log)
|
||||
if err == nil && patchByte != nil {
|
||||
patches = append(patches, patchByte)
|
||||
} else if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/match/any/%d': %v", policy.Name, rule.Name, i, k, err))
|
||||
}
|
||||
}
|
||||
|
||||
patchByte, err = convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/resources/kinds", strconv.Itoa(i)), rule.ExcludeResources.Kinds, log)
|
||||
if err == nil && patchByte != nil {
|
||||
patches = append(patches, patchByte)
|
||||
} else if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude': %v", policy.Name, rule.Name, i, err))
|
||||
}
|
||||
|
||||
for j, excludeAll := range rule.ExcludeResources.All {
|
||||
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/all/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(j)), excludeAll.ResourceDescription.Kinds, log)
|
||||
if err == nil && patchByte != nil {
|
||||
patches = append(patches, patchByte)
|
||||
} else if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude/all/%d': %v", policy.Name, rule.Name, i, j, err))
|
||||
}
|
||||
}
|
||||
|
||||
for k, excludeAny := range rule.ExcludeResources.Any {
|
||||
patchByte, err := convertGVKForKinds(fmt.Sprintf("/spec/rules/%s/exclude/any/%s/resources/kinds", strconv.Itoa(i), strconv.Itoa(k)), excludeAny.ResourceDescription.Kinds, log)
|
||||
if err == nil && patchByte != nil {
|
||||
patches = append(patches, patchByte)
|
||||
} else if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to convert GVK for rule '%s/%s/%d/exclude/any/%d': %v", policy.Name, rule.Name, i, k, err))
|
||||
}
|
||||
}
|
||||
patches = append(patches, patchByte)
|
||||
}
|
||||
|
||||
return patches, errs
|
||||
}
|
||||
|
||||
func convertGVKForKinds(path string, kinds []string, log logr.Logger) ([]byte, error) {
|
||||
kindList := []string{}
|
||||
for _, k := range kinds {
|
||||
gvk := common.GetFormatedKind(k)
|
||||
if gvk == k {
|
||||
continue
|
||||
}
|
||||
kindList = append(kindList, gvk)
|
||||
}
|
||||
|
||||
if len(kindList) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
p, err := buildReplaceJsonPatch(path, kindList)
|
||||
log.V(4).WithName("convertGVKForKinds").Info("generated patch", "patch", string(p))
|
||||
return p, err
|
||||
}
|
||||
|
||||
func buildReplaceJsonPatch(path string, kindList []string) ([]byte, error) {
|
||||
jsonPatch := struct {
|
||||
Path string `json:"path"`
|
||||
Op string `json:"op"`
|
||||
Value []string `json:"value"`
|
||||
}{
|
||||
path,
|
||||
"replace",
|
||||
kindList,
|
||||
}
|
||||
return json.Marshal(jsonPatch)
|
||||
}
|
||||
|
||||
func convertPatchToJSON6902(policy *kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
|
||||
patches = make([][]byte, 0)
|
||||
|
||||
|
|
|
@ -307,3 +307,84 @@ func Test_UpdateVariablePath(t *testing.T) {
|
|||
|
||||
assert.DeepEqual(t, rulePatches, expectedPatches)
|
||||
}
|
||||
|
||||
func Test_checkForGVKFormatPatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
policy []byte
|
||||
expectedPatches []byte
|
||||
}{
|
||||
{
|
||||
name: "match-kinds-empty",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"match-kinds-empty"},"spec":{"rules":[{"name":"test","match":{"resources":{"kinds":["ConfigMap","batch.volcano.sh/v1alpha1/Job"]}},"validate":{"message":"Metadatalabel'name'isrequired.","pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: nil,
|
||||
},
|
||||
{
|
||||
name: "match-kinds",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"match-kinds"},"spec":{"rules":[{"name":"test","match":{"resources":{"kinds":["configmap","batch.volcano.sh/v1alpha1/job"]}},"validate":{"message":"Metadatalabel'name'isrequired.","pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: []byte(`{"path":"/spec/rules/0/match/resources/kinds","op":"replace","value":["Configmap","batch.volcano.sh/v1alpha1/Job"]}`),
|
||||
},
|
||||
{
|
||||
name: "exclude-kinds-empty",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"exclude-kinds-empty"},"spec":{"rules":[{"name":"test","exclude":{"resources":{"kinds":["ConfigMap","batch.volcano.sh/v1alpha1/Job"]}},"validate":{"message":"Metadatalabel'name'isrequired.","pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: nil,
|
||||
},
|
||||
{
|
||||
name: "exclude-kinds",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"exclude-kinds"},"spec":{"rules":[{"name":"test","exclude":{"resources":{"kinds":["configmap","batch.volcano.sh/v1alpha1/job"]}},"validate":{"message":"Metadatalabel'name'isrequired.","pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: []byte(`{"path":"/spec/rules/0/exclude/resources/kinds","op":"replace","value":["Configmap","batch.volcano.sh/v1alpha1/Job"]}`),
|
||||
},
|
||||
{
|
||||
name: "match-any-kinds-empty",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"match-any-kinds-empty"},"spec":{"rules":[{"name":"test","match":{"any":[{"resources":{"kinds":["Deployment","Pod","DaemonSet"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: nil,
|
||||
},
|
||||
{
|
||||
name: "match-any-kinds",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"match-any-kinds"},"spec":{"rules":[{"name":"test","match":{"any":[{"resources":{"kinds":["deployment","pod","DaemonSet"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: []byte(`{"path":"/spec/rules/0/match/any/0/resources/kinds","op":"replace","value":["Deployment","Pod"]}`),
|
||||
},
|
||||
{
|
||||
name: "match-all-kinds-empty",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"match-all-kinds-empty"},"spec":{"rules":[{"name":"test","match":{"all":[{"resources":{"kinds":["Deployment","Pod","DaemonSet"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: nil,
|
||||
},
|
||||
{
|
||||
name: "match-all-kinds",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"match-all-kinds"},"spec":{"rules":[{"name":"test","match":{"all":[{"resources":{"kinds":["deployment","pod","DaemonSet"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: []byte(`{"path":"/spec/rules/0/match/all/0/resources/kinds","op":"replace","value":["Deployment","Pod"]}`),
|
||||
},
|
||||
{
|
||||
name: "exclude-any-kinds-empty",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"exclude-any-kinds-empty"},"spec":{"rules":[{"name":"test","exclude":{"any":[{"resources":{"kinds":["Deployment","Pod","DaemonSet"]}},{"resources":{"kinds":["ConfigMap"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: nil,
|
||||
},
|
||||
{
|
||||
name: "exclude-any-kinds",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"exclude-any-kinds-empty"},"spec":{"rules":[{"name":"test","exclude":{"any":[{"resources":{"kinds":["Deployment","Pod","DaemonSet"]}},{"resources":{"kinds":["configMap"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: []byte(`{"path":"/spec/rules/0/exclude/any/1/resources/kinds","op":"replace","value":["ConfigMap"]}`),
|
||||
},
|
||||
{
|
||||
name: "exclude-all-kinds-empty",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"exclude-all-kinds-empty"},"spec":{"rules":[{"name":"test","exclude":{"all":[{"resources":{"kinds":["Deployment","Pod","DaemonSet"]}},{"resources":{"kinds":["ConfigMap"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: nil,
|
||||
},
|
||||
{
|
||||
name: "exclude-all-kinds",
|
||||
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"exclude-all-kinds"},"spec":{"rules":[{"name":"test","exclude":{"all":[{"resources":{"kinds":["Deployment","pod","DaemonSet"]}},{"resources":{"kinds":["ConfigMap"]}}]},"validate":{"pattern":{"metadata":{"labels":{"name":"?*"}}}}}]}}`),
|
||||
expectedPatches: []byte(`{"path":"/spec/rules/0/exclude/all/0/resources/kinds","op":"replace","value":["Pod"]}`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
var policy kyverno.ClusterPolicy
|
||||
err := json.Unmarshal(test.policy, &policy)
|
||||
assert.NilError(t, err, fmt.Sprintf("failed to convert policy test %s: %v", test.name, err))
|
||||
|
||||
patches, errs := checkForGVKFormatPatch(&policy, log.Log)
|
||||
assert.Assert(t, len(errs) == 0)
|
||||
for _, p := range patches {
|
||||
assert.Equal(t, string(p), string(test.expectedPatches), fmt.Sprintf("test %s failed", test.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue