mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-15 17:51:20 +00:00
Merge pull request #351 from nirmata/348_feature_wildcardsNamespaces
support wild cards for namespaces in rule resource description
This commit is contained in:
commit
3d02f81434
10 changed files with 70 additions and 182 deletions
|
@ -24,9 +24,9 @@ spec :
|
|||
- Deployment
|
||||
- StatefulSet
|
||||
name: "mongo*" # Optional, a resource name is optional. Name supports wildcards * and ?
|
||||
namespaces: # Optional, list of namespaces
|
||||
- devtest2
|
||||
- devtest1
|
||||
namespaces: # Optional, list of namespaces. Supports wilcards * and ?
|
||||
- "dev*"
|
||||
- test
|
||||
selector: # Optional, a resource selector is optional. Selector values support wildcards * and ?
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
|
@ -39,7 +39,8 @@ spec :
|
|||
- Daemonsets
|
||||
name: "*"
|
||||
namespaces:
|
||||
- devtest2
|
||||
- prod
|
||||
- "kube*"
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
|
|
|
@ -18,36 +18,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
// //EngineResponse provides the response to the application of a policy rule set on a resource
|
||||
// type EngineResponse struct {
|
||||
// // JSON patches for mutation rules
|
||||
// Patches [][]byte
|
||||
// // Resource patched with the policy changes
|
||||
// PatchedResource unstructured.Unstructured
|
||||
// // Rule details
|
||||
// RuleInfos []info.RuleInfo
|
||||
// // PolicyS
|
||||
// EngineStats
|
||||
// }
|
||||
|
||||
// type EngineResponseNew struct {
|
||||
// // error while processing engine action
|
||||
// Err error
|
||||
// // Resource patched with the engine action changes
|
||||
// PatchedResource unstructured.Unstructured
|
||||
// // Policy Response
|
||||
// PolicyRespone PolicyResponse
|
||||
// }
|
||||
|
||||
// type PolicyResponse struct {
|
||||
// // policy name
|
||||
// Policy string
|
||||
// // resource details
|
||||
// Resource kyverno.ResourceSpec
|
||||
// }
|
||||
|
||||
// type PolicyStatus
|
||||
|
||||
//EngineStats stores in the statistics for a single application of resource
|
||||
type EngineStats struct {
|
||||
// average time required to process the policy rules on a resource
|
||||
|
@ -56,136 +26,6 @@ type EngineStats struct {
|
|||
RulesAppliedCount int
|
||||
}
|
||||
|
||||
// //ListResourcesThatApplyToPolicy returns list of resources that are filtered by policy rules
|
||||
// func ListResourcesThatApplyToPolicy(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]resourceInfo {
|
||||
// // key uid
|
||||
// resourceMap := map[string]resourceInfo{}
|
||||
// for _, rule := range policy.Spec.Rules {
|
||||
// // Match
|
||||
// for _, k := range rule.MatchResources.Kinds {
|
||||
// namespaces := []string{}
|
||||
// if k == "Namespace" {
|
||||
// namespaces = []string{""}
|
||||
// } else {
|
||||
// if rule.MatchResources.Namespace != "" {
|
||||
// // if namespace is specified then we add the namespace
|
||||
// namespaces = append(namespaces, rule.MatchResources.Namespace)
|
||||
// } else {
|
||||
// // no namespace specified, refer to all namespaces
|
||||
// namespaces = getAllNamespaces(client)
|
||||
// }
|
||||
|
||||
// // Check if exclude namespace is not clashing
|
||||
// namespaces = excludeNamespaces(namespaces, rule.ExcludeResources.Namespace)
|
||||
// }
|
||||
|
||||
// // If kind is namespace then namespace is "", override
|
||||
// // Get resources in the namespace
|
||||
// for _, ns := range namespaces {
|
||||
// rMap := getResourcesPerNamespace(k, client, ns, rule, filterK8Resources)
|
||||
// mergeresources(resourceMap, rMap)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return resourceMap
|
||||
// }
|
||||
|
||||
// func getResourcesPerNamespace(kind string, client *client.Client, namespace string, rule kyverno.Rule, filterK8Resources []utils.K8Resource) map[string]resourceInfo {
|
||||
// resourceMap := map[string]resourceInfo{}
|
||||
// // List resources
|
||||
// list, err := client.ListResource(kind, namespace, rule.MatchResources.Selector)
|
||||
// if err != nil {
|
||||
// glog.Errorf("unable to list resource for %s with label selector %s", kind, rule.MatchResources.Selector.String())
|
||||
// return nil
|
||||
// }
|
||||
// var selector labels.Selector
|
||||
// // exclude label selector
|
||||
// if rule.ExcludeResources.Selector != nil {
|
||||
// selector, err = v1helper.LabelSelectorAsSelector(rule.ExcludeResources.Selector)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// }
|
||||
// }
|
||||
// for _, res := range list.Items {
|
||||
// // exclude label selectors
|
||||
// if selector != nil {
|
||||
// set := labels.Set(res.GetLabels())
|
||||
// if selector.Matches(set) {
|
||||
// // if matches
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// var name string
|
||||
// // match
|
||||
// // name
|
||||
// // wild card matching
|
||||
// name = rule.MatchResources.Name
|
||||
// if name != "" {
|
||||
// // if does not match then we skip
|
||||
// if !wildcard.Match(name, res.GetName()) {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// // exclude
|
||||
// // name
|
||||
// // wild card matching
|
||||
// name = rule.ExcludeResources.Name
|
||||
// if name != "nil" {
|
||||
// // if matches then we skip
|
||||
// if wildcard.Match(name, res.GetName()) {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// gvk := res.GroupVersionKind()
|
||||
|
||||
// ri := resourceInfo{Resource: res, Gvk: &metav1.GroupVersionKind{Group: gvk.Group,
|
||||
// Version: gvk.Version,
|
||||
// Kind: gvk.Kind}}
|
||||
// // Skip the filtered resources
|
||||
// if utils.SkipFilteredResources(gvk.Kind, res.GetNamespace(), res.GetName(), filterK8Resources) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// resourceMap[string(res.GetUID())] = ri
|
||||
// }
|
||||
// return resourceMap
|
||||
// }
|
||||
|
||||
// // merge b into a map
|
||||
// func mergeresources(a, b map[string]resourceInfo) {
|
||||
// for k, v := range b {
|
||||
// a[k] = v
|
||||
// }
|
||||
// }
|
||||
|
||||
// func getAllNamespaces(client *client.Client) []string {
|
||||
// namespaces := []string{}
|
||||
// // get all namespaces
|
||||
// nsList, err := client.ListResource("Namespace", "", nil)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// return namespaces
|
||||
// }
|
||||
// for _, ns := range nsList.Items {
|
||||
// namespaces = append(namespaces, ns.GetName())
|
||||
// }
|
||||
// return namespaces
|
||||
// }
|
||||
|
||||
// func excludeNamespaces(namespaces []string, excludeNs string) []string {
|
||||
// if excludeNs == "" {
|
||||
// return namespaces
|
||||
// }
|
||||
// filteredNamespaces := []string{}
|
||||
// for _, n := range namespaces {
|
||||
// if n == excludeNs {
|
||||
// continue
|
||||
// }
|
||||
// filteredNamespaces = append(filteredNamespaces, n)
|
||||
// }
|
||||
// return filteredNamespaces
|
||||
// }
|
||||
|
||||
//MatchesResourceDescription checks if the resource matches resource desription of the rule or not
|
||||
func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno.Rule) bool {
|
||||
matches := rule.MatchResources.ResourceDescription
|
||||
|
@ -207,8 +47,8 @@ func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno
|
|||
}
|
||||
|
||||
// Matches
|
||||
// check if the resource namespace is defined in the list of namespaces for inclusion
|
||||
if len(matches.Namespaces) > 0 && !utils.Contains(matches.Namespaces, namespace) {
|
||||
// check if the resource namespace is defined in the list of namespace pattern
|
||||
if len(matches.Namespaces) > 0 && !utils.ContainsNamepace(matches.Namespaces, namespace) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -238,7 +78,7 @@ func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno
|
|||
if len(exclude.Namespaces) == 0 {
|
||||
return NotEvaluate
|
||||
}
|
||||
if utils.Contains(exclude.Namespaces, namespace) {
|
||||
if utils.ContainsNamepace(exclude.Namespaces, namespace) {
|
||||
return Skip
|
||||
}
|
||||
return Process
|
||||
|
|
|
@ -133,7 +133,7 @@ func excludeResources(included map[string]unstructured.Unstructured, exclude kyv
|
|||
if len(exclude.Namespaces) == 0 {
|
||||
return NotEvaluate
|
||||
}
|
||||
if utils.Contains(exclude.Namespaces, namespace) {
|
||||
if utils.ContainsNamepace(exclude.Namespaces, namespace) {
|
||||
return Skip
|
||||
}
|
||||
return Process
|
||||
|
@ -265,7 +265,7 @@ func excludeNamespaces(namespaces, excludeNs []string) []string {
|
|||
}
|
||||
filteredNamespaces := []string{}
|
||||
for _, n := range namespaces {
|
||||
if utils.Contains(excludeNs, n) {
|
||||
if utils.ContainsNamepace(excludeNs, n) {
|
||||
continue
|
||||
}
|
||||
filteredNamespaces = append(filteredNamespaces, n)
|
||||
|
|
|
@ -91,7 +91,7 @@ func subsetMap(a, b map[string]interface{}) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func contains(a interface{}, b []interface{}) bool {
|
||||
func containsInt(a interface{}, b []interface{}) bool {
|
||||
switch typed := a.(type) {
|
||||
case bool:
|
||||
for _, bv := range b {
|
||||
|
@ -181,7 +181,7 @@ func subsetSlice(a, b []interface{}) bool {
|
|||
}
|
||||
|
||||
for _, av := range a {
|
||||
if !contains(av, b) {
|
||||
if !containsInt(av, b) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,15 +22,33 @@ type K8Resource struct {
|
|||
}
|
||||
|
||||
//Contains Check if strint is contained in a list of string
|
||||
func Contains(list []string, element string) bool {
|
||||
func contains(list []string, element string, fn func(string, string) bool) bool {
|
||||
for _, e := range list {
|
||||
if e == element {
|
||||
if fn(e, element) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//ContainsNamepace check if namespace satisfies any list of pattern(regex)
|
||||
func ContainsNamepace(patterns []string, ns string) bool {
|
||||
return contains(patterns, ns, compareNamespaces)
|
||||
}
|
||||
|
||||
//ContainsString check if the string is contains in a list
|
||||
func ContainsString(list []string, element string) bool {
|
||||
return contains(list, element, compareString)
|
||||
}
|
||||
|
||||
func compareNamespaces(pattern, ns string) bool {
|
||||
return wildcard.Match(pattern, ns)
|
||||
}
|
||||
|
||||
func compareString(str, name string) bool {
|
||||
return str == name
|
||||
}
|
||||
|
||||
//SkipFilteredResourcesReq checks if request is to be skipped based on filtered kinds
|
||||
func SkipFilteredResourcesReq(request *v1beta1.AdmissionRequest, filterK8Resources []K8Resource) bool {
|
||||
kind := request.Kind.Kind
|
||||
|
|
|
@ -9,32 +9,61 @@ import (
|
|||
func Test_allEmpty(t *testing.T) {
|
||||
var list []string
|
||||
var element string
|
||||
res := Contains(list, element)
|
||||
res := ContainsString(list, element)
|
||||
assert.Assert(t, res == false)
|
||||
}
|
||||
|
||||
func Test_emptyList(t *testing.T) {
|
||||
var list []string
|
||||
element := "foo"
|
||||
res := Contains(list, element)
|
||||
res := ContainsString(list, element)
|
||||
assert.Assert(t, res == false)
|
||||
}
|
||||
|
||||
func Test_emptyElement(t *testing.T) {
|
||||
list := []string{"foo", "bar"}
|
||||
var element string
|
||||
res := Contains(list, element)
|
||||
res := ContainsString(list, element)
|
||||
assert.Assert(t, res == false)
|
||||
}
|
||||
|
||||
func Test_emptyElementInList(t *testing.T) {
|
||||
list := []string{"foo", "bar", ""}
|
||||
var element string
|
||||
res := Contains(list, element)
|
||||
res := ContainsString(list, element)
|
||||
assert.Assert(t, res == true)
|
||||
|
||||
list = []string{"foo", "bar", "bar"}
|
||||
element = "bar"
|
||||
res = Contains(list, element)
|
||||
res = ContainsString(list, element)
|
||||
assert.Assert(t, res == true)
|
||||
}
|
||||
|
||||
func Test_containsNs(t *testing.T) {
|
||||
var patterns []string
|
||||
var res bool
|
||||
patterns = []string{"*"}
|
||||
res = ContainsNamepace(patterns, "default")
|
||||
assert.Assert(t, res == true)
|
||||
|
||||
patterns = []string{"*", "default"}
|
||||
res = ContainsNamepace(patterns, "default")
|
||||
assert.Assert(t, res == true)
|
||||
|
||||
patterns = []string{"default2", "default"}
|
||||
res = ContainsNamepace(patterns, "default1")
|
||||
assert.Assert(t, res == false)
|
||||
|
||||
patterns = []string{"d*"}
|
||||
res = ContainsNamepace(patterns, "default")
|
||||
assert.Assert(t, res == true)
|
||||
|
||||
patterns = []string{"d*"}
|
||||
res = ContainsNamepace(patterns, "test")
|
||||
assert.Assert(t, res == false)
|
||||
|
||||
patterns = []string{}
|
||||
res = ContainsNamepace(patterns, "test")
|
||||
assert.Assert(t, res == false)
|
||||
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool
|
|||
for _, policy := range policies {
|
||||
|
||||
// check if policy has a rule for the admission request kind
|
||||
if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
if !utils.ContainsString(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ func (ws *WebhookServer) validateUniqueRuleName(policy *kyverno.ClusterPolicy) *
|
|||
var ruleNames []string
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if utils.Contains(ruleNames, rule.Name) {
|
||||
if utils.ContainsString(ruleNames, rule.Name) {
|
||||
msg := fmt.Sprintf(`The policy "%s" is invalid: duplicate rule name: "%s"`, policy.Name, rule.Name)
|
||||
glog.Errorln(msg)
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pat
|
|||
var engineResponses []engine.EngineResponseNew
|
||||
for _, policy := range policies {
|
||||
|
||||
if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
if !utils.ContainsString(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -14,5 +14,5 @@ expected:
|
|||
rules:
|
||||
- name: validate-host-network-port
|
||||
type: Validation
|
||||
message: Validation rule 'validate-host-network-port' failed at '/spec/containers/0/ports/0/hostPort/' for resource Pod//nginx-host-network. Host network and port are not allowed
|
||||
message: "Validation rule 'validate-host-network-port' failed at '/spec/containers/0/ports/0/hostPort/' for resource Pod//nginx-host-network. Host network and port are not allowed"
|
||||
success: false
|
Loading…
Reference in a new issue