mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
Merge pull request #2261 from vyankyGH/GVK_Format
Support GVK format is case sensitive
This commit is contained in:
commit
74549c71ed
8 changed files with 82 additions and 12 deletions
|
@ -3,10 +3,11 @@ package common
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
dclient "github.com/kyverno/kyverno/pkg/dclient"
|
||||
|
@ -201,3 +202,14 @@ func removePolicyFromLabels(pName string, labels map[string]string) (bool, map[s
|
|||
|
||||
return false, labels
|
||||
}
|
||||
|
||||
func GetFormatedKind(str string) (kind string) {
|
||||
if strings.Count(str, "/") == 0 {
|
||||
return strings.Title(str)
|
||||
}
|
||||
splitString := strings.Split(str, "/")
|
||||
if strings.Count(str, "/") == 1 {
|
||||
return splitString[0] + "/" + strings.Title(splitString[1])
|
||||
}
|
||||
return splitString[0] + "/" + splitString[1] + "/" + strings.Title(splitString[2])
|
||||
}
|
||||
|
|
|
@ -32,15 +32,15 @@ func checkKind(kinds []string, resource unstructured.Unstructured) bool {
|
|||
for _, kind := range kinds {
|
||||
SplitGVK := strings.Split(kind, "/")
|
||||
if len(SplitGVK) == 1 {
|
||||
if resource.GetKind() == kind {
|
||||
if resource.GetKind() == strings.Title(kind) {
|
||||
return true
|
||||
}
|
||||
} else if len(SplitGVK) == 2 {
|
||||
if resource.GroupVersionKind().Kind == SplitGVK[1] && resource.GroupVersionKind().Version == SplitGVK[0] {
|
||||
if resource.GroupVersionKind().Kind == strings.Title(SplitGVK[1]) && resource.GroupVersionKind().Version == SplitGVK[0] {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if resource.GroupVersionKind().Group == SplitGVK[0] && resource.GroupVersionKind().Kind == SplitGVK[2] && (resource.GroupVersionKind().Version == SplitGVK[1] || resource.GroupVersionKind().Version == "*") {
|
||||
if resource.GroupVersionKind().Group == SplitGVK[0] && resource.GroupVersionKind().Kind == strings.Title(SplitGVK[2]) && (resource.GroupVersionKind().Version == SplitGVK[1] || resource.GroupVersionKind().Version == "*") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -869,6 +869,24 @@ func TestMatchesResourceDescription(t *testing.T) {
|
|||
Policy: []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "check-host-path" }, "spec": { "validationFailureAction": "enforce", "background": true, "rules": [ { "name": "check-host-path", "match": { "resources": { "kinds": [ "rbac.authorization.k8s.io/v1beta1/ClusterRole" ] } }, "validate": { "message": "Host path is not allowed", "pattern": { "spec": { "volumes": [ { "name": "*", "hostPath": { "path": "" } } ] } } } } ] } }`),
|
||||
areErrorsExpected: true,
|
||||
},
|
||||
{
|
||||
Description: "Test for GVK case sensitive",
|
||||
AdmissionInfo: kyverno.RequestInfo{
|
||||
ClusterRoles: []string{"admin"},
|
||||
},
|
||||
Resource: []byte(`{ "apiVersion": "v1", "kind": "Pod", "metadata": { "name": "myapp-pod2", "labels": { "app": "myapp2" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx" } ] } }`),
|
||||
Policy: []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-latest-tag", "annotations": { "policies.kyverno.io/category": "Workload Isolation", "policies.kyverno.io/description": "The ':latest' tag is mutable and can lead to unexpected errors if the image changes. A best practice is to use an immutable tag that maps to a specific version of an application pod." } }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "require-image-tag", "match": { "resources": { "kinds": [ "pod" ] } }, "validate": { "message": "An image tag is required", "pattern": { "spec": { "containers": [ { "image": "*:*" } ] } } } } ] } }`),
|
||||
areErrorsExpected: false,
|
||||
},
|
||||
{
|
||||
Description: "Test should pass for GVK case sensitive",
|
||||
AdmissionInfo: kyverno.RequestInfo{
|
||||
ClusterRoles: []string{"admin"},
|
||||
},
|
||||
Resource: []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "name": "qos-demo", "labels": { "test": "qos" } }, "spec": { "replicas": 1, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "creationTimestamp": "2020-09-21T12:56:35Z", "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:latest", "resources": { "limits": { "cpu": "50m" } } } ]}}}}`),
|
||||
Policy: []byte(`{ "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "policy-qos" }, "spec": { "validationFailureAction": "enforce", "rules": [ { "name": "add-memory-limit", "match": { "resources": { "kinds": [ "apps/v1/deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "mutate": { "overlay": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "+(memory)": "300Mi", "+(cpu)": "100" } } } ] } } } } } }, { "name": "check-cpu-memory-limits", "match": { "resources": { "kinds": [ "apps/v1/Deployment" ], "selector": { "matchLabels": { "test": "qos" } } } }, "validate": { "message": "Resource limits are required for CPU and memory", "pattern": { "spec": { "template": { "spec": { "containers": [ { "(name)": "*", "resources": { "limits": { "memory": "?*", "cpu": "?*" } } } ] } } } } } } ] } }`),
|
||||
areErrorsExpected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tcs {
|
||||
|
|
|
@ -278,9 +278,9 @@ func getKindsFromPolicy(rule v1.Rule) map[string]bool {
|
|||
for _, kind := range rule.MatchResources.Kinds {
|
||||
if strings.Contains(kind, "/") {
|
||||
lastElement := kind[strings.LastIndex(kind, "/")+1:]
|
||||
resourceTypesMap[lastElement] = true
|
||||
resourceTypesMap[strings.Title(lastElement)] = true
|
||||
}
|
||||
resourceTypesMap[kind] = true
|
||||
resourceTypesMap[strings.Title(kind)] = true
|
||||
}
|
||||
|
||||
if rule.MatchResources.Any != nil {
|
||||
|
@ -288,7 +288,7 @@ func getKindsFromPolicy(rule v1.Rule) map[string]bool {
|
|||
for _, kind := range resFilter.ResourceDescription.Kinds {
|
||||
if strings.Contains(kind, "/") {
|
||||
lastElement := kind[strings.LastIndex(kind, "/")+1:]
|
||||
resourceTypesMap[lastElement] = true
|
||||
resourceTypesMap[strings.Title(lastElement)] = true
|
||||
}
|
||||
resourceTypesMap[kind] = true
|
||||
}
|
||||
|
@ -300,9 +300,9 @@ func getKindsFromPolicy(rule v1.Rule) map[string]bool {
|
|||
for _, kind := range resFilter.ResourceDescription.Kinds {
|
||||
if strings.Contains(kind, "/") {
|
||||
lastElement := kind[strings.LastIndex(kind, "/")+1:]
|
||||
resourceTypesMap[lastElement] = true
|
||||
resourceTypesMap[strings.Title(lastElement)] = true
|
||||
}
|
||||
resourceTypesMap[kind] = true
|
||||
resourceTypesMap[strings.Title(kind)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
openapiv2 "github.com/googleapis/gnostic/openapiv2"
|
||||
data "github.com/kyverno/kyverno/api"
|
||||
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/common"
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
cmap "github.com/orcaman/concurrent-map"
|
||||
|
@ -144,7 +145,7 @@ func (o *Controller) ValidatePolicyMutation(policy v1.ClusterPolicy) error {
|
|||
for _, rule := range policy.Spec.Rules {
|
||||
if rule.HasMutate() {
|
||||
for _, kind := range rule.MatchResources.Kinds {
|
||||
kindToRules[kind] = append(kindToRules[kind], rule)
|
||||
kindToRules[kind] = append(kindToRules[common.GetFormatedKind(kind)], rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ func (pc *PolicyController) processExistingResources(policy *kyverno.ClusterPoli
|
|||
continue
|
||||
}
|
||||
|
||||
for _, k := range rule.MatchResources.Kinds {
|
||||
for _, kind := range rule.MatchResources.Kinds {
|
||||
k := common.GetFormatedKind(kind)
|
||||
logger = logger.WithValues("rule", rule.Name, "kind", k)
|
||||
namespaced, err := pc.rm.GetScope(k)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package policycache
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
@ -141,7 +142,8 @@ func (m *pMap) add(policy *kyverno.ClusterPolicy) {
|
|||
|
||||
func addCacheHelper(rmr kyverno.ResourceFilter, m *pMap, rule kyverno.Rule, mutateMap map[string]bool, pName string, enforcePolicy bool, validateEnforceMap map[string]bool, validateAuditMap map[string]bool, generateMap map[string]bool, imageVerifyMap map[string]bool) {
|
||||
for _, gvk := range rmr.Kinds {
|
||||
_, kind := common.GetKindFromGVK(gvk)
|
||||
_, k := common.GetKindFromGVK(gvk)
|
||||
kind := strings.Title(k)
|
||||
_, ok := m.kindDataMap[kind]
|
||||
if !ok {
|
||||
m.kindDataMap[kind] = make(map[PolicyType][]string)
|
||||
|
|
|
@ -60,6 +60,17 @@ func GenerateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, log logr.Logg
|
|||
}
|
||||
patches = append(patches, convertPatch...)
|
||||
|
||||
formatedGVK, errs := checkForGVKFormatPatch(policy, log)
|
||||
if len(errs) > 0 {
|
||||
var errMsgs []string
|
||||
for _, err := range errs {
|
||||
errMsgs = append(errMsgs, err.Error())
|
||||
log.Error(err, "failed to format the kind")
|
||||
}
|
||||
updateMsgs = append(updateMsgs, strings.Join(errMsgs, ";"))
|
||||
}
|
||||
patches = append(patches, formatedGVK...)
|
||||
|
||||
overlaySMPPatches, errs := convertOverlayToStrategicMerge(policy, log)
|
||||
if len(errs) > 0 {
|
||||
var errMsgs []string
|
||||
|
@ -74,6 +85,31 @@ func GenerateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, log logr.Logg
|
|||
return utils.JoinPatches(patches), updateMsgs
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
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,
|
||||
}
|
||||
patchByte, err := json.Marshal(jsonPatch)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to convert policy '%s': %v", policy.Name, err))
|
||||
}
|
||||
patches = append(patches, patchByte)
|
||||
}
|
||||
return patches, errs
|
||||
}
|
||||
|
||||
func convertPatchToJSON6902(policy *kyverno.ClusterPolicy, log logr.Logger) (patches [][]byte, errs []error) {
|
||||
patches = make([][]byte, 0)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue