1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

Merge pull request #670 from shravanshetty1/658_optional_kind_field

#658 - Make 'kinds' in match / exclude block optional
This commit is contained in:
shuting 2020-02-07 11:36:51 -08:00 committed by GitHub
commit 4d8bb0666a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 23 additions and 617 deletions

View file

@ -71,8 +71,7 @@ spec:
type: string
resources:
type: object
required:
- kinds
minProperties: 1
properties:
kinds:
type: array

View file

@ -71,8 +71,7 @@ spec:
type: string
resources:
type: object
required:
- kinds
minProperties: 1
properties:
kinds:
type: array

View file

@ -135,10 +135,6 @@ func validateMatchedResourceDescription(rd kyverno.ResourceDescription) (string,
return "", fmt.Errorf("match resources not specified")
}
if len(rd.Kinds) == 0 {
return "match", fmt.Errorf("kind is mandatory")
}
if err := validateResourceDescription(rd); err != nil {
return "match", err
}

View file

@ -248,24 +248,6 @@ func Test_Validate_ResourceDescription_MatchedValid(t *testing.T) {
_, err = validateMatchedResourceDescription(rd)
assert.NilError(t, err)
}
func Test_Validate_ResourceDescription_MissingKindsOnMatched(t *testing.T) {
var err error
matchedResourcedescirption := []byte(`
{
"selector": {
"matchLabels": {
"app.type": "prod"
}
}
}`)
var rd kyverno.ResourceDescription
err = json.Unmarshal(matchedResourcedescirption, &rd)
assert.NilError(t, err)
_, err = validateMatchedResourceDescription(rd)
assert.Assert(t, err != nil)
}
func Test_Validate_ResourceDescription_MissingKindsOnExclude(t *testing.T) {
var err error

View file

@ -31,8 +31,10 @@ func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno
matches := rule.MatchResources.ResourceDescription
exclude := rule.ExcludeResources.ResourceDescription
if !findKind(matches.Kinds, resource.GetKind()) {
return false
if len(matches.Kinds) > 0 {
if !findKind(matches.Kinds, resource.GetKind()) {
return false
}
}
name := resource.GetName()

View file

@ -31,196 +31,6 @@ func TestGetAnchorsFromMap_ThereAreAnchors(t *testing.T) {
assert.Equal(t, actualMap["(namespace)"].(string), "kube-?olicy")
}
func TestValidate_ServiceTest(t *testing.T) {
rawPolicy := []byte(`{
"apiVersion":"kyverno.nirmata.io/v1",
"kind":"ClusterPolicy",
"metadata":{
"name":"policy-service"
},
"spec":{
"rules":[
{
"name":"ps1",
"resource":{
"kinds":[
"Service"
],
"name":"game-service*"
},
"mutate":{
"patches":[
{
"path":"/metadata/labels/isMutated",
"op":"add",
"value":"true"
},
{
"path":"/metadata/labels/secretLabel",
"op":"replace",
"value":"weKnow"
},
{
"path":"/metadata/labels/originalLabel",
"op":"remove"
},
{
"path":"/spec/selector/app",
"op":"replace",
"value":"mutedApp"
}
]
},
"validate":{
"message":"This resource is broken",
"pattern":{
"spec":{
"ports":[
{
"name":"hs",
"protocol":32
}
]
}
}
}
}
]
}
}`)
rawResource := []byte(`{
"kind":"Service",
"apiVersion":"v1",
"metadata":{
"name":"game-service",
"labels":{
"originalLabel":"isHere",
"secretLabel":"thisIsMySecret"
}
},
"spec":{
"selector":{
"app":"MyApp"
},
"ports":[
{
"name":"http",
"protocol":"TCP",
"port":80,
"targetPort":9376
}
]
}
}
`)
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
}
func TestValidate_MapHasFloats(t *testing.T) {
rawPolicy := []byte(`{
"apiVersion":"kyverno.nirmata.io/v1",
"kind":"ClusterPolicy",
"metadata":{
"name":"policy-deployment-changed"
},
"spec":{
"rules":[
{
"name":"First policy v2",
"resource":{
"kinds":[
"Deployment"
],
"name":"nginx-*"
},
"mutate":{
"patches":[
{
"path":"/metadata/labels/isMutated",
"op":"add",
"value":"true"
},
{
"path":"/metadata/labels/app",
"op":"replace",
"value":"nginx_is_mutated"
}
]
},
"validate":{
"message":"replicas number is wrong",
"pattern":{
"metadata":{
"labels":{
"app":"*"
}
},
"spec":{
"replicas":3
}
}
}
}
]
}
}`)
rawResource := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"replicas":3,
"selector":{
"matchLabels":{
"app":"nginx"
}
},
"template":{
"metadata":{
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nginx",
"image":"nginx:1.7.9",
"ports":[
{
"containerPort":80
}
]
}
]
}
}
}
}
`)
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
assert.NilError(t, err)
er := Validate(PolicyContext{Policy: policy, NewResource: *resourceUnstructured})
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
}
func TestValidate_image_tag_fail(t *testing.T) {
// If image tag is latest then imagepull policy needs to be checked
rawPolicy := []byte(`{

View file

@ -165,7 +165,7 @@ func (nsc *NamespaceController) processPolicy(policy *kyverno.ClusterPolicy) {
func listpolicies(ns unstructured.Unstructured, pMetaStore policystore.LookupInterface) []kyverno.ClusterPolicy {
var filteredpolicies []kyverno.ClusterPolicy
glog.V(4).Infof("listing policies for namespace %s", ns.GetName())
policies, err := pMetaStore.LookUp(ns.GetKind(), ns.GetNamespace())
policies, err := pMetaStore.ListAll()
if err != nil {
glog.Errorf("failed to get list policies: %v", err)
return nil

View file

@ -3,6 +3,8 @@ package policystore
import (
"sync"
"k8s.io/apimachinery/pkg/labels"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
@ -34,8 +36,7 @@ type UpdateInterface interface {
//LookupInterface provides api to lookup policies
type LookupInterface interface {
// Lookup based on kind and namespaces
LookUp(kind, namespace string) ([]kyverno.ClusterPolicy, error)
ListAll() ([]kyverno.ClusterPolicy, error)
}
// NewPolicyStore returns a new policy store
@ -81,19 +82,18 @@ func (ps *PolicyStore) Register(policy kyverno.ClusterPolicy) {
}
}
//LookUp look up the resources
func (ps *PolicyStore) LookUp(kind, namespace string) ([]kyverno.ClusterPolicy, error) {
ret := []kyverno.ClusterPolicy{}
// lookup meta-store
policyNames := ps.lookUp(kind, namespace)
for _, policyName := range policyNames {
policy, err := ps.pLister.Get(policyName)
if err != nil {
return nil, err
}
ret = append(ret, *policy)
func (ps *PolicyStore) ListAll() ([]kyverno.ClusterPolicy, error) {
policyPointers, err := ps.pLister.List(labels.NewSelector())
if err != nil {
return nil, err
}
return ret, nil
var policies = make([]kyverno.ClusterPolicy, 0, len(policyPointers))
for _, policy := range policyPointers {
policies = append(policies, *policy)
}
return policies, nil
}
//UnRegister Remove policy information
@ -125,48 +125,6 @@ func (ps *PolicyStore) UnRegister(policy kyverno.ClusterPolicy) error {
return nil
}
//LookUp lookups up the policies for kind and namespace
// returns a list of <policy, rule> that statisfy the filters
func (ps *PolicyStore) lookUp(kind, namespace string) []string {
ps.mu.RLock()
defer ps.mu.RUnlock()
var policyMap policyMap
var ret []string
// kind
kindMap := ps.getKind(kind)
if kindMap == nil {
return []string{}
}
// get namespace specific policies
policyMap = kindMap[namespace]
ret = append(ret, transform(policyMap)...)
// get policies on all namespaces
policyMap = kindMap["*"]
ret = append(ret, transform(policyMap)...)
return unique(ret)
}
func unique(intSlice []string) []string {
keys := make(map[string]bool)
list := []string{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}
// generates a copy
func transform(pmap policyMap) []string {
ret := []string{}
for k := range pmap {
ret = append(ret, k)
}
return ret
}
func (ps *PolicyStore) addKind(kind string) namespaceMap {
val, ok := ps.data[kind]
if ok {

View file

@ -1,340 +0,0 @@
package policystore
import (
"encoding/json"
"reflect"
"testing"
"time"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/fake"
listerv1 "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
cache "k8s.io/client-go/tools/cache"
)
func Test_Operations(t *testing.T) {
rawPolicy1 := []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "test-policy1"
},
"spec": {
"rules": [
{
"name": "r1",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"mutate": {
"overlay": "temp"
}
},
{
"name": "r2",
"match": {
"resources": {
"kinds": [
"Pod",
"Deployment"
]
}
},
"mutate": {
"overlay": "temp"
}
},
{
"name": "r3",
"match": {
"resources": {
"kinds": [
"Pod",
"Deployment"
],
"namespaces": [
"n1"
]
}
},
"mutate": {
"overlay": "temp"
}
},
{
"name": "r4",
"match": {
"resources": {
"kinds": [
"Pod",
"Deployment"
],
"namespaces": [
"n1",
"n2"
]
}
},
"validate": {
"pattern": "temp"
}
}
]
}
}
`)
rawPolicy2 := []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "test-policy2"
},
"spec": {
"rules": [
{
"name": "r1",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"mutate": {
"overlay": "temp"
}
},
{
"name": "r2",
"match": {
"resources": {
"kinds": [
"Pod"
],
"namespaces": [
"n4"
]
}
},
"mutate": {
"overlay": "temp"
}
},
{
"name": "r2",
"match": {
"resources": {
"kinds": [
"Pod"
],
"namespaces": [
"n4",
"n5",
"n6"
]
}
},
"validate": {
"pattern": "temp"
}
}
]
}
}`)
rawPolicy3 := []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "test-policy3"
},
"spec": {
"rules": [
{
"name": "r4",
"match": {
"resources": {
"kinds": [
"Service"
]
}
},
"mutate": {
"overlay": "temp"
}
}
]
}
}`)
var policy1 kyverno.ClusterPolicy
json.Unmarshal(rawPolicy1, &policy1)
var policy2 kyverno.ClusterPolicy
json.Unmarshal(rawPolicy2, &policy2)
var policy3 kyverno.ClusterPolicy
json.Unmarshal(rawPolicy3, &policy3)
scheme.Scheme.AddKnownTypes(kyverno.SchemeGroupVersion,
&kyverno.ClusterPolicy{},
)
var obj runtime.Object
var err error
var retPolicies []kyverno.ClusterPolicy
polices := []runtime.Object{}
// list of runtime objects
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err = decode(rawPolicy1, nil, nil)
if err != nil {
t.Error(err)
}
polices = append(polices, obj)
obj, _, err = decode(rawPolicy2, nil, nil)
if err != nil {
t.Error(err)
}
polices = append(polices, obj)
obj, _, err = decode(rawPolicy3, nil, nil)
if err != nil {
t.Error(err)
}
polices = append(polices, obj)
// Mock Lister
client := fake.NewSimpleClientset(polices...)
fakeInformer := &FakeInformer{client: client}
store := NewPolicyStore(fakeInformer)
// Test Operations
// Add
store.Register(policy1)
// Add
store.Register(policy2)
// Add
store.Register(policy3)
// Lookup
retPolicies, err = store.LookUp("Pod", "")
if err != nil {
t.Error(err)
}
if len(retPolicies) != len([]kyverno.ClusterPolicy{policy1, policy2}) {
// checking length as the order of polcies might be different
t.Error("not matching")
}
// Remove
err = store.UnRegister(policy1)
if err != nil {
t.Error(err)
}
retPolicies, err = store.LookUp("Pod", "")
if err != nil {
t.Error(err)
}
// Lookup
if !reflect.DeepEqual(retPolicies, []kyverno.ClusterPolicy{policy2}) {
t.Error("not matching")
}
// Add
store.Register(policy1)
retPolicies, err = store.LookUp("Pod", "")
if err != nil {
t.Error(err)
}
if len(retPolicies) != len([]kyverno.ClusterPolicy{policy1, policy2}) {
// checking length as the order of polcies might be different
t.Error("not matching")
}
retPolicies, err = store.LookUp("Service", "")
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(retPolicies, []kyverno.ClusterPolicy{policy3}) {
t.Error("not matching")
}
}
type FakeInformer struct {
client *fake.Clientset
}
func (fi *FakeInformer) Informer() cache.SharedIndexInformer {
fsi := &FakeSharedInformer{}
return fsi
}
func (fi *FakeInformer) Lister() listerv1.ClusterPolicyLister {
fl := &FakeLister{client: fi.client}
return fl
}
type FakeLister struct {
client *fake.Clientset
}
func (fl *FakeLister) List(selector labels.Selector) (ret []*kyverno.ClusterPolicy, err error) {
return nil, nil
}
func (fl *FakeLister) Get(name string) (*kyverno.ClusterPolicy, error) {
return fl.client.KyvernoV1().ClusterPolicies().Get(name, v1.GetOptions{})
}
func (fl *FakeLister) GetPolicyForPolicyViolation(pv *kyverno.ClusterPolicyViolation) ([]*kyverno.ClusterPolicy, error) {
return nil, nil
}
func (fl *FakeLister) ListResources(selector labels.Selector) (ret []*kyverno.ClusterPolicy, err error) {
return nil, nil
}
func (fl *FakeLister) GetPolicyForNamespacedPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.ClusterPolicy, error) {
return nil, nil
}
type FakeSharedInformer struct {
}
func (fsi *FakeSharedInformer) HasSynced() bool {
return true
}
func (fsi *FakeSharedInformer) AddEventHandler(handler cache.ResourceEventHandler) {
}
func (fsi *FakeSharedInformer) AddEventHandlerWithResyncPeriod(handler cache.ResourceEventHandler, resyncPeriod time.Duration) {
}
func (fsi *FakeSharedInformer) AddIndexers(indexers cache.Indexers) error {
return nil
}
func (fsi *FakeSharedInformer) GetIndexer() cache.Indexer {
return nil
}
func (fsi *FakeSharedInformer) GetStore() cache.Store {
return nil
}
func (fsi *FakeSharedInformer) GetController() cache.Controller {
return nil
}
func (fsi *FakeSharedInformer) Run(stopCh <-chan struct{}) {
}
func (fsi *FakeSharedInformer) LastSyncResourceVersion() string {
return ""
}

View file

@ -195,7 +195,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
}
func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
policies, err := ws.pMetaStore.LookUp(request.Kind.Kind, request.Namespace)
policies, err := ws.pMetaStore.ListAll()
if err != nil {
// Unable to connect to policy Lister to access policies
glog.Errorf("Unable to connect to policy controller to access policies. Policies are NOT being applied: %v", err)
@ -292,7 +292,7 @@ func (ws *WebhookServer) handleMutateAdmissionRequest(request *v1beta1.Admission
}
func (ws *WebhookServer) handleValidateAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
policies, err := ws.pMetaStore.LookUp(request.Kind.Kind, request.Namespace)
policies, err := ws.pMetaStore.ListAll()
if err != nil {
// Unable to connect to policy Lister to access policies
glog.Errorf("Unable to connect to policy controller to access policies. Policies are NOT being applied: %v", err)