1
0
Fork 0
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:
shuting 2019-09-12 23:06:51 -07:00 committed by GitHub
commit 3d02f81434
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 70 additions and 182 deletions

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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
}
}

View file

@ -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

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)

View file

@ -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
}

View file

@ -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