From 3dda879e51eab9fd541ec8c9959e102eea11a5ef Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 7 Aug 2019 16:14:33 -0700 Subject: [PATCH 01/66] policyviolation, policy controller reconciliation --- definitions/install_debug.yaml | 53 +- definitions/new_install.yaml | 285 +++++++ examples/cli/p1.yaml | 27 + examples/cli/pv1.yaml | 15 + main.go | 54 +- .../clientset/versioned/clientset.go | 90 ++ pkg/clientNew/clientset/versioned/doc.go | 20 + .../versioned/fake/clientset_generated.go | 77 ++ pkg/clientNew/clientset/versioned/fake/doc.go | 20 + .../clientset/versioned/fake/register.go | 56 ++ .../clientset/versioned/scheme/doc.go | 20 + .../clientset/versioned/scheme/register.go | 56 ++ .../informers/externalversions/factory.go | 180 ++++ .../informers/externalversions/generic.go | 64 ++ .../internalinterfaces/factory_interfaces.go | 40 + pkg/dclient/violation.go | 11 + pkg/policy/controller.go | 778 ++++++++++++++++++ scripts/new_update-codegen.sh | 27 + 18 files changed, 1856 insertions(+), 17 deletions(-) create mode 100644 definitions/new_install.yaml create mode 100644 examples/cli/p1.yaml create mode 100644 examples/cli/pv1.yaml create mode 100644 pkg/clientNew/clientset/versioned/clientset.go create mode 100644 pkg/clientNew/clientset/versioned/doc.go create mode 100644 pkg/clientNew/clientset/versioned/fake/clientset_generated.go create mode 100644 pkg/clientNew/clientset/versioned/fake/doc.go create mode 100644 pkg/clientNew/clientset/versioned/fake/register.go create mode 100644 pkg/clientNew/clientset/versioned/scheme/doc.go create mode 100644 pkg/clientNew/clientset/versioned/scheme/register.go create mode 100644 pkg/clientNew/informers/externalversions/factory.go create mode 100644 pkg/clientNew/informers/externalversions/generic.go create mode 100644 pkg/clientNew/informers/externalversions/internalinterfaces/factory_interfaces.go create mode 100644 pkg/dclient/violation.go create mode 100644 pkg/policy/controller.go create mode 100755 scripts/new_update-codegen.sh diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 91242f12f5..4028417ab3 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -169,4 +169,55 @@ spec: name: type: string data: - AnyValue: {} \ No newline at end of file + AnyValue: {} +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: policyviolations.kyverno.io +spec: + group: kyverno.io + versions: + - name: v1alpha1 + served: true + storage: true + scope: Cluster + names: + kind: PolicyViolation + plural: policyviolations + singular: policyviolation + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + spec: + required: + - policyName + - resource + - rules + properties: + policyName: + type: string + resource: + type: object + required: + - kind + - name + properties: + kind: + type: string + name: + type: string + namespace: + type: string + rules: + type: array + items: + type: object + required: + - name + - type + - status + - message +--- \ No newline at end of file diff --git a/definitions/new_install.yaml b/definitions/new_install.yaml new file mode 100644 index 0000000000..a2a8ab724c --- /dev/null +++ b/definitions/new_install.yaml @@ -0,0 +1,285 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: policies.kyverno.io +spec: + group: kyverno.io + versions: + - name: v1alpha1 + served: true + storage: true + scope: Cluster + names: + kind: Policy + plural: policies + singular: policy + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + spec: + required: + - rules + properties: + # default values to be handled by user + validationFailureAction: + type: string + enum: + - enforce # blocks the resorce api-reques if a rule fails. Default behavior + - audit # allows resource creationg and reports the failed validation rules as violations + rules: + type: array + items: + type: object + required: + - name + - match + properties: + name: + type: string + match: + type: object + required: + - resources + properties: + resources: + type: object + required: + - kinds + properties: + kinds: + type: array + items: + type: string + name: + type: string + namespace: + type: string + selector: + properties: + matchLabels: + type: object + additionalProperties: + type: string + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + exclude: + type: object + required: + - resources + properties: + resources: + type: object + properties: + kinds: + type: array + items: + type: string + name: + type: string + namespace: + type: string + selector: + properties: + matchLabels: + type: object + additionalProperties: + type: string + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + values: + type: array + items: + type: string + mutate: + type: object + properties: + overlay: + AnyValue: {} + patches: + type: array + items: + type: object + required: + - path + - op + properties: + path: + type: string + op: + type: string + enum: + - add + - replace + - remove + value: + AnyValue: {} + validate: + type: object + required: + - pattern + properties: + message: + type: string + pattern: + AnyValue: {} + generate: + type: object + required: + - kind + - name + properties: + kind: + type: string + name: + type: string + clone: + type: object + required: + - namespace + - name + properties: + namespace: + type: string + name: + type: string + data: + AnyValue: {} +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: policyviolations.kyverno.io +spec: + group: kyverno.io + versions: + - name: v1aplha1 + served: true + storage: true + scope: Cluster + names: + kind: PolicyViolation + plural: policyviolations + singular: policyviolation + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + spec: + required: + - policyName + - resource + - rules + properties: + policyName: + type: string + resource: + type: object + required: + - kind + - name + properties: + kind: + type: string + name: + type: string + namespace: + type: string + rules: + type: array + items: + type: object + required: + - name + - type + - status + - message +--- +kind: Namespace +apiVersion: v1 +metadata: + name: "kyverno" +--- +apiVersion: v1 +kind: Service +metadata: + namespace: kyverno + name: kyverno-svc + labels: + app: kyverno +spec: + ports: + - port: 443 + targetPort: 443 + selector: + app: kyverno +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kyverno-service-account + namespace: kyverno +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: kyverno-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: kyverno-service-account + namespace: kyverno +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + namespace: kyverno + name: kyverno + labels: + app: kyverno +spec: + replicas: 1 + template: + metadata: + labels: + app: kyverno + spec: + serviceAccountName: kyverno-service-account + containers: + - name: kyverno + image: nirmata/kyverno:latest + args: ["--filterK8Resources","[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*]Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*]"] + ports: + - containerPort: 443 + securityContext: + privileged: true + diff --git a/examples/cli/p1.yaml b/examples/cli/p1.yaml new file mode 100644 index 0000000000..4562277d35 --- /dev/null +++ b/examples/cli/p1.yaml @@ -0,0 +1,27 @@ +apiVersion: kyverno.io/v1alpha1 +kind: Policy +metadata: + name: check-cpu-memory +spec: + rules: + - name: check-pod-resources + match: + resources: + kinds: + - Pod + validate: + message: "CPU and memory resource requests and limits are required" + pattern: + spec: + containers: + # 'name: *' selects all containers in the pod + - name: "*" + resources: + limits: + # '?' requires 1 alphanumeric character and '*' means that there can be 0 or more characters. + # Using them together e.g. '?*' requires at least one character. + memory: "?*" + cpu: "?*" + requests: + memory: "?*" + cpu: "?*" \ No newline at end of file diff --git a/examples/cli/pv1.yaml b/examples/cli/pv1.yaml new file mode 100644 index 0000000000..5a36dee855 --- /dev/null +++ b/examples/cli/pv1.yaml @@ -0,0 +1,15 @@ +apiVersion: kyverno.io/v1alpha1 +kind: PolicyViolation +metadata: + name: pv1 +spec: + policyName: check-cpu-memory + resource: + kind: Pod + namespace: "" + name: pod1 + rules: + - name: r1 + type: Mutation + status: Failed + message: test mesaage for rule failure \ No newline at end of file diff --git a/main.go b/main.go index 0d2b1a83e1..92617adbeb 100644 --- a/main.go +++ b/main.go @@ -5,11 +5,12 @@ import ( "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/annotations" + clientNew "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + kyvernoinformer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions" "github.com/nirmata/kyverno/pkg/config" - controller "github.com/nirmata/kyverno/pkg/controller" client "github.com/nirmata/kyverno/pkg/dclient" event "github.com/nirmata/kyverno/pkg/event" - gencontroller "github.com/nirmata/kyverno/pkg/gencontroller" + "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/sharedinformer" "github.com/nirmata/kyverno/pkg/utils" "github.com/nirmata/kyverno/pkg/violation" @@ -36,6 +37,22 @@ func main() { glog.Fatalf("Error creating client: %v\n", err) } + // check if the k8 server version is supported ? + // if !version.Supportedk8Server(client) { + // glog.Fatalf("the k8 server version is not supported. refer to https://github.com/nirmata/kyverno/blob/master/documentation/installation.md for more details") + // } + //------------------------------------- + // create policy client + pclient, err := clientNew.NewForConfig(clientConfig) + if err != nil { + glog.Fatalf("Error creating client: %v\n", err) + } + pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10) + pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) + if err != nil { + glog.Fatalf("error creating policy controller: %v\n", err) + } + //------------------------------------- policyInformerFactory, err := sharedinformer.NewSharedInformerFactory(clientConfig) if err != nil { glog.Fatalf("Error creating policy sharedinformer: %v\n", err) @@ -44,15 +61,15 @@ func main() { eventController := event.NewEventController(client, policyInformerFactory) violationBuilder := violation.NewPolicyViolationBuilder(client, policyInformerFactory, eventController) annotationsController := annotations.NewAnnotationControler(client) - policyController := controller.NewPolicyController( - client, - policyInformerFactory, - violationBuilder, - eventController, - annotationsController, - filterK8Resources) + // policyController := controller.NewPolicyController( + // client, + // policyInformerFactory, + // violationBuilder, + // eventController, + // annotationsController, + // filterK8Resources) - genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces(), annotationsController) + // genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces(), annotationsController) tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) @@ -73,22 +90,27 @@ func main() { glog.Fatalf("Failed registering Admission Webhooks: %v\n", err) } + //-------- + pInformer.Start(stopCh) + go pc.Run(1, stopCh) + //TODO add WG for the go routine? + //-------- policyInformerFactory.Run(stopCh) kubeInformer.Start(stopCh) eventController.Run(stopCh) - genControler.Run(stopCh) + // genControler.Run(stopCh) annotationsController.Run(stopCh) - if err = policyController.Run(stopCh); err != nil { - glog.Fatalf("Error running PolicyController: %v\n", err) - } + // if err = policyController.Run(stopCh); err != nil { + // glog.Fatalf("Error running PolicyController: %v\n", err) + // } server.RunAsync() <-stopCh server.Stop() - genControler.Stop() + // genControler.Stop() eventController.Stop() annotationsController.Stop() - policyController.Stop() + // policyController.Stop() } func init() { diff --git a/pkg/clientNew/clientset/versioned/clientset.go b/pkg/clientNew/clientset/versioned/clientset.go new file mode 100644 index 0000000000..8c98dcd7db --- /dev/null +++ b/pkg/clientNew/clientset/versioned/clientset.go @@ -0,0 +1,90 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + kyvernoV1alpha1 *kyvernov1alpha1.KyvernoV1alpha1Client +} + +// KyvernoV1alpha1 retrieves the KyvernoV1alpha1Client +func (c *Clientset) KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface { + return c.kyvernoV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.kyvernoV1alpha1, err = kyvernov1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.kyvernoV1alpha1 = kyvernov1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.kyvernoV1alpha1 = kyvernov1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/pkg/clientNew/clientset/versioned/doc.go b/pkg/clientNew/clientset/versioned/doc.go new file mode 100644 index 0000000000..41721ca52d --- /dev/null +++ b/pkg/clientNew/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/pkg/clientNew/clientset/versioned/fake/clientset_generated.go b/pkg/clientNew/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 0000000000..18e3e0d57b --- /dev/null +++ b/pkg/clientNew/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,77 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1" + fakekyvernov1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +var _ clientset.Interface = &Clientset{} + +// KyvernoV1alpha1 retrieves the KyvernoV1alpha1Client +func (c *Clientset) KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface { + return &fakekyvernov1alpha1.FakeKyvernoV1alpha1{Fake: &c.Fake} +} diff --git a/pkg/clientNew/clientset/versioned/fake/doc.go b/pkg/clientNew/clientset/versioned/fake/doc.go new file mode 100644 index 0000000000..9b99e71670 --- /dev/null +++ b/pkg/clientNew/clientset/versioned/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/pkg/clientNew/clientset/versioned/fake/register.go b/pkg/clientNew/clientset/versioned/fake/register.go new file mode 100644 index 0000000000..f4ee22e9ea --- /dev/null +++ b/pkg/clientNew/clientset/versioned/fake/register.go @@ -0,0 +1,56 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + kyvernov1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/pkg/clientNew/clientset/versioned/scheme/doc.go b/pkg/clientNew/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000000..7dc3756168 --- /dev/null +++ b/pkg/clientNew/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/pkg/clientNew/clientset/versioned/scheme/register.go b/pkg/clientNew/clientset/versioned/scheme/register.go new file mode 100644 index 0000000000..110bace001 --- /dev/null +++ b/pkg/clientNew/clientset/versioned/scheme/register.go @@ -0,0 +1,56 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + kyvernov1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/pkg/clientNew/informers/externalversions/factory.go b/pkg/clientNew/informers/externalversions/factory.go new file mode 100644 index 0000000000..07bddb9a53 --- /dev/null +++ b/pkg/clientNew/informers/externalversions/factory.go @@ -0,0 +1,180 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" + kyverno "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Kyverno() kyverno.Interface +} + +func (f *sharedInformerFactory) Kyverno() kyverno.Interface { + return kyverno.New(f, f.namespace, f.tweakListOptions) +} diff --git a/pkg/clientNew/informers/externalversions/generic.go b/pkg/clientNew/informers/externalversions/generic.go new file mode 100644 index 0000000000..3fdf135f0b --- /dev/null +++ b/pkg/clientNew/informers/externalversions/generic.go @@ -0,0 +1,64 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=kyverno.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("policies"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1alpha1().Policies().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("policyviolations"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1alpha1().PolicyViolations().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/pkg/clientNew/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/clientNew/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 0000000000..2081ff4851 --- /dev/null +++ b/pkg/clientNew/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,40 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/dclient/violation.go b/pkg/dclient/violation.go new file mode 100644 index 0000000000..37f9988161 --- /dev/null +++ b/pkg/dclient/violation.go @@ -0,0 +1,11 @@ +package client + +import ( + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" +) + +//CreatePolicyViolation create a Policy Violation resource +func (c *Client) CreatePolicyViolation(pv kyvernov1alpha1.PolicyViolation) error { + _, err := c.CreateResource("PolicyViolation", ",", pv, false) + return err +} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go new file mode 100644 index 0000000000..a2f4e8240c --- /dev/null +++ b/pkg/policy/controller.go @@ -0,0 +1,778 @@ +package policy + +import ( + "encoding/json" + "fmt" + "reflect" + "sync" + "time" + + "github.com/golang/glog" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" + informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + client "github.com/nirmata/kyverno/pkg/dclient" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" +) + +const ( + // maxRetries is the number of times a Polict will be retried before it is dropped out of the queue. + // With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the times + // a deployment is going to be requeued: + // + // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s + maxRetries = 15 +) + +var controllerKind = kyverno.SchemeGroupVersion.WithKind("Policy") + +// PolicyController is responsible for synchronizing Policy objects stored +// in the system with the corresponding policy violations +type PolicyController struct { + client *client.Client + kyvernoclient *kyvernoclient.Clientset + eventRecorder record.EventRecorder + syncHandler func(pKey string) error + enqueuePolicy func(policy *kyverno.Policy) + + //pvControl is used for adoptin/releasing replica sets + pvControl PVControlInterface + // Policys that need to be synced + queue workqueue.RateLimitingInterface + // pLister can list/get policy from the shared informer's store + pLister lister.PolicyLister + // pvLister can list/get policy violation from the shared informer's store + pvLister lister.PolicyViolationLister + // pListerSynced returns true if the Policy store has been synced at least once + pListerSynced cache.InformerSynced + // pvListerSynced retrns true if the Policy store has been synced at least once + pvListerSynced cache.InformerSynced +} + +// NewPolicyController create a new PolicyController +func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer informer.PolicyInformer, pvInformer informer.PolicyViolationInformer) (*PolicyController, error) { + // Event broad caster + eventBroadcaster := record.NewBroadcaster() + eventBroadcaster.StartLogging(glog.Infof) + eventInterface, err := client.GetEventsInterface() + if err != nil { + return nil, err + } + eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: eventInterface}) + + pc := PolicyController{ + client: client, + kyvernoclient: kyvernoClient, + eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policy_controller"}), + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policy"), + } + + pc.pvControl = RealPVControl{Client: kyvernoClient, Recorder: pc.eventRecorder} + + pInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: pc.addPolicy, + UpdateFunc: pc.updatePolicy, + DeleteFunc: pc.deletePolicy, + }) + + pvInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: pc.addPolicyViolation, + UpdateFunc: pc.updatePolicyViolation, + DeleteFunc: pc.deletePolicyViolation, + }) + + pc.enqueuePolicy = pc.enqueue + pc.syncHandler = pc.syncPolicy + + pc.pLister = pInformer.Lister() + pc.pvLister = pvInformer.Lister() + pc.pListerSynced = pInformer.Informer().HasSynced + pc.pvListerSynced = pInformer.Informer().HasSynced + + return &pc, nil +} + +func (pc *PolicyController) addPolicy(obj interface{}) { + p := obj.(*kyverno.Policy) + glog.V(4).Infof("Adding Policy %s", p.Name) + pc.enqueuePolicy(p) +} + +func (pc *PolicyController) updatePolicy(old, cur interface{}) { + oldP := old.(*kyverno.Policy) + curP := cur.(*kyverno.Policy) + glog.V(4).Infof("Updating Policy %s", oldP.Name) + pc.enqueuePolicy(curP) +} + +func (pc *PolicyController) deletePolicy(obj interface{}) { + p, ok := obj.(*kyverno.Policy) + if !ok { + tombstone, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) + return + } + p, ok = tombstone.Obj.(*kyverno.Policy) + if !ok { + glog.Info(fmt.Errorf("Tombstone contained object that is not a Policy %#v", obj)) + return + } + } + glog.V(4).Infof("Deleting Policy %s", p.Name) + pc.enqueuePolicy(p) +} + +func (pc *PolicyController) addPolicyViolation(obj interface{}) { + pv := obj.(*kyverno.PolicyViolation) + + if pv.DeletionTimestamp != nil { + // On a restart of the controller manager, it's possible for an object to + // show up in a state that is already pending deletion. + pc.deletePolicyViolation(pv) + return + } + + // generate labels to match the policy from the spec, if not present + if updatePolicyLabelIfNotDefined(pc.pvControl, pv) { + return + } + + // If it has a ControllerRef, that's all that matters. + if controllerRef := metav1.GetControllerOf(pv); controllerRef != nil { + p := pc.resolveControllerRef(controllerRef) + if p == nil { + return + } + glog.V(4).Infof("PolicyViolation %s added.", pv.Name) + pc.enqueuePolicy(p) + return + } + + // Otherwise, it's an orphan. Get a list of all matching Policies and sync + // them to see if anyone wants to adopt it. + ps := pc.getPolicyForPolicyViolation(pv) + if len(ps) == 0 { + return + } + glog.V(4).Infof("Orphan Policy Violation %s added.", pv.Name) + for _, p := range ps { + pc.enqueuePolicy(p) + } +} + +func (pc *PolicyController) updatePolicyViolation(old, cur interface{}) { + curPV := cur.(*kyverno.PolicyViolation) + oldPV := old.(*kyverno.PolicyViolation) + if curPV.ResourceVersion == oldPV.ResourceVersion { + // Periodic resync will send update events for all known Policy Violation. + // Two different versions of the same replica set will always have different RVs. + return + } + + // generate labels to match the policy from the spec, if not present + if updatePolicyLabelIfNotDefined(pc.pvControl, curPV) { + return + } + + curControllerRef := metav1.GetControllerOf(curPV) + oldControllerRef := metav1.GetControllerOf(oldPV) + controllerRefChanged := !reflect.DeepEqual(curControllerRef, oldControllerRef) + if controllerRefChanged && oldControllerRef != nil { + // The ControllerRef was changed. Sync the old controller, if any. + if p := pc.resolveControllerRef(oldControllerRef); p != nil { + pc.enqueuePolicy(p) + } + } + // If it has a ControllerRef, that's all that matters. + if curControllerRef != nil { + p := pc.resolveControllerRef(curControllerRef) + if p == nil { + return + } + glog.V(4).Infof("PolicyViolation %s updated.", curPV.Name) + pc.enqueuePolicy(p) + return + } + + // Otherwise, it's an orphan. If anything changed, sync matching controllers + // to see if anyone wants to adopt it now. + labelChanged := !reflect.DeepEqual(curPV.Labels, oldPV.Labels) + if labelChanged || controllerRefChanged { + ps := pc.getPolicyForPolicyViolation(curPV) + if len(ps) == 0 { + return + } + glog.V(4).Infof("Orphan PolicyViolation %s updated", curPV.Name) + for _, p := range ps { + pc.enqueuePolicy(p) + } + } +} + +// deletePolicyViolation enqueues the Policy that manages a PolicyViolation when +// the PolicyViolation is deleted. obj could be an *kyverno.PolicyViolation, or +// a DeletionFinalStateUnknown marker item. + +func (pc *PolicyController) deletePolicyViolation(obj interface{}) { + pv, ok := obj.(*kyverno.PolicyViolation) + // When a delete is dropped, the relist will notice a PolicyViolation in the store not + // in the list, leading to the insertion of a tombstone object which contains + // the deleted key/value. Note that this value might be stale. If the PolicyViolation + // changed labels the new Policy will not be woken up till the periodic resync. + if !ok { + tombstone, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) + return + } + pv, ok = tombstone.Obj.(*kyverno.PolicyViolation) + if !ok { + glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) + return + } + } + controllerRef := metav1.GetControllerOf(pv) + if controllerRef == nil { + // No controller should care about orphans being deleted. + return + } + p := pc.resolveControllerRef(controllerRef) + if p == nil { + return + } + glog.V(4).Infof("PolicyViolation %s deleted", pv.Name) + pc.enqueuePolicy(p) +} + +// resolveControllerRef returns the controller referenced by a ControllerRef, +// or nil if the ControllerRef could not be resolved to a matching controller +// of the correct Kind. +func (pc *PolicyController) resolveControllerRef(controllerRef *metav1.OwnerReference) *kyverno.Policy { + // We can't look up by UID, so look up by Name and then verify UID. + // Don't even try to look up by Name if it's the wrong Kind. + if controllerRef.Kind != controllerRef.Kind { + return nil + } + p, err := pc.pLister.Get(controllerRef.Name) + if err != nil { + return nil + } + if p.UID != controllerRef.UID { + // The controller we found with this Name is not the same one that the + // ControllerRef points to. + return nil + } + return p +} + +func (pc *PolicyController) getPolicyForPolicyViolation(pv *kyverno.PolicyViolation) []*kyverno.Policy { + policies, err := pc.pLister.GetPolicyForPolicyViolation(pv) + if err != nil || len(policies) == 0 { + return nil + } + // Because all ReplicaSet's belonging to a deployment should have a unique label key, + // there should never be more than one deployment returned by the above method. + // If that happens we should probably dynamically repair the situation by ultimately + // trying to clean up one of the controllers, for now we just return the older one + if len(policies) > 1 { + // ControllerRef will ensure we don't do anything crazy, but more than one + // item in this list nevertheless constitutes user error. + glog.V(4).Infof("user error! more than one policy is selecting policy violation %s with labels: %#v, returning %s", + pv.Name, pv.Labels, policies[0].Name) + } + return policies +} + +func (pc *PolicyController) enqueue(policy *kyverno.Policy) { + key, err := cache.MetaNamespaceKeyFunc(policy) + if err != nil { + glog.Error(err) + return + } + pc.queue.Add(key) +} + +// Run begins watching and syncing. +func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) { + + defer utilruntime.HandleCrash() + defer pc.queue.ShutDown() + + glog.Info("Starting policy controller") + defer glog.Info("Shutting down policy controller") + + if !cache.WaitForCacheSync(stopCh, pc.pListerSynced) { + return + } + for i := 0; i < workers; i++ { + go wait.Until(pc.worker, time.Second, stopCh) + } + <-stopCh +} + +// worker runs a worker thread that just dequeues items, processes them, and marks them done. +// It enforces that the syncHandler is never invoked concurrently with the same key. +func (pc *PolicyController) worker() { + for pc.processNextWorkItem() { + } +} + +func (pc *PolicyController) processNextWorkItem() bool { + key, quit := pc.queue.Get() + if quit { + return false + } + defer pc.queue.Done(key) + + err := pc.syncHandler(key.(string)) + pc.handleErr(err, key) + + return true +} + +func (pc *PolicyController) handleErr(err error, key interface{}) { + if err == nil { + pc.queue.Forget(key) + return + } + + if pc.queue.NumRequeues(key) < maxRetries { + glog.V(2).Infof("Error syncing Policy %v: %v", key, err) + pc.queue.AddRateLimited(key) + return + } + + utilruntime.HandleError(err) + glog.V(2).Infof("Dropping deployment %q out of the queue: %v", key, err) + pc.queue.Forget(key) +} + +func (pc *PolicyController) syncPolicy(key string) error { + startTime := time.Now() + glog.V(4).Infof("Started syncing policy %q (%v)", key, startTime) + defer func() { + glog.V(4).Infof("Finished syncing policy %q (%v)", key, time.Since(startTime)) + }() + policy, err := pc.pLister.Get(key) + if errors.IsNotFound(err) { + glog.V(2).Infof("Policy %v has been deleted", key) + return nil + } + + if err != nil { + return err + } + + // Deep-copy otherwise we are mutating our cache. + // TODO: Deep-copy only when needed. + p := policy.DeepCopy() + // TODO: Update Status to update ObserverdGeneration + + pvList, err := pc.getPolicyViolationsForPolicy(p) + if err != nil { + return err + } + + // // Add messages + + if p.DeletionTimestamp != nil { + return pc.syncStatusOnly(p, pvList) + } + return nil +} + +//TODO +func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error { + // Sync Status based on Policy Violation + return nil +} +func (pc *PolicyController) getPolicyViolationsForPolicy(p *kyverno.Policy) ([]*kyverno.PolicyViolation, error) { + // List all PolicyViolation to find those we own but that no longer match our + // selector. They will be orphaned by ClaimPolicyViolation(). + pvList, err := pc.pvLister.List(labels.Everything()) + if err != nil { + return nil, err + } + policyLabelmap := map[string]string{"policy": p.Name} + //NOt using a field selector, as the match function will have to cash the runtime.object + // to get the field, while it can get labels directly, saves the cast effort + //spec.policyName!=default + // fs := fields.Set{"spec.name": name}.AsSelector().String() + + ls := &metav1.LabelSelector{} + err = metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&policyLabelmap, ls, nil) + if err != nil { + return nil, fmt.Errorf("failed to generate label sector of Policy name %s: %v", p.Name, err) + } + policySelector, err := metav1.LabelSelectorAsSelector(ls) + if err != nil { + return nil, fmt.Errorf("Policy %s has invalid label selector: %v", p.Name, err) + } + + canAdoptFunc := RecheckDeletionTimestamp(func() (metav1.Object, error) { + fresh, err := pc.kyvernoclient.KyvernoV1alpha1().Policies().Get(p.Name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + if fresh.UID != p.UID { + return nil, fmt.Errorf("original Policy %v is gone: got uid %v, wanted %v", p.Name, fresh.UID, p.UID) + } + return fresh, nil + }) + + cm := NewPolicyViolationControllerRefManager(pc.pvControl, p, policySelector, controllerKind, canAdoptFunc) + + return cm.claimPolicyViolations(pvList) +} + +func (m *PolicyViolationControllerRefManager) claimPolicyViolations(sets []*kyverno.PolicyViolation) ([]*kyverno.PolicyViolation, error) { + var claimed []*kyverno.PolicyViolation + var errlist []error + + match := func(obj metav1.Object) bool { + return m.Selector.Matches(labels.Set(obj.GetLabels())) + } + adopt := func(obj metav1.Object) error { + return m.adoptPolicyViolation(obj.(*kyverno.PolicyViolation)) + } + release := func(obj metav1.Object) error { + return m.releasePolicyViolation(obj.(*kyverno.PolicyViolation)) + } + + for _, pv := range sets { + ok, err := m.ClaimObject(pv, match, adopt, release) + if err != nil { + errlist = append(errlist, err) + continue + } + if ok { + claimed = append(claimed, pv) + } + } + return claimed, utilerrors.NewAggregate(errlist) +} + +func (m *PolicyViolationControllerRefManager) adoptPolicyViolation(pv *kyverno.PolicyViolation) error { + if err := m.CanAdopt(); err != nil { + return fmt.Errorf("can't adopt PolicyViolation %v (%v): %v", pv.Name, pv.UID, err) + } + // Note that ValidateOwnerReferences() will reject this patch if another + // OwnerReference exists with controller=true. + //TODO Add JSON Patch Owner reference for resource + //TODO Update owner refence for resource + controllerFlag := true + blockOwnerDeletionFlag := true + pOwnerRef := metav1.OwnerReference{APIVersion: m.controllerKind.GroupVersion().String(), + Kind: m.controllerKind.Kind, + Name: m.Controller.GetName(), + UID: m.Controller.GetUID(), + Controller: &controllerFlag, + BlockOwnerDeletion: &blockOwnerDeletionFlag, + } + addControllerPatch, err := createOwnerReferencePatch(pOwnerRef) + if err != nil { + glog.Errorf("failed to add owner reference %v for PolicyViolation %s: %v", pOwnerRef, pv.Name, err) + return err + } + // addControllerPatch := fmt.Sprintf( + // `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`, + // m.controllerKind.GroupVersion(), m.controllerKind.Kind, + // m.Controller.GetName(), m.Controller.GetUID(), pv.UID) + + return m.pvControl.PatchPolicyViolation(pv.Name, addControllerPatch) +} + +type patchOwnerReferenceValue struct { + Op string `json:"op"` + Path string `json:"path"` + Value []metav1.OwnerReference `json:"value"` +} + +func createOwnerReferencePatch(ownerRef metav1.OwnerReference) ([]byte, error) { + payload := []patchOwnerReferenceValue{{ + Op: "add", + Path: "/metadata/ownerReferences", + Value: []metav1.OwnerReference{ownerRef}, + }} + return json.Marshal(payload) +} + +func removeOwnerReferencePatch(ownerRef metav1.OwnerReference) ([]byte, error) { + payload := []patchOwnerReferenceValue{{ + Op: "remove", + Path: "/metadata/ownerReferences", + Value: []metav1.OwnerReference{ownerRef}, + }} + return json.Marshal(payload) +} + +func (m *PolicyViolationControllerRefManager) releasePolicyViolation(pv *kyverno.PolicyViolation) error { + glog.V(2).Infof("patching PolicyViolation %s to remove its controllerRef to %s/%s:%s", + pv.Name, m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.Controller.GetName()) + //TODO JSON patch for owner reference for resources + controllerFlag := true + blockOwnerDeletionFlag := true + pOwnerRef := metav1.OwnerReference{APIVersion: m.controllerKind.GroupVersion().String(), + Kind: m.controllerKind.Kind, + Name: m.Controller.GetName(), + UID: m.Controller.GetUID(), + Controller: &controllerFlag, + BlockOwnerDeletion: &blockOwnerDeletionFlag, + } + + removeControllerPatch, err := removeOwnerReferencePatch(pOwnerRef) + if err != nil { + glog.Errorf("failed to add owner reference %v for PolicyViolation %s: %v", pOwnerRef, pv.Name, err) + return err + } + + // deleteOwnerRefPatch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, m.Controller.GetUID(), pv.UID) + + err = m.pvControl.PatchPolicyViolation(pv.Name, removeControllerPatch) + if err != nil { + if errors.IsNotFound(err) { + // If the ReplicaSet no longer exists, ignore it. + return nil + } + if errors.IsInvalid(err) { + // Invalid error will be returned in two cases: 1. the ReplicaSet + // has no owner reference, 2. the uid of the ReplicaSet doesn't + // match, which means the ReplicaSet is deleted and then recreated. + // In both cases, the error can be ignored. + return nil + } + } + return err +} + +type PolicyViolationControllerRefManager struct { + BaseControllerRefManager + controllerKind schema.GroupVersionKind + pvControl PVControlInterface +} + +func NewPolicyViolationControllerRefManager( + pvControl PVControlInterface, + controller metav1.Object, + selector labels.Selector, + controllerKind schema.GroupVersionKind, + canAdopt func() error, +) *PolicyViolationControllerRefManager { + + m := PolicyViolationControllerRefManager{ + BaseControllerRefManager: BaseControllerRefManager{ + Controller: controller, + Selector: selector, + CanAdoptFunc: canAdopt, + }, + controllerKind: controllerKind, + pvControl: pvControl, + } + return &m +} + +type BaseControllerRefManager struct { + Controller metav1.Object + Selector labels.Selector + canAdoptErr error + canAdoptOnce sync.Once + CanAdoptFunc func() error +} + +func (m *BaseControllerRefManager) CanAdopt() error { + m.canAdoptOnce.Do(func() { + if m.CanAdoptFunc != nil { + m.canAdoptErr = m.CanAdoptFunc() + } + }) + return m.canAdoptErr +} + +func (m *BaseControllerRefManager) ClaimObject(obj metav1.Object, match func(metav1.Object) bool, adopt, release func(metav1.Object) error) (bool, error) { + controllerRef := metav1.GetControllerOf(obj) + if controllerRef != nil { + if controllerRef.UID != m.Controller.GetUID() { + // Owned by someone else. Ignore + return false, nil + } + if match(obj) { + // We already own it and the selector matches. + // Return true (successfully claimed) before checking deletion timestamp. + // We're still allowed to claim things we already own while being deleted + // because doing so requires taking no actions. + return true, nil + + } + // Owned by us but selector doesn't match. + // Try to release, unless we're being deleted. + if m.Controller.GetDeletionTimestamp() != nil { + return false, nil + } + if err := release(obj); err != nil { + // If the PolicyViolation no longer exists, ignore the error. + if errors.IsNotFound(err) { + return false, nil + } + // Either someone else released it, or there was a transient error. + // The controller should requeue and try again if it's still stale. + return false, err + } + // Successfully released. + return false, nil + } + // It's an orphan. + if m.Controller.GetDeletionTimestamp() != nil || !match(obj) { + // Ignore if we're being deleted or selector doesn't match. + return false, nil + } + if obj.GetDeletionTimestamp() != nil { + // Ignore if the object is being deleted + return false, nil + } + // Selector matches. Try to adopt. + if err := adopt(obj); err != nil { + // If the PolicyViolation no longer exists, ignore the error + if errors.IsNotFound(err) { + return false, nil + } + // Either someone else claimed it first, or there was a transient error. + // The controller should requeue and try again if it's still orphaned. + return false, err + } + // Successfully adopted. + return true, nil + +} + +type PVControlInterface interface { + PatchPolicyViolation(name string, data []byte) error +} + +// RealPVControl is the default implementation of PVControlInterface. +type RealPVControl struct { + Client kyvernoclient.Interface + Recorder record.EventRecorder +} + +func (r RealPVControl) PatchPolicyViolation(name string, data []byte) error { + _, err := r.Client.KyvernoV1alpha1().PolicyViolations().Patch(name, types.JSONPatchType, data) + return err +} + +// RecheckDeletionTimestamp returns a CanAdopt() function to recheck deletion. +// +// The CanAdopt() function calls getObject() to fetch the latest value, +// and denies adoption attempts if that object has a non-nil DeletionTimestamp. +func RecheckDeletionTimestamp(getObject func() (metav1.Object, error)) func() error { + return func() error { + obj, err := getObject() + if err != nil { + return fmt.Errorf("can't recheck DeletionTimestamp: %v", err) + } + if obj.GetDeletionTimestamp() != nil { + return fmt.Errorf("%v/%v has just been deleted at %v", obj.GetNamespace(), obj.GetName(), obj.GetDeletionTimestamp()) + } + return nil + } +} + +type patchLabelValue struct { + Op string `json:"op"` + Path string `json:"path"` + Value string `json:"value"` +} + +type patchLabelMapValue struct { + Op string `json:"op"` + Path string `json:"path"` + Value map[string]string `json:"value"` +} + +func createLabelPatch(policy string) ([]byte, error) { + payload := []patchLabelValue{{ + Op: "add", + Path: "/metadata/labels/policy", + Value: policy, + }} + return json.Marshal(payload) +} + +func createLabelMapPatch(policy string) ([]byte, error) { + payload := []patchLabelMapValue{{ + Op: "add", + Path: "/metadata/labels", + Value: map[string]string{"policy": policy}, + }} + return json.Marshal(payload) +} + +//updatePolicyLabelIfNotDefined adds the label 'policy' to the PolicyViolation +// label is used here to lookup policyViolation and corresponding Policy +func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.PolicyViolation) bool { + updateLabel := func() bool { + glog.V(4).Infof("adding label 'policy:%s' to PolicyViolation %s", pv.Spec.PolicyName, pv.Name) + // add label based on the policy spec + labels := pv.GetLabels() + if pv.Spec.PolicyName == "" { + glog.Error("policy not defined for violation") + // should be cleaned up + return false + } + if labels == nil { + // create a patch to generate the labels map with policy label + patch, err := createLabelMapPatch(pv.Spec.PolicyName) + if err != nil { + glog.Errorf("unable to init label map. %v", err) + return false + } + if err := pvControl.PatchPolicyViolation(pv.Name, patch); err != nil { + glog.Errorf("Unable to add 'policy' label to PolicyViolation %s: %v", pv.Name, err) + return false + } + // update successful + return true + } + // JSON Patch to add exact label + labelPatch, err := createLabelPatch(pv.Spec.PolicyName) + if err != nil { + glog.Errorf("failed to generate patch to add label 'policy': %v", err) + return false + } + if err := pvControl.PatchPolicyViolation(pv.Name, labelPatch); err != nil { + glog.Errorf("Unable to add 'policy' label to PolicyViolation %s: %v", pv.Name, err) + return false + } + // update successful + return true + } + + var policyName string + var ok bool + // operate oncopy of resource + curLabels := pv.GetLabels() + if policyName, ok = curLabels["policy"]; !ok { + return updateLabel() + } + // TODO: would be benificial to add a check to verify if the policy in name and resource spec match + if policyName != pv.Spec.PolicyName { + glog.Errorf("label 'policy:%s' and spec.policyName %s dont match ", policyName, pv.Spec.PolicyName) + //TODO handle this case + return updateLabel() + } + return false +} diff --git a/scripts/new_update-codegen.sh b/scripts/new_update-codegen.sh new file mode 100755 index 0000000000..38f54515e6 --- /dev/null +++ b/scripts/new_update-codegen.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -o errexit +set -o nounset +set -o pipefail + +case "$(uname -s)" in + Linux*) linkutil=readlink;; + Darwin*) linkutil=greadlink;; + *) machine="UNKNOWN:${unameOut}" +esac + +# get nirmata root +NIRMATA_DIR=$(dirname ${BASH_SOURCE})/.. +NIRMATA_ROOT=$(${linkutil} -f ${NIRMATA_DIR}) + +# get relative path to code generation script +CODEGEN_PKG=${NIRMATA_DIR}/vendor/k8s.io/code-generator + +# get relative path of nirmata +NIRMATA_PKG=${NIRMATA_ROOT#"${GOPATH}/src/"} + +# perform code generation +${CODEGEN_PKG}/generate-groups.sh \ + "deepcopy,client,informer,lister" \ + ${NIRMATA_PKG}/pkg/clientNew \ + ${NIRMATA_PKG}/pkg/api \ + kyverno:v1alpha1 From 9a8d9b316f03082a9496224920ee644abd3404a5 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Thu, 8 Aug 2019 02:32:53 -0700 Subject: [PATCH 02/66] sync Status in Policy --- pkg/api/kyverno/register.go | 6 + pkg/api/kyverno/v1alpha1/doc.go | 4 + pkg/api/kyverno/v1alpha1/register.go | 37 ++ pkg/api/kyverno/v1alpha1/types.go | 143 +++++++ pkg/api/kyverno/v1alpha1/utils.go | 103 +++++ .../kyverno/v1alpha1/zz_generated.deepcopy.go | 367 ++++++++++++++++++ .../versioned/typed/kyverno/v1alpha1/doc.go | 20 + .../typed/kyverno/v1alpha1/fake/doc.go | 20 + .../v1alpha1/fake/fake_kyverno_client.go | 44 +++ .../kyverno/v1alpha1/fake/fake_policy.go | 131 +++++++ .../v1alpha1/fake/fake_policyviolation.go | 131 +++++++ .../kyverno/v1alpha1/generated_expansion.go | 23 ++ .../typed/kyverno/v1alpha1/kyverno_client.go | 95 +++++ .../typed/kyverno/v1alpha1/policy.go | 180 +++++++++ .../typed/kyverno/v1alpha1/policyviolation.go | 180 +++++++++ .../externalversions/kyverno/interface.go | 46 +++ .../kyverno/v1alpha1/interface.go | 52 +++ .../kyverno/v1alpha1/policy.go | 88 +++++ .../kyverno/v1alpha1/policyviolation.go | 88 +++++ .../kyverno/v1alpha1/expansion_generated.go | 73 ++++ .../listers/kyverno/v1alpha1/policy.go | 65 ++++ .../kyverno/v1alpha1/policyviolation.go | 65 ++++ pkg/policy/controller.go | 43 +- 23 files changed, 1988 insertions(+), 16 deletions(-) create mode 100644 pkg/api/kyverno/register.go create mode 100644 pkg/api/kyverno/v1alpha1/doc.go create mode 100644 pkg/api/kyverno/v1alpha1/register.go create mode 100644 pkg/api/kyverno/v1alpha1/types.go create mode 100644 pkg/api/kyverno/v1alpha1/utils.go create mode 100644 pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go create mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go create mode 100644 pkg/clientNew/informers/externalversions/kyverno/interface.go create mode 100644 pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go create mode 100644 pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go create mode 100644 pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go create mode 100644 pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go create mode 100644 pkg/clientNew/listers/kyverno/v1alpha1/policy.go create mode 100644 pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go diff --git a/pkg/api/kyverno/register.go b/pkg/api/kyverno/register.go new file mode 100644 index 0000000000..0025c5debc --- /dev/null +++ b/pkg/api/kyverno/register.go @@ -0,0 +1,6 @@ +package kyverno + +const ( + // GroupName must be the same as specified in Policy CRD + GroupName = "kyverno.io" +) diff --git a/pkg/api/kyverno/v1alpha1/doc.go b/pkg/api/kyverno/v1alpha1/doc.go new file mode 100644 index 0000000000..4fa2b53292 --- /dev/null +++ b/pkg/api/kyverno/v1alpha1/doc.go @@ -0,0 +1,4 @@ +// +k8s:deepcopy-gen=package +// +groupName=kyverno.io + +package v1alpha1 diff --git a/pkg/api/kyverno/v1alpha1/register.go b/pkg/api/kyverno/v1alpha1/register.go new file mode 100644 index 0000000000..500bdb7670 --- /dev/null +++ b/pkg/api/kyverno/v1alpha1/register.go @@ -0,0 +1,37 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/nirmata/kyverno/pkg/apis/policy" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: policy.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &PolicyViolation{}, + &PolicyViolationList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/api/kyverno/v1alpha1/types.go b/pkg/api/kyverno/v1alpha1/types.go new file mode 100644 index 0000000000..ffad06ca80 --- /dev/null +++ b/pkg/api/kyverno/v1alpha1/types.go @@ -0,0 +1,143 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Policy contains rules to be applied to created resources +type Policy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec Spec `json:"spec"` + Status PolicyStatus `json:"status"` +} + +// Spec describes policy behavior by its rules +type Spec struct { + Rules []Rule `json:"rules"` + ValidationFailureAction string `json:"validationFailureAction"` +} + +// Rule is set of mutation, validation and generation actions +// for the single resource description +type Rule struct { + Name string `json:"name"` + MatchResources MatchResources `json:"match"` + ExcludeResources ExcludeResources `json:"exclude,omitempty"` + Mutation Mutation `json:"mutate"` + Validation Validation `json:"validate"` + Generation Generation `json:"generate"` +} + +//MatchResources contains resource description of the resources that the rule is to apply on +type MatchResources struct { + ResourceDescription `json:"resources"` +} + +//ExcludeResources container resource description of the resources that are to be excluded from the applying the policy rule +type ExcludeResources struct { + ResourceDescription `json:"resources"` +} + +// ResourceDescription describes the resource to which the PolicyRule will be applied. +type ResourceDescription struct { + Kinds []string `json:"kinds"` + Name string `json:"name"` + Namespace string `json:"namespace,omitempty"` + Selector *metav1.LabelSelector `json:"selector"` +} + +// Mutation describes the way how Mutating Webhook will react on resource creation +type Mutation struct { + Overlay interface{} `json:"overlay"` + Patches []Patch `json:"patches"` +} + +// +k8s:deepcopy-gen=false + +// Patch declares patch operation for created object according to RFC 6902 +type Patch struct { + Path string `json:"path"` + Operation string `json:"op"` + Value interface{} `json:"value"` +} + +// Validation describes the way how Validating Webhook will check the resource on creation +type Validation struct { + Message string `json:"message"` + Pattern interface{} `json:"pattern"` +} + +// Generation describes which resources will be created when other resource is created +type Generation struct { + Kind string `json:"kind"` + Name string `json:"name"` + Data interface{} `json:"data"` + Clone CloneFrom `json:"clone"` +} + +// CloneFrom - location of a Secret or a ConfigMap +// which will be used as source when applying 'generate' +type CloneFrom struct { + Namespace string `json:"namespace"` + Name string `json:"name"` +} + +//PolicyStatus provides status for violations +type PolicyStatus struct { + Violations int `json:"violations"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PolicyList is a list of Policy resources +type PolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []Policy `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PolicyViolation stores the information regarinding the resources for which a policy failed to apply +type PolicyViolation struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec PolicyViolationSpec `json:"spec"` + Status string `json:"status"` +} + +// PolicyViolationSpec describes policy behavior by its rules +type PolicyViolationSpec struct { + Policy string `json:"policy"` + ResourceSpec `json:"resource"` + ViolatedRules []ViolatedRule `json:"rules"` +} + +// ResourceSpec information to identify the resource +type ResourceSpec struct { + Kind string `json:"kind"` + Namespace string `json:"namespace,omitempty"` + Name string `json:"name"` +} + +type ViolatedRule struct { + Name string `json:"name"` + Type string `json:"type"` + Message string `json:"message"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PolicyViolationList is a list of Policy Violation +type PolicyViolationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []PolicyViolation `json:"items"` +} diff --git a/pkg/api/kyverno/v1alpha1/utils.go b/pkg/api/kyverno/v1alpha1/utils.go new file mode 100644 index 0000000000..892a52e74f --- /dev/null +++ b/pkg/api/kyverno/v1alpha1/utils.go @@ -0,0 +1,103 @@ +package v1alpha1 + +import ( + "errors" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Validate checks if rule is not empty and all substructures are valid +func (r *Rule) Validate() error { + // check matches Resoource Description of match resource + err := r.MatchResources.ResourceDescription.Validate() + if err != nil { + return err + } + + return nil +} + +// Validate checks if all necesarry fields are present and have values. Also checks a Selector. +// Returns error if +// - kinds is not defined +func (pr *ResourceDescription) Validate() error { + if len(pr.Kinds) == 0 { + return errors.New("The Kind is not specified") + } + + if pr.Selector != nil { + selector, err := metav1.LabelSelectorAsSelector(pr.Selector) + if err != nil { + return err + } + requirements, _ := selector.Requirements() + if len(requirements) == 0 { + return errors.New("The requirements are not specified in selector") + } + } + + return nil +} + +// Validate if all mandatory PolicyPatch fields are set +func (pp *Patch) Validate() error { + if pp.Path == "" { + return errors.New("JSONPatch field 'path' is mandatory") + } + + if pp.Operation == "add" || pp.Operation == "replace" { + if pp.Value == nil { + return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation) + } + + return nil + } else if pp.Operation == "remove" { + return nil + } + + return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation) +} + +// Validate returns error if generator is configured incompletely +func (gen *Generation) Validate() error { + if gen.Data == nil && gen.Clone == (CloneFrom{}) { + return fmt.Errorf("Neither data nor clone (source) of %s is specified", gen.Kind) + } + if gen.Data != nil && gen.Clone != (CloneFrom{}) { + return fmt.Errorf("Both data nor clone (source) of %s are specified", gen.Kind) + } + return nil +} + +// DeepCopyInto is declared because k8s:deepcopy-gen is +// not able to generate this method for interface{} member +func (in *Mutation) DeepCopyInto(out *Mutation) { + if out != nil { + *out = *in + } +} + +// DeepCopyInto is declared because k8s:deepcopy-gen is +// not able to generate this method for interface{} member +func (pp *Patch) DeepCopyInto(out *Patch) { + if out != nil { + *out = *pp + } +} + +// DeepCopyInto is declared because k8s:deepcopy-gen is +// not able to generate this method for interface{} member +func (in *Validation) DeepCopyInto(out *Validation) { + if out != nil { + *out = *in + } +} + +// DeepCopyInto is declared because k8s:deepcopy-gen is +// not able to generate this method for interface{} member +func (gen *Generation) DeepCopyInto(out *Generation) { + if out != nil { + *out = *gen + } +} diff --git a/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go b/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..72e050cb39 --- /dev/null +++ b/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,367 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloneFrom) DeepCopyInto(out *CloneFrom) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloneFrom. +func (in *CloneFrom) DeepCopy() *CloneFrom { + if in == nil { + return nil + } + out := new(CloneFrom) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExcludeResources) DeepCopyInto(out *ExcludeResources) { + *out = *in + in.ResourceDescription.DeepCopyInto(&out.ResourceDescription) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludeResources. +func (in *ExcludeResources) DeepCopy() *ExcludeResources { + if in == nil { + return nil + } + out := new(ExcludeResources) + in.DeepCopyInto(out) + return out +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Generation. +func (in *Generation) DeepCopy() *Generation { + if in == nil { + return nil + } + out := new(Generation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MatchResources) DeepCopyInto(out *MatchResources) { + *out = *in + in.ResourceDescription.DeepCopyInto(&out.ResourceDescription) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchResources. +func (in *MatchResources) DeepCopy() *MatchResources { + if in == nil { + return nil + } + out := new(MatchResources) + in.DeepCopyInto(out) + return out +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mutation. +func (in *Mutation) DeepCopy() *Mutation { + if in == nil { + return nil + } + out := new(Mutation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Policy) DeepCopyInto(out *Policy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Policy. +func (in *Policy) DeepCopy() *Policy { + if in == nil { + return nil + } + out := new(Policy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Policy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyList) DeepCopyInto(out *PolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Policy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList. +func (in *PolicyList) DeepCopy() *PolicyList { + if in == nil { + return nil + } + out := new(PolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyStatus. +func (in *PolicyStatus) DeepCopy() *PolicyStatus { + if in == nil { + return nil + } + out := new(PolicyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyViolation) DeepCopyInto(out *PolicyViolation) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyViolation. +func (in *PolicyViolation) DeepCopy() *PolicyViolation { + if in == nil { + return nil + } + out := new(PolicyViolation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PolicyViolation) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyViolationList) DeepCopyInto(out *PolicyViolationList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PolicyViolation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyViolationList. +func (in *PolicyViolationList) DeepCopy() *PolicyViolationList { + if in == nil { + return nil + } + out := new(PolicyViolationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PolicyViolationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyViolationSpec) DeepCopyInto(out *PolicyViolationSpec) { + *out = *in + out.ResourceSpec = in.ResourceSpec + if in.ViolatedRules != nil { + in, out := &in.ViolatedRules, &out.ViolatedRules + *out = make([]ViolatedRule, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyViolationSpec. +func (in *PolicyViolationSpec) DeepCopy() *PolicyViolationSpec { + if in == nil { + return nil + } + out := new(PolicyViolationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) { + *out = *in + if in.Kinds != nil { + in, out := &in.Kinds, &out.Kinds + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceDescription. +func (in *ResourceDescription) DeepCopy() *ResourceDescription { + if in == nil { + return nil + } + out := new(ResourceDescription) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceSpec. +func (in *ResourceSpec) DeepCopy() *ResourceSpec { + if in == nil { + return nil + } + out := new(ResourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Rule) DeepCopyInto(out *Rule) { + *out = *in + in.MatchResources.DeepCopyInto(&out.MatchResources) + in.ExcludeResources.DeepCopyInto(&out.ExcludeResources) + in.Mutation.DeepCopyInto(&out.Mutation) + in.Validation.DeepCopyInto(&out.Validation) + in.Generation.DeepCopyInto(&out.Generation) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule. +func (in *Rule) DeepCopy() *Rule { + if in == nil { + return nil + } + out := new(Rule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Spec) DeepCopyInto(out *Spec) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]Rule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spec. +func (in *Spec) DeepCopy() *Spec { + if in == nil { + return nil + } + out := new(Spec) + in.DeepCopyInto(out) + return out +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Validation. +func (in *Validation) DeepCopy() *Validation { + if in == nil { + return nil + } + out := new(Validation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ViolatedRule) DeepCopyInto(out *ViolatedRule) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ViolatedRule. +func (in *ViolatedRule) DeepCopy() *ViolatedRule { + if in == nil { + return nil + } + out := new(ViolatedRule) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go new file mode 100644 index 0000000000..df51baa4d4 --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go new file mode 100644 index 0000000000..16f4439906 --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go new file mode 100644 index 0000000000..244260245f --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go @@ -0,0 +1,44 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeKyvernoV1alpha1 struct { + *testing.Fake +} + +func (c *FakeKyvernoV1alpha1) Policies() v1alpha1.PolicyInterface { + return &FakePolicies{c} +} + +func (c *FakeKyvernoV1alpha1) PolicyViolations() v1alpha1.PolicyViolationInterface { + return &FakePolicyViolations{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeKyvernoV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go new file mode 100644 index 0000000000..76bd94b95f --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go @@ -0,0 +1,131 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakePolicies implements PolicyInterface +type FakePolicies struct { + Fake *FakeKyvernoV1alpha1 +} + +var policiesResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policies"} + +var policiesKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "Policy"} + +// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. +func (c *FakePolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(policiesResource, name), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// List takes label and field selectors, and returns the list of Policies that match those selectors. +func (c *FakePolicies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(policiesResource, policiesKind, opts), &v1alpha1.PolicyList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.PolicyList{ListMeta: obj.(*v1alpha1.PolicyList).ListMeta} + for _, item := range obj.(*v1alpha1.PolicyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested policies. +func (c *FakePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(policiesResource, opts)) +} + +// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *FakePolicies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(policiesResource, policy), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *FakePolicies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(policiesResource, policy), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePolicies) UpdateStatus(policy *v1alpha1.Policy) (*v1alpha1.Policy, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(policiesResource, "status", policy), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// Delete takes name of the policy and deletes it. Returns an error if one occurs. +func (c *FakePolicies) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(policiesResource, name), &v1alpha1.Policy{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(policiesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.PolicyList{}) + return err +} + +// Patch applies the patch and returns the patched policy. +func (c *FakePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(policiesResource, name, pt, data, subresources...), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go new file mode 100644 index 0000000000..e5306f11fe --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go @@ -0,0 +1,131 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakePolicyViolations implements PolicyViolationInterface +type FakePolicyViolations struct { + Fake *FakeKyvernoV1alpha1 +} + +var policyviolationsResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policyviolations"} + +var policyviolationsKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "PolicyViolation"} + +// Get takes name of the policyViolation, and returns the corresponding policyViolation object, and an error if there is any. +func (c *FakePolicyViolations) Get(name string, options v1.GetOptions) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(policyviolationsResource, name), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// List takes label and field selectors, and returns the list of PolicyViolations that match those selectors. +func (c *FakePolicyViolations) List(opts v1.ListOptions) (result *v1alpha1.PolicyViolationList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(policyviolationsResource, policyviolationsKind, opts), &v1alpha1.PolicyViolationList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.PolicyViolationList{ListMeta: obj.(*v1alpha1.PolicyViolationList).ListMeta} + for _, item := range obj.(*v1alpha1.PolicyViolationList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested policyViolations. +func (c *FakePolicyViolations) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(policyviolationsResource, opts)) +} + +// Create takes the representation of a policyViolation and creates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *FakePolicyViolations) Create(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(policyviolationsResource, policyViolation), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// Update takes the representation of a policyViolation and updates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *FakePolicyViolations) Update(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(policyviolationsResource, policyViolation), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePolicyViolations) UpdateStatus(policyViolation *v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(policyviolationsResource, "status", policyViolation), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// Delete takes name of the policyViolation and deletes it. Returns an error if one occurs. +func (c *FakePolicyViolations) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(policyviolationsResource, name), &v1alpha1.PolicyViolation{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePolicyViolations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(policyviolationsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.PolicyViolationList{}) + return err +} + +// Patch applies the patch and returns the patched policyViolation. +func (c *FakePolicyViolations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(policyviolationsResource, name, pt, data, subresources...), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go new file mode 100644 index 0000000000..442fa55942 --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go @@ -0,0 +1,23 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type PolicyExpansion interface{} + +type PolicyViolationExpansion interface{} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go new file mode 100644 index 0000000000..93b837d4df --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go @@ -0,0 +1,95 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +type KyvernoV1alpha1Interface interface { + RESTClient() rest.Interface + PoliciesGetter + PolicyViolationsGetter +} + +// KyvernoV1alpha1Client is used to interact with features provided by the kyverno.io group. +type KyvernoV1alpha1Client struct { + restClient rest.Interface +} + +func (c *KyvernoV1alpha1Client) Policies() PolicyInterface { + return newPolicies(c) +} + +func (c *KyvernoV1alpha1Client) PolicyViolations() PolicyViolationInterface { + return newPolicyViolations(c) +} + +// NewForConfig creates a new KyvernoV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*KyvernoV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &KyvernoV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new KyvernoV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *KyvernoV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new KyvernoV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *KyvernoV1alpha1Client { + return &KyvernoV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *KyvernoV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go new file mode 100644 index 0000000000..b7fb02600e --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go @@ -0,0 +1,180 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + scheme "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PoliciesGetter has a method to return a PolicyInterface. +// A group's client should implement this interface. +type PoliciesGetter interface { + Policies() PolicyInterface +} + +// PolicyInterface has methods to work with Policy resources. +type PolicyInterface interface { + Create(*v1alpha1.Policy) (*v1alpha1.Policy, error) + Update(*v1alpha1.Policy) (*v1alpha1.Policy, error) + UpdateStatus(*v1alpha1.Policy) (*v1alpha1.Policy, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Policy, error) + List(opts v1.ListOptions) (*v1alpha1.PolicyList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) + PolicyExpansion +} + +// policies implements PolicyInterface +type policies struct { + client rest.Interface +} + +// newPolicies returns a Policies +func newPolicies(c *KyvernoV1alpha1Client) *policies { + return &policies{ + client: c.RESTClient(), + } +} + +// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. +func (c *policies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Get(). + Resource("policies"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Policies that match those selectors. +func (c *policies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PolicyList{} + err = c.client.Get(). + Resource("policies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested policies. +func (c *policies) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("policies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *policies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Post(). + Resource("policies"). + Body(policy). + Do(). + Into(result) + return +} + +// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *policies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Put(). + Resource("policies"). + Name(policy.Name). + Body(policy). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *policies) UpdateStatus(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Put(). + Resource("policies"). + Name(policy.Name). + SubResource("status"). + Body(policy). + Do(). + Into(result) + return +} + +// Delete takes name of the policy and deletes it. Returns an error if one occurs. +func (c *policies) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("policies"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *policies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("policies"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched policy. +func (c *policies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Patch(pt). + Resource("policies"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go new file mode 100644 index 0000000000..bc93b28281 --- /dev/null +++ b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go @@ -0,0 +1,180 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + scheme "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PolicyViolationsGetter has a method to return a PolicyViolationInterface. +// A group's client should implement this interface. +type PolicyViolationsGetter interface { + PolicyViolations() PolicyViolationInterface +} + +// PolicyViolationInterface has methods to work with PolicyViolation resources. +type PolicyViolationInterface interface { + Create(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) + Update(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) + UpdateStatus(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.PolicyViolation, error) + List(opts v1.ListOptions) (*v1alpha1.PolicyViolationList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) + PolicyViolationExpansion +} + +// policyViolations implements PolicyViolationInterface +type policyViolations struct { + client rest.Interface +} + +// newPolicyViolations returns a PolicyViolations +func newPolicyViolations(c *KyvernoV1alpha1Client) *policyViolations { + return &policyViolations{ + client: c.RESTClient(), + } +} + +// Get takes name of the policyViolation, and returns the corresponding policyViolation object, and an error if there is any. +func (c *policyViolations) Get(name string, options v1.GetOptions) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Get(). + Resource("policyviolations"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PolicyViolations that match those selectors. +func (c *policyViolations) List(opts v1.ListOptions) (result *v1alpha1.PolicyViolationList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PolicyViolationList{} + err = c.client.Get(). + Resource("policyviolations"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested policyViolations. +func (c *policyViolations) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("policyviolations"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a policyViolation and creates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *policyViolations) Create(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Post(). + Resource("policyviolations"). + Body(policyViolation). + Do(). + Into(result) + return +} + +// Update takes the representation of a policyViolation and updates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *policyViolations) Update(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Put(). + Resource("policyviolations"). + Name(policyViolation.Name). + Body(policyViolation). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *policyViolations) UpdateStatus(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Put(). + Resource("policyviolations"). + Name(policyViolation.Name). + SubResource("status"). + Body(policyViolation). + Do(). + Into(result) + return +} + +// Delete takes name of the policyViolation and deletes it. Returns an error if one occurs. +func (c *policyViolations) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("policyviolations"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *policyViolations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("policyviolations"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched policyViolation. +func (c *policyViolations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Patch(pt). + Resource("policyviolations"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/clientNew/informers/externalversions/kyverno/interface.go b/pkg/clientNew/informers/externalversions/kyverno/interface.go new file mode 100644 index 0000000000..ee3f322230 --- /dev/null +++ b/pkg/clientNew/informers/externalversions/kyverno/interface.go @@ -0,0 +1,46 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package kyverno + +import ( + internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go new file mode 100644 index 0000000000..ceead2b318 --- /dev/null +++ b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go @@ -0,0 +1,52 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Policies returns a PolicyInformer. + Policies() PolicyInformer + // PolicyViolations returns a PolicyViolationInformer. + PolicyViolations() PolicyViolationInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// Policies returns a PolicyInformer. +func (v *version) Policies() PolicyInformer { + return &policyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// PolicyViolations returns a PolicyViolationInformer. +func (v *version) PolicyViolations() PolicyViolationInformer { + return &policyViolationInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go new file mode 100644 index 0000000000..b59ee59a86 --- /dev/null +++ b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go @@ -0,0 +1,88 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PolicyInformer provides access to a shared informer and lister for +// Policies. +type PolicyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.PolicyLister +} + +type policyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewPolicyInformer constructs a new informer for Policy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPolicyInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredPolicyInformer constructs a new informer for Policy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().Policies().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().Policies().Watch(options) + }, + }, + &kyvernov1alpha1.Policy{}, + resyncPeriod, + indexers, + ) +} + +func (f *policyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *policyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&kyvernov1alpha1.Policy{}, f.defaultInformer) +} + +func (f *policyInformer) Lister() v1alpha1.PolicyLister { + return v1alpha1.NewPolicyLister(f.Informer().GetIndexer()) +} diff --git a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go new file mode 100644 index 0000000000..286b015fd9 --- /dev/null +++ b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go @@ -0,0 +1,88 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PolicyViolationInformer provides access to a shared informer and lister for +// PolicyViolations. +type PolicyViolationInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.PolicyViolationLister +} + +type policyViolationInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewPolicyViolationInformer constructs a new informer for PolicyViolation type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPolicyViolationInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPolicyViolationInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredPolicyViolationInformer constructs a new informer for PolicyViolation type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPolicyViolationInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().PolicyViolations().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().PolicyViolations().Watch(options) + }, + }, + &kyvernov1alpha1.PolicyViolation{}, + resyncPeriod, + indexers, + ) +} + +func (f *policyViolationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPolicyViolationInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *policyViolationInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&kyvernov1alpha1.PolicyViolation{}, f.defaultInformer) +} + +func (f *policyViolationInformer) Lister() v1alpha1.PolicyViolationLister { + return v1alpha1.NewPolicyViolationLister(f.Informer().GetIndexer()) +} diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go b/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go new file mode 100644 index 0000000000..00ee629f77 --- /dev/null +++ b/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go @@ -0,0 +1,73 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "fmt" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" +) + +// PolicyListerExpansion allows custom methods to be added to +// PolicyLister. +type PolicyListerExpansion interface { + GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) +} + +// PolicyViolationListerExpansion allows custom methods to be added to +// PolicyViolationLister. +type PolicyViolationListerExpansion interface{} + +func (pl *policyLister) GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) { + if len(pv.Labels) == 0 { + return nil, fmt.Errorf("no Policy found for PolicyViolation %v because it has no labels", pv.Name) + } + + pList, err := pl.List(labels.Everything()) + if err != nil { + return nil, err + } + + var policies []*kyverno.Policy + for _, p := range pList { + labelPolicy := fmt.Sprintf("policy=%s", p.Name) + labelSelector, err := metav1.ParseToLabelSelector(labelPolicy) + if err != nil { + return nil, fmt.Errorf("failed to build label selector for %s: %v", labelPolicy, err) + } + selector, err := metav1.LabelSelectorAsSelector(labelSelector) + if err != nil { + return nil, fmt.Errorf("invalid label selector: %v", err) + } + // If a policy with a nil or empty selector creeps in, it should match nothing, not everything. + if selector.Empty() || !selector.Matches(labels.Set(pv.Labels)) { + continue + } + policies = append(policies, p) + } + + if len(policies) == 0 { + return nil, fmt.Errorf("could not find Policy set for PolicyViolation %s with labels: %v", pv.Name, pv.Labels) + } + + return policies, nil + +} diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/policy.go b/pkg/clientNew/listers/kyverno/v1alpha1/policy.go new file mode 100644 index 0000000000..c981855c33 --- /dev/null +++ b/pkg/clientNew/listers/kyverno/v1alpha1/policy.go @@ -0,0 +1,65 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PolicyLister helps list Policies. +type PolicyLister interface { + // List lists all Policies in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) + // Get retrieves the Policy from the index for a given name. + Get(name string) (*v1alpha1.Policy, error) + PolicyListerExpansion +} + +// policyLister implements the PolicyLister interface. +type policyLister struct { + indexer cache.Indexer +} + +// NewPolicyLister returns a new PolicyLister. +func NewPolicyLister(indexer cache.Indexer) PolicyLister { + return &policyLister{indexer: indexer} +} + +// List lists all Policies in the indexer. +func (s *policyLister) List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Policy)) + }) + return ret, err +} + +// Get retrieves the Policy from the index for a given name. +func (s *policyLister) Get(name string) (*v1alpha1.Policy, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("policy"), name) + } + return obj.(*v1alpha1.Policy), nil +} diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go b/pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go new file mode 100644 index 0000000000..e93ec95228 --- /dev/null +++ b/pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go @@ -0,0 +1,65 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PolicyViolationLister helps list PolicyViolations. +type PolicyViolationLister interface { + // List lists all PolicyViolations in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) + // Get retrieves the PolicyViolation from the index for a given name. + Get(name string) (*v1alpha1.PolicyViolation, error) + PolicyViolationListerExpansion +} + +// policyViolationLister implements the PolicyViolationLister interface. +type policyViolationLister struct { + indexer cache.Indexer +} + +// NewPolicyViolationLister returns a new PolicyViolationLister. +func NewPolicyViolationLister(indexer cache.Indexer) PolicyViolationLister { + return &policyViolationLister{indexer: indexer} +} + +// List lists all PolicyViolations in the indexer. +func (s *policyViolationLister) List(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.PolicyViolation)) + }) + return ret, err +} + +// Get retrieves the PolicyViolation from the index for a given name. +func (s *policyViolationLister) Get(name string) (*v1alpha1.PolicyViolation, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("policyviolation"), name) + } + return obj.(*v1alpha1.PolicyViolation), nil +} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index a2f4e8240c..ad5f53360d 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -388,18 +388,29 @@ func (pc *PolicyController) syncPolicy(key string) error { return err } - // // Add messages - - if p.DeletionTimestamp != nil { - return pc.syncStatusOnly(p, pvList) - } - return nil + return pc.syncStatusOnly(p, pvList) } //TODO func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error { - // Sync Status based on Policy Violation - return nil + newStatus := calculateStatus(pvList) + if reflect.DeepEqual(newStatus, p.Status) { + // no update to status + return nil + } + // update status + newPolicy := p + newPolicy.Status = newStatus + _, err := pc.kyvernoclient.KyvernoV1alpha1().Policies().UpdateStatus(newPolicy) + return err +} + +func calculateStatus(pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus { + violationCount := len(pvList) + status := kyverno.PolicyStatus{ + Violations: violationCount, + } + return status } func (pc *PolicyController) getPolicyViolationsForPolicy(p *kyverno.Policy) ([]*kyverno.PolicyViolation, error) { // List all PolicyViolation to find those we own but that no longer match our @@ -725,17 +736,17 @@ func createLabelMapPatch(policy string) ([]byte, error) { // label is used here to lookup policyViolation and corresponding Policy func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.PolicyViolation) bool { updateLabel := func() bool { - glog.V(4).Infof("adding label 'policy:%s' to PolicyViolation %s", pv.Spec.PolicyName, pv.Name) + glog.V(4).Infof("adding label 'policy:%s' to PolicyViolation %s", pv.Spec.Policy, pv.Name) // add label based on the policy spec labels := pv.GetLabels() - if pv.Spec.PolicyName == "" { + if pv.Spec.Policy == "" { glog.Error("policy not defined for violation") // should be cleaned up return false } if labels == nil { // create a patch to generate the labels map with policy label - patch, err := createLabelMapPatch(pv.Spec.PolicyName) + patch, err := createLabelMapPatch(pv.Spec.Policy) if err != nil { glog.Errorf("unable to init label map. %v", err) return false @@ -748,7 +759,7 @@ func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.Pol return true } // JSON Patch to add exact label - labelPatch, err := createLabelPatch(pv.Spec.PolicyName) + labelPatch, err := createLabelPatch(pv.Spec.Policy) if err != nil { glog.Errorf("failed to generate patch to add label 'policy': %v", err) return false @@ -761,16 +772,16 @@ func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.Pol return true } - var policyName string + var policy string var ok bool // operate oncopy of resource curLabels := pv.GetLabels() - if policyName, ok = curLabels["policy"]; !ok { + if policy, ok = curLabels["policy"]; !ok { return updateLabel() } // TODO: would be benificial to add a check to verify if the policy in name and resource spec match - if policyName != pv.Spec.PolicyName { - glog.Errorf("label 'policy:%s' and spec.policyName %s dont match ", policyName, pv.Spec.PolicyName) + if policy != pv.Spec.Policy { + glog.Errorf("label 'policy:%s' and spec.policy %s dont match ", policy, pv.Spec.Policy) //TODO handle this case return updateLabel() } From 9ec85e5f74d206450b4d0c5fc9abb30ef2af3af1 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Thu, 8 Aug 2019 02:39:38 -0700 Subject: [PATCH 03/66] update crd --- definitions/install_debug.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 4028417ab3..9aadecd9a5 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -193,11 +193,11 @@ spec: properties: spec: required: - - policyName + - policy - resource - rules properties: - policyName: + policy: type: string resource: type: object From 733e77657d2efd2902113fc44b02c73a51d981d0 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Thu, 8 Aug 2019 13:37:32 -0700 Subject: [PATCH 04/66] update label selector --- .../listers/kyverno/v1alpha1/expansion_generated.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go b/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go index 00ee629f77..bfe398f1be 100644 --- a/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go +++ b/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go @@ -48,12 +48,14 @@ func (pl *policyLister) GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) var policies []*kyverno.Policy for _, p := range pList { - labelPolicy := fmt.Sprintf("policy=%s", p.Name) - labelSelector, err := metav1.ParseToLabelSelector(labelPolicy) + policyLabelmap := map[string]string{"policy": p.Name} + + ls := &metav1.LabelSelector{} + err = metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&policyLabelmap, ls, nil) if err != nil { - return nil, fmt.Errorf("failed to build label selector for %s: %v", labelPolicy, err) + return nil, fmt.Errorf("failed to generate label sector of Policy name %s: %v", p.Name, err) } - selector, err := metav1.LabelSelectorAsSelector(labelSelector) + selector, err := metav1.LabelSelectorAsSelector(ls) if err != nil { return nil, fmt.Errorf("invalid label selector: %v", err) } From c8179a182d712ff8e1f323d99ab19bc3cedc2ae2 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Thu, 8 Aug 2019 13:59:50 -0700 Subject: [PATCH 05/66] controller for policyviolation --- pkg/api/kyverno/v1alpha1/types.go | 13 +- pkg/policy/controller.go | 20 +-- pkg/policyviolation/controller.go | 261 ++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 11 deletions(-) create mode 100644 pkg/policyviolation/controller.go diff --git a/pkg/api/kyverno/v1alpha1/types.go b/pkg/api/kyverno/v1alpha1/types.go index ffad06ca80..54a1f7f093 100644 --- a/pkg/api/kyverno/v1alpha1/types.go +++ b/pkg/api/kyverno/v1alpha1/types.go @@ -109,8 +109,8 @@ type PolicyList struct { type PolicyViolation struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec PolicyViolationSpec `json:"spec"` - Status string `json:"status"` + Spec PolicyViolationSpec `json:"spec"` + Status PolicyViolationStatus `json:"status"` } // PolicyViolationSpec describes policy behavior by its rules @@ -127,12 +127,21 @@ type ResourceSpec struct { Name string `json:"name"` } +// ViolatedRule stores the information regarding the rule type ViolatedRule struct { Name string `json:"name"` Type string `json:"type"` Message string `json:"message"` } +//PolicyViolationStatus provides information regarding policyviolation status +// status: +// LastUpdateTime : the time the polivy violation was updated +type PolicyViolationStatus struct { + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + //TODO: having user information regarding the owner of resource can be helpful +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // PolicyViolationList is a list of Policy Violation diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index ad5f53360d..4022a8a9be 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -30,7 +30,7 @@ import ( ) const ( - // maxRetries is the number of times a Polict will be retried before it is dropped out of the queue. + // maxRetries is the number of times a Policy will be retried before it is dropped out of the queue. // With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the times // a deployment is going to be requeued: // @@ -44,12 +44,12 @@ var controllerKind = kyverno.SchemeGroupVersion.WithKind("Policy") // in the system with the corresponding policy violations type PolicyController struct { client *client.Client - kyvernoclient *kyvernoclient.Clientset + kyvernoClient *kyvernoclient.Clientset eventRecorder record.EventRecorder syncHandler func(pKey string) error enqueuePolicy func(policy *kyverno.Policy) - //pvControl is used for adoptin/releasing replica sets + //pvControl is used for adoptin/releasing policy violation pvControl PVControlInterface // Policys that need to be synced queue workqueue.RateLimitingInterface @@ -76,7 +76,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. pc := PolicyController{ client: client, - kyvernoclient: kyvernoClient, + kyvernoClient: kyvernoClient, eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policy_controller"}), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policy"), } @@ -316,7 +316,7 @@ func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) { glog.Info("Starting policy controller") defer glog.Info("Shutting down policy controller") - if !cache.WaitForCacheSync(stopCh, pc.pListerSynced) { + if !cache.WaitForCacheSync(stopCh, pc.pListerSynced, pc.pvListerSynced) { return } for i := 0; i < workers; i++ { @@ -358,7 +358,7 @@ func (pc *PolicyController) handleErr(err error, key interface{}) { } utilruntime.HandleError(err) - glog.V(2).Infof("Dropping deployment %q out of the queue: %v", key, err) + glog.V(2).Infof("Dropping policy %q out of the queue: %v", key, err) pc.queue.Forget(key) } @@ -391,7 +391,9 @@ func (pc *PolicyController) syncPolicy(key string) error { return pc.syncStatusOnly(p, pvList) } -//TODO +//syncStatusOnly updates the policy status subresource +// status: +// - violations : (count of the resources that violate this policy ) func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error { newStatus := calculateStatus(pvList) if reflect.DeepEqual(newStatus, p.Status) { @@ -401,7 +403,7 @@ func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno. // update status newPolicy := p newPolicy.Status = newStatus - _, err := pc.kyvernoclient.KyvernoV1alpha1().Policies().UpdateStatus(newPolicy) + _, err := pc.kyvernoClient.KyvernoV1alpha1().Policies().UpdateStatus(newPolicy) return err } @@ -436,7 +438,7 @@ func (pc *PolicyController) getPolicyViolationsForPolicy(p *kyverno.Policy) ([]* } canAdoptFunc := RecheckDeletionTimestamp(func() (metav1.Object, error) { - fresh, err := pc.kyvernoclient.KyvernoV1alpha1().Policies().Get(p.Name, metav1.GetOptions{}) + fresh, err := pc.kyvernoClient.KyvernoV1alpha1().Policies().Get(p.Name, metav1.GetOptions{}) if err != nil { return nil, err } diff --git a/pkg/policyviolation/controller.go b/pkg/policyviolation/controller.go new file mode 100644 index 0000000000..86922390c9 --- /dev/null +++ b/pkg/policyviolation/controller.go @@ -0,0 +1,261 @@ +package policyviolation + +import ( + "fmt" + "reflect" + "time" + + "github.com/golang/glog" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" + informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + client "github.com/nirmata/kyverno/pkg/dclient" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" + + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" +) + +const ( + // maxRetries is the number of times a PolicyViolation will be retried before it is dropped out of the queue. + // With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the times + // a deployment is going to be requeued: + // + // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s + maxRetries = 15 +) + +var controllerKind = kyverno.SchemeGroupVersion.WithKind("PolicyViolation") + +type PolicyViolationController struct { + kyvernoClient *kyvernoclient.Clientset + eventRecorder record.EventRecorder + syncHandler func(pKey string) error + enqueuePolicyViolation func(policy *kyverno.PolicyViolation) + // Policys that need to be synced + queue workqueue.RateLimitingInterface + // pvLister can list/get policy violation from the shared informer's store + pvLister lister.PolicyViolationLister + // pLister can list/get policy from the shared informer's store + pLister lister.PolicyLister + // pListerSynced returns true if the Policy store has been synced at least once + pListerSynced cache.InformerSynced + // pvListerSynced retrns true if the Policy store has been synced at least once + pvListerSynced cache.InformerSynced + //pvControl is used for updating status/cleanup policy violation + pvControl PVControlInterface +} + +//NewPolicyViolationController creates a new NewPolicyViolationController +func NewPolicyViolationController(client *client.Client, kyvernoClient *kyvernoclient.Clientset, pInformer informer.PolicyInformer, pvInformer informer.PolicyViolationInformer) (*PolicyViolationController, error) { + // Event broad caster + eventBroadcaster := record.NewBroadcaster() + eventBroadcaster.StartLogging(glog.Infof) + eventInterface, err := client.GetEventsInterface() + if err != nil { + return nil, err + } + eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: eventInterface}) + + pvc := PolicyViolationController{ + kyvernoClient: kyvernoClient, + + eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policyviolation_controller"}), + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policyviolation"), + } + pvc.pvControl = RealPVControl{Client: kyvernoClient, Recorder: pvc.eventRecorder} + pvInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: pvc.addPolicyViolation, + UpdateFunc: pvc.updatePolicyViolation, + DeleteFunc: pvc.deletePolicyViolation, + }) + + pvc.enqueuePolicyViolation = pvc.enqueue + pvc.syncHandler = pvc.syncPolicyViolation + + pvc.pLister = pInformer.Lister() + pvc.pvLister = pvInformer.Lister() + pvc.pListerSynced = pInformer.Informer().HasSynced + pvc.pvListerSynced = pInformer.Informer().HasSynced + + return &pvc, nil +} + +func (pvc *PolicyViolationController) addPolicyViolation(obj interface{}) { + pv := obj.(*kyverno.PolicyViolation) + glog.V(4).Infof("Adding PolicyViolation %s", pv.Name) + pvc.enqueuePolicyViolation(pv) +} + +func (pvc *PolicyViolationController) updatePolicyViolation(old, cur interface{}) { + oldPv := old.(*kyverno.PolicyViolation) + curPv := cur.(*kyverno.PolicyViolation) + glog.V(4).Infof("Updating Policy Violation %s", oldPv.Name) + if err := pvc.syncLastUpdateTimeStatus(curPv, oldPv); err != nil { + glog.Errorf("Failed to update lastUpdateTime in PolicyViolation %s status: %v", curPv.Name, err) + } + pvc.enqueuePolicyViolation(curPv) +} + +func (pvc *PolicyViolationController) deletePolicyViolation(obj interface{}) { + pv, ok := obj.(*kyverno.PolicyViolation) + if !ok { + tombstone, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + glog.Info(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) + return + } + pv, ok = tombstone.Obj.(*kyverno.PolicyViolation) + if !ok { + glog.Info(fmt.Errorf("Tombstone contained object that is not a PolicyViolation %#v", obj)) + return + } + } + glog.V(4).Infof("Deleting PolicyViolation %s", pv.Name) + pvc.enqueuePolicyViolation(pv) +} + +func (pvc *PolicyViolationController) enqueue(policyViolation *kyverno.PolicyViolation) { + key, err := cache.MetaNamespaceKeyFunc(policyViolation) + if err != nil { + glog.Error(err) + return + } + pvc.queue.Add(key) +} + +// Run begins watching and syncing. +func (pvc *PolicyViolationController) Run(workers int, stopCh <-chan struct{}) { + + defer utilruntime.HandleCrash() + defer pvc.queue.ShutDown() + + glog.Info("Starting policyviolation controller") + defer glog.Info("Shutting down policyviolation controller") + + if !cache.WaitForCacheSync(stopCh, pvc.pListerSynced, pvc.pvListerSynced) { + return + } + for i := 0; i < workers; i++ { + go wait.Until(pvc.worker, time.Second, stopCh) + } + <-stopCh +} + +// worker runs a worker thread that just dequeues items, processes them, and marks them done. +// It enforces that the syncHandler is never invoked concurrently with the same key. +func (pvc *PolicyViolationController) worker() { + for pvc.processNextWorkItem() { + } +} + +func (pvc *PolicyViolationController) processNextWorkItem() bool { + key, quit := pvc.queue.Get() + if quit { + return false + } + defer pvc.queue.Done(key) + + err := pvc.syncHandler(key.(string)) + pvc.handleErr(err, key) + + return true +} + +func (pvc *PolicyViolationController) handleErr(err error, key interface{}) { + if err == nil { + pvc.queue.Forget(key) + return + } + + if pvc.queue.NumRequeues(key) < maxRetries { + glog.V(2).Infof("Error syncing PolicyViolation %v: %v", key, err) + pvc.queue.AddRateLimited(key) + return + } + + utilruntime.HandleError(err) + glog.V(2).Infof("Dropping policyviolation %q out of the queue: %v", key, err) + pvc.queue.Forget(key) +} + +func (pvc *PolicyViolationController) syncPolicyViolation(key string) error { + startTime := time.Now() + glog.V(4).Infof("Started syncing policy violation %q (%v)", key, startTime) + defer func() { + glog.V(4).Infof("Finished syncing policy violation %q (%v)", key, time.Since(startTime)) + }() + policyViolation, err := pvc.pvLister.Get(key) + if errors.IsNotFound(err) { + glog.V(2).Infof("PolicyViolation %v has been deleted", key) + return nil + } + + if err != nil { + return err + } + + // Deep-copy otherwise we are mutating our cache. + // TODO: Deep-copy only when needed. + pv := policyViolation.DeepCopy() + // TODO: Update Status to update ObserverdGeneration + + return pvc.syncStatusOnly(pv) +} + +//syncStatusOnly updates the policyviolation status subresource +// status: +func (pvc *PolicyViolationController) syncStatusOnly(curPv *kyverno.PolicyViolation) error { + // newStatus := calculateStatus(pv) + return nil +} + +//TODO: think this through again +//syncLastUpdateTimeStatus updates the policyviolation lastUpdateTime if anything in ViolationSpec changed +// - lastUpdateTime : (time stamp when the policy violation changed) +func (pvc *PolicyViolationController) syncLastUpdateTimeStatus(curPv *kyverno.PolicyViolation, oldPv *kyverno.PolicyViolation) error { + // check if there is any change in policy violation information + if !updated(curPv, oldPv) { + return nil + } + // update the lastUpdateTime + newPolicyViolation := curPv + newPolicyViolation.Status = kyverno.PolicyViolationStatus{LastUpdateTime: metav1.Now()} + + return pvc.pvControl.UpdateStatusPolicyViolation(newPolicyViolation) +} + +func updated(curPv *kyverno.PolicyViolation, oldPv *kyverno.PolicyViolation) bool { + return !reflect.DeepEqual(curPv.Spec, oldPv.Spec) + //TODO check if owner reference changed, then should we update the lastUpdateTime as well ? +} + +type PVControlInterface interface { + UpdateStatusPolicyViolation(newPv *kyverno.PolicyViolation) error + RemovePolicyViolation(name string) error +} + +// RealPVControl is the default implementation of PVControlInterface. +type RealPVControl struct { + Client kyvernoclient.Interface + Recorder record.EventRecorder +} + +//UpdateStatusPolicyViolation updates the status for policy violation +func (r RealPVControl) UpdateStatusPolicyViolation(newPv *kyverno.PolicyViolation) error { + _, err := r.Client.KyvernoV1alpha1().PolicyViolations().UpdateStatus(newPv) + return err +} + +//RemovePolicyViolation removes the policy violation +func (r RealPVControl) RemovePolicyViolation(name string) error { + return nil +} From 188c3c9ef2646aca2182f682d4bc4258d5c304c9 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Thu, 8 Aug 2019 15:30:44 -0700 Subject: [PATCH 06/66] test yamls --- examples/cli/pv1.yaml | 21 +++++++++++++++++++-- examples/test/p1.yaml | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 examples/test/p1.yaml diff --git a/examples/cli/pv1.yaml b/examples/cli/pv1.yaml index 5a36dee855..c6ea83bcc0 100644 --- a/examples/cli/pv1.yaml +++ b/examples/cli/pv1.yaml @@ -3,7 +3,7 @@ kind: PolicyViolation metadata: name: pv1 spec: - policyName: check-cpu-memory + policy: check-cpu-memory resource: kind: Pod namespace: "" @@ -12,4 +12,21 @@ spec: - name: r1 type: Mutation status: Failed - message: test mesaage for rule failure \ No newline at end of file + message: test mesaage for rule failure +--- +apiVersion: kyverno.io/v1alpha1 +kind: PolicyViolation +metadata: + name: pv2 +spec: + policy: check-cpu-memory + resource: + kind: Pod + namespace: "" + name: pod1 + rules: + - name: r1 + type: Mutation + status: Failed + message: test mesaage for rule failure +--- \ No newline at end of file diff --git a/examples/test/p1.yaml b/examples/test/p1.yaml new file mode 100644 index 0000000000..76c0b73197 --- /dev/null +++ b/examples/test/p1.yaml @@ -0,0 +1,28 @@ +apiVersion: kyverno.io/v1alpha1 +kind: Policy +metadata: + name: check-resources +spec: + validationFailureAction: "audit" + rules: + - name: check-pod-resources + match: + resources: + kinds: + - Pod + validate: + message: "CPU and memory resource requests and limits are required" + pattern: + spec: + containers: + # 'name: *' selects all containers in the pod + - name: "*" + resources: + requests: + # '?' requires 1 alphanumeric character and '*' means that there can be 0 or more characters. + # Using them together e.g. '?*' requires at least one character. + memory: "?*" + cpu: "?*" + limits: + memory: "?*" + cpu: "?*" \ No newline at end of file From 9569c0772a199ddc1aad13d66c6bcc7b76405c40 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Thu, 8 Aug 2019 15:36:19 -0700 Subject: [PATCH 07/66] init policy violation container --- main.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.go b/main.go index 92617adbeb..749b974e1e 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( client "github.com/nirmata/kyverno/pkg/dclient" event "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/policy" + "github.com/nirmata/kyverno/pkg/policyviolation" "github.com/nirmata/kyverno/pkg/sharedinformer" "github.com/nirmata/kyverno/pkg/utils" "github.com/nirmata/kyverno/pkg/violation" @@ -52,6 +53,10 @@ func main() { if err != nil { glog.Fatalf("error creating policy controller: %v\n", err) } + pvc, err := policyviolation.NewPolicyViolationController(client, pclient, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) + if err != nil { + glog.Fatalf("error creating policy violation controller: %v\n", err) + } //------------------------------------- policyInformerFactory, err := sharedinformer.NewSharedInformerFactory(clientConfig) if err != nil { @@ -93,6 +98,7 @@ func main() { //-------- pInformer.Start(stopCh) go pc.Run(1, stopCh) + go pvc.Run(1, stopCh) //TODO add WG for the go routine? //-------- policyInformerFactory.Run(stopCh) From a813b84c24de02f40cf8b9c33ffbe56482c4616e Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Thu, 8 Aug 2019 15:36:37 -0700 Subject: [PATCH 08/66] rebase with master v1 --- pkg/annotations/annotations.go | 63 ++++++++++++++++++++++++++++++++++ pkg/annotations/controller.go | 2 +- pkg/info/info.go | 2 ++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go index 21f4e71747..46725b8cc2 100644 --- a/pkg/annotations/annotations.go +++ b/pkg/annotations/annotations.go @@ -78,6 +78,16 @@ func (p *Policy) updatePolicy(obj *Policy, ruleType pinfo.RuleType) bool { updates := false // Check Mutation rules switch ruleType { + case pinfo.All: + if p.compareMutationRules(obj.MutationRules) { + updates = true + } + if p.compareValidationRules(obj.ValidationRules) { + updates = true + } + if p.compareGenerationRules(obj.GenerationRules) { + updates = true + } case pinfo.Mutation: if p.compareMutationRules(obj.MutationRules) { updates = true @@ -214,6 +224,59 @@ func ParseAnnotationsFromObject(bytes []byte) map[string]string { return ann } +func PatchAnnotations(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) ([]byte, error) { + if ruleType != pinfo.All && !pi.ContainsRuleType(ruleType) { + // the rule was not proceesed in the current policy application + return nil, nil + } + // transform the PolicyInfo to anotation struct + policyObj := newAnnotationForPolicy(pi) + if ann == nil { + ann = make(map[string]string, 0) + policyByte, err := json.Marshal(policyObj) + if err != nil { + return nil, err + } + // create a json patch to add annotation object + ann[BuildKeyString(pi.Name)] = string(policyByte) + // patch adds the annotation map with the policy information + jsonPatch, err := createAddJSONPatchMap(ann) + return jsonPatch, err + } + // if the annotations map already exists then we need to update it by adding a patch to the field inside the annotation + cPolicy, ok := ann[BuildKey(pi.Name)] + if !ok { + // annotations does not contain the policy + policyByte, err := json.Marshal(policyObj) + if err != nil { + return nil, err + } + jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(policyByte)) + return jsonPatch, err + } + // an annotaion exists for the policy, we need to update the information if anything has changed + cPolicyObj := Policy{} + err := json.Unmarshal([]byte(cPolicy), &cPolicyObj) + if err != nil { + // error while unmarshallign the content + return nil, err + } + // update policy information inside the annotation + // 1> policy status + // 2> rule (name, status,changes,type) + update := cPolicyObj.updatePolicy(policyObj, ruleType) + if !update { + // there is not update, so we dont + return nil, nil + } + policyByte, err := json.Marshal(cPolicyObj) + if err != nil { + return nil, err + } + jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(policyByte)) + return jsonPatch, err +} + //AddPolicyJSONPatch generate JSON Patch to add policy informatino JSON patch func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) (map[string]string, []byte, error) { if !pi.ContainsRuleType(ruleType) { diff --git a/pkg/annotations/controller.go b/pkg/annotations/controller.go index 06cb06871a..e251719492 100644 --- a/pkg/annotations/controller.go +++ b/pkg/annotations/controller.go @@ -46,7 +46,7 @@ func (c *controller) Run(stopCh <-chan struct{}) { } func (c *controller) Stop() { - defer c.queue.ShutDown() + c.queue.ShutDown() glog.Info("Shutting down annotation controller workers") } diff --git a/pkg/info/info.go b/pkg/info/info.go index fbde19e160..8d61b764f0 100644 --- a/pkg/info/info.go +++ b/pkg/info/info.go @@ -99,6 +99,7 @@ const ( Mutation RuleType = iota Validation Generation + All ) func (ri RuleType) String() string { @@ -106,6 +107,7 @@ func (ri RuleType) String() string { "Mutation", "Validation", "Generation", + "All", }[ri] } From a30ad6bab27ead414bd28447c6a3c59304bc5284 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Fri, 9 Aug 2019 11:08:02 -0700 Subject: [PATCH 09/66] cleanup --- main.go | 5 ++--- pkg/engine/mutation.go | 31 ++++++++++++++++++++++--------- pkg/engine/overlay.go | 2 +- pkg/engine/patches.go | 2 +- pkg/engine/patches_test.go | 18 +++++++++--------- pkg/info/info.go | 2 +- pkg/policyviolation/helpers.go | 17 +++++++++++++++++ 7 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 pkg/policyviolation/helpers.go diff --git a/main.go b/main.go index 749b974e1e..ee3a7745d0 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,6 @@ import ( "github.com/nirmata/kyverno/pkg/policyviolation" "github.com/nirmata/kyverno/pkg/sharedinformer" "github.com/nirmata/kyverno/pkg/utils" - "github.com/nirmata/kyverno/pkg/violation" "github.com/nirmata/kyverno/pkg/webhooks" "k8s.io/sample-controller/pkg/signals" ) @@ -64,7 +63,7 @@ func main() { } kubeInformer := utils.NewKubeInformerFactory(clientConfig) eventController := event.NewEventController(client, policyInformerFactory) - violationBuilder := violation.NewPolicyViolationBuilder(client, policyInformerFactory, eventController) + // violationBuilder := violation.NewPolicyViolationBuilder(client, policyInformerFactory, eventController) annotationsController := annotations.NewAnnotationControler(client) // policyController := controller.NewPolicyController( // client, @@ -79,7 +78,7 @@ func main() { if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, violationBuilder, annotationsController, filterK8Resources) + server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, nil, annotationsController, filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) } diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index f1d551e1e6..8f1112a28c 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -5,10 +5,13 @@ import ( kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" "github.com/nirmata/kyverno/pkg/info" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + // "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Mutate performs mutation. Overlay first and then mutation patches func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []*info.RuleInfo) { + // + var allPatches [][]byte patchedDocument := rawResource ris := []*info.RuleInfo{} @@ -17,16 +20,22 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio if rule.Mutation == nil { continue } - ri := info.NewRuleInfo(rule.Name, info.Mutation) + // check if the resource satisfies the filter conditions defined in the rule + //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that + // dont statisfy a policy rule resource description ok := ResourceMeetsDescription(rawResource, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, gvk) if !ok { - glog.V(3).Infof("Not applicable on specified resource kind%s", gvk.Kind) + name := ParseNameFromObject(rawResource) + namespace := ParseNamespaceFromObject(rawResource) + glog.V(3).Infof("resource %s/%s does not satisfy the resource description for the rule ", namespace, name) continue } + ri := info.NewRuleInfo(rule.Name, info.Mutation) + // Process Overlay if rule.Mutation.Overlay != nil { - overlayPatches, err := ProcessOverlay(rule, rawResource, gvk) + overlayPatches, err := processOverlay(rule, rawResource, gvk) if err == nil { if len(overlayPatches) == 0 { // if array elements dont match then we skip(nil patch, no error) @@ -34,28 +43,32 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio // policy is not applicable continue } - ri.Addf("Rule %s: Overlay succesfully applied.", rule.Name) - // merge the json patches + glog.V(4).Infof("overlay applied succesfully on resource") + ri.Add("Overlay succesfully applied") patch := JoinPatches(overlayPatches) + allPatches = append(allPatches, overlayPatches...) + // update rule information // strip slashes from string ri.Changes = string(patch) - allPatches = append(allPatches, overlayPatches...) } else { + glog.V(4).Infof("failed to apply overlay: %v", err) ri.Fail() - ri.Addf("overlay application has failed, err %v.", err) + ri.Addf("failed to apply overlay: %v", err) } } // Process Patches if len(rule.Mutation.Patches) != 0 { - rulePatches, errs := ProcessPatches(rule, patchedDocument) + rulePatches, errs := processPatches(rule, patchedDocument) if len(errs) > 0 { ri.Fail() for _, err := range errs { + glog.V(4).Infof("failed to apply patches: %v", err) ri.Addf("patches application has failed, err %v.", err) } } else { - ri.Addf("Rule %s: Patches succesfully applied.", rule.Name) + glog.V(4).Infof("patches applied succesfully on resource") + ri.Addf("Patches succesfully applied.") allPatches = append(allPatches, rulePatches...) } } diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index 7fea8f4cec..fb4c556471 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -17,7 +17,7 @@ import ( // ProcessOverlay handles validating admission request // Checks the target resources for rules defined in the policy -func ProcessOverlay(rule kubepolicy.Rule, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, error) { +func processOverlay(rule kubepolicy.Rule, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, error) { var resource interface{} if err := json.Unmarshal(rawResource, &resource); err != nil { diff --git a/pkg/engine/patches.go b/pkg/engine/patches.go index 7b7a19bd8e..8f7d69c76e 100644 --- a/pkg/engine/patches.go +++ b/pkg/engine/patches.go @@ -12,7 +12,7 @@ import ( // ProcessPatches Returns array from separate patches that can be applied to the document // Returns error ONLY in case when creation of resource should be denied. -func ProcessPatches(rule kubepolicy.Rule, resource []byte) (allPatches [][]byte, errs []error) { +func processPatches(rule kubepolicy.Rule, resource []byte) (allPatches [][]byte, errs []error) { if len(resource) == 0 { errs = append(errs, errors.New("Source document for patching is empty")) return nil, errs diff --git a/pkg/engine/patches_test.go b/pkg/engine/patches_test.go index 0f6bbcfe1b..2c58a15566 100644 --- a/pkg/engine/patches_test.go +++ b/pkg/engine/patches_test.go @@ -35,7 +35,7 @@ const endpointsDocument string = `{ func TestProcessPatches_EmptyPatches(t *testing.T) { var emptyRule = types.Rule{} - patches, err := ProcessPatches(emptyRule, []byte(endpointsDocument)) + patches, err := processPatches(emptyRule, []byte(endpointsDocument)) assert.Check(t, len(err) == 1) assert.Assert(t, len(patches) == 0) } @@ -64,14 +64,14 @@ func makeRuleWithPatches(patches []types.Patch) types.Rule { func TestProcessPatches_EmptyDocument(t *testing.T) { rule := makeRuleWithPatch(makeAddIsMutatedLabelPatch()) - patchesBytes, err := ProcessPatches(rule, nil) + patchesBytes, err := processPatches(rule, nil) assert.Assert(t, err != nil) assert.Assert(t, len(patchesBytes) == 0) } func TestProcessPatches_AllEmpty(t *testing.T) { emptyRule := types.Rule{} - patchesBytes, err := ProcessPatches(emptyRule, nil) + patchesBytes, err := processPatches(emptyRule, nil) assert.Check(t, len(err) == 1) assert.Assert(t, len(patchesBytes) == 0) } @@ -80,7 +80,7 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) { patch := makeAddIsMutatedLabelPatch() patch.Path = "/metadata/additional/is-mutated" rule := makeRuleWithPatch(patch) - patchesBytes, err := ProcessPatches(rule, []byte(endpointsDocument)) + patchesBytes, err := processPatches(rule, []byte(endpointsDocument)) assert.Check(t, len(err) == 1) assert.Assert(t, len(patchesBytes) == 0) } @@ -88,7 +88,7 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) { func TestProcessPatches_RemovePathDoesntExist(t *testing.T) { patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"} rule := makeRuleWithPatch(patch) - patchesBytes, err := ProcessPatches(rule, []byte(endpointsDocument)) + patchesBytes, err := processPatches(rule, []byte(endpointsDocument)) assert.Check(t, len(err) == 0) assert.Assert(t, len(patchesBytes) == 0) } @@ -97,7 +97,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_EmptyResult(t *testing.T) { patch1 := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"} patch2 := types.Patch{Path: "/spec/labels/label3", Operation: "add", Value: "label3Value"} rule := makeRuleWithPatches([]types.Patch{patch1, patch2}) - patchesBytes, err := ProcessPatches(rule, []byte(endpointsDocument)) + patchesBytes, err := processPatches(rule, []byte(endpointsDocument)) assert.Check(t, len(err) == 1) assert.Assert(t, len(patchesBytes) == 0) } @@ -107,7 +107,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul patch2 := types.Patch{Path: "/spec/labels/label2", Operation: "remove", Value: "label2Value"} patch3 := types.Patch{Path: "/metadata/labels/label3", Operation: "add", Value: "label3Value"} rule := makeRuleWithPatches([]types.Patch{patch1, patch2, patch3}) - patchesBytes, err := ProcessPatches(rule, []byte(endpointsDocument)) + patchesBytes, err := processPatches(rule, []byte(endpointsDocument)) assert.Check(t, len(err) == 0) assert.Assert(t, len(patchesBytes) != 0) assertEqStringAndData(t, `{"path":"/metadata/labels/label3","op":"add","value":"label3Value"}`, patchesBytes[0]) @@ -116,7 +116,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul func TestProcessPatches_RemovePathDoesntExist_EmptyResult(t *testing.T) { patch := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"} rule := makeRuleWithPatch(patch) - patchesBytes, err := ProcessPatches(rule, []byte(endpointsDocument)) + patchesBytes, err := processPatches(rule, []byte(endpointsDocument)) assert.Check(t, len(err) == 0) assert.Assert(t, len(patchesBytes) == 0) } @@ -125,7 +125,7 @@ func TestProcessPatches_RemovePathDoesntExist_NotEmptyResult(t *testing.T) { patch1 := types.Patch{Path: "/metadata/labels/is-mutated", Operation: "remove"} patch2 := types.Patch{Path: "/metadata/labels/label2", Operation: "add", Value: "label2Value"} rule := makeRuleWithPatches([]types.Patch{patch1, patch2}) - patchesBytes, err := ProcessPatches(rule, []byte(endpointsDocument)) + patchesBytes, err := processPatches(rule, []byte(endpointsDocument)) assert.Check(t, len(err) == 0) assert.Assert(t, len(patchesBytes) == 1) assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, patchesBytes[0]) diff --git a/pkg/info/info.go b/pkg/info/info.go index 8d61b764f0..05777a4cdc 100644 --- a/pkg/info/info.go +++ b/pkg/info/info.go @@ -114,9 +114,9 @@ func (ri RuleType) String() string { //RuleInfo defines rule struct type RuleInfo struct { Name string + RuleType RuleType Msgs []string Changes string // this will store the mutation patch being applied by the rule - RuleType RuleType success bool } diff --git a/pkg/policyviolation/helpers.go b/pkg/policyviolation/helpers.go new file mode 100644 index 0000000000..69d8d85ae4 --- /dev/null +++ b/pkg/policyviolation/helpers.go @@ -0,0 +1,17 @@ +package policyviolation + +import ( + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" +) + +//BuildPolicyViolation returns an value of type PolicyViolation +func BuildPolicyViolation(policy string, resource kyverno.ResourceSpec, fRules []kyverno.ViolatedRule) kyverno.PolicyViolation { + pv := kyverno.PolicyViolation{ + Spec: kyverno.PolicyViolationSpec{ + Policy: policy, + ResourceSpec: resource, + ViolatedRules: fRules, + }, + } + return pv +} From 373d9a45ad62efd0c7fe24327843e4859cbef818 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Fri, 9 Aug 2019 12:59:37 -0700 Subject: [PATCH 10/66] clean up mutation --- pkg/engine/mutation.go | 62 ++++++++++++++++-------------- pkg/engine/overlay.go | 18 ++++++--- pkg/engine/utils.go | 70 ++++++++++++++++++++++++++++++++++ pkg/engine/validation.go | 41 +++++++++++++------- pkg/webhooks/mutation.go | 81 +++++++++++++++++----------------------- pkg/webhooks/utils.go | 10 +++++ 6 files changed, 188 insertions(+), 94 deletions(-) diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 8f1112a28c..1de615622c 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -5,16 +5,19 @@ import ( kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" "github.com/nirmata/kyverno/pkg/info" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - // "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Mutate performs mutation. Overlay first and then mutation patches +//TODO: check if gvk needs to be passed or can be set in resource func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []*info.RuleInfo) { - // - - var allPatches [][]byte - patchedDocument := rawResource - ris := []*info.RuleInfo{} + //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information + //TODO: pass unstructured instead of rawResource ? + resource, err := convertToUnstructured(rawResource) + if err != nil { + glog.Errorf("unable to convert raw resource to unstructured: %v", err) + } + var patches [][]byte + var ruleInfos []*info.RuleInfo for _, rule := range policy.Spec.Rules { if rule.Mutation == nil { @@ -24,56 +27,57 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio // check if the resource satisfies the filter conditions defined in the rule //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that // dont statisfy a policy rule resource description - ok := ResourceMeetsDescription(rawResource, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, gvk) + ok := MatchesResourceDescription(resource, rule, gvk) if !ok { - name := ParseNameFromObject(rawResource) - namespace := ParseNamespaceFromObject(rawResource) - glog.V(3).Infof("resource %s/%s does not satisfy the resource description for the rule ", namespace, name) + glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName()) continue } - ri := info.NewRuleInfo(rule.Name, info.Mutation) + + ruleInfo := info.NewRuleInfo(rule.Name, info.Mutation) // Process Overlay if rule.Mutation.Overlay != nil { - overlayPatches, err := processOverlay(rule, rawResource, gvk) + oPatches, err := processOverlay(resource, rule, rawResource) if err == nil { - if len(overlayPatches) == 0 { + if len(oPatches) == 0 { // if array elements dont match then we skip(nil patch, no error) // or if acnohor is defined and doenst match // policy is not applicable + glog.V(4).Info("overlay does not match, so skipping applying rule") continue } - glog.V(4).Infof("overlay applied succesfully on resource") - ri.Add("Overlay succesfully applied") - patch := JoinPatches(overlayPatches) - allPatches = append(allPatches, overlayPatches...) + + glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) + ruleInfo.Add("Overlay succesfully applied") + // update rule information // strip slashes from string - ri.Changes = string(patch) + patch := JoinPatches(oPatches) + ruleInfo.Changes = string(patch) + patches = append(patches, oPatches...) } else { glog.V(4).Infof("failed to apply overlay: %v", err) - ri.Fail() - ri.Addf("failed to apply overlay: %v", err) + ruleInfo.Fail() + ruleInfo.Addf("failed to apply overlay: %v", err) } } // Process Patches if len(rule.Mutation.Patches) != 0 { - rulePatches, errs := processPatches(rule, patchedDocument) + jsonPatches, errs := processPatches(rule, rawResource) if len(errs) > 0 { - ri.Fail() + ruleInfo.Fail() for _, err := range errs { glog.V(4).Infof("failed to apply patches: %v", err) - ri.Addf("patches application has failed, err %v.", err) + ruleInfo.Addf("patches application has failed, err %v.", err) } } else { - glog.V(4).Infof("patches applied succesfully on resource") - ri.Addf("Patches succesfully applied.") - allPatches = append(allPatches, rulePatches...) + glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) + ruleInfo.Addf("Patches succesfully applied.") + patches = append(patches, jsonPatches...) } } - ris = append(ris, ri) + ruleInfos = append(ruleInfos, ruleInfo) } - - return allPatches, ris + return patches, ruleInfos } diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index fb4c556471..62af73e867 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -12,22 +12,30 @@ import ( jsonpatch "github.com/evanphx/json-patch" kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // ProcessOverlay handles validating admission request // Checks the target resources for rules defined in the policy -func processOverlay(rule kubepolicy.Rule, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, error) { +func processOverlay(resourceUnstr *unstructured.Unstructured, rule kubepolicy.Rule, rawResource []byte) ([][]byte, error) { + //TODO check if there is better solution + resourceRaw, err := resourceUnstr.MarshalJSON() + if err != nil { + glog.V(4).Infof("unable to marshal resource : %v", err) + return nil, err + } var resource interface{} - if err := json.Unmarshal(rawResource, &resource); err != nil { + if err := json.Unmarshal(resourceRaw, &resource); err != nil { + glog.V(4).Infof("unable to unmarshal resource : %v", err) return nil, err } - resourceInfo := ParseResourceInfoFromObject(rawResource) + // resourceInfo := ParseResourceInfoFromObject(rawResource) patches, err := processOverlayPatches(resource, *rule.Mutation.Overlay) if err != nil && strings.Contains(err.Error(), "Conditions are not met") { - glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name) + glog.V(4).Infof("overlay pattern %s does not match resource %s/%s", *rule.Mutation.Overlay, resourceUnstr.GetNamespace(), resourceUnstr.GetName()) + // glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name) return nil, nil } diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index ecb03f2eac..ba9c372a61 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -150,6 +150,66 @@ func excludeNamespaces(namespaces []string, excludeNs *string) []string { return filteredNamespaces } +//MatchesResourceDescription checks if the resource matches resource desription of the rule or not +func MatchesResourceDescription(resource *unstructured.Unstructured, rule v1alpha1.Rule, gvk metav1.GroupVersionKind) bool { + matches := rule.MatchResources.ResourceDescription + exclude := rule.ExcludeResources.ResourceDescription + + if !findKind(matches.Kinds, gvk.Kind) { + return false + } + + // meta := parseMetadataFromObject(resourceRaw) + name := resource.GetName() + namespace := resource.GetNamespace() + + if matches.Name != nil { + // Matches + if !wildcard.Match(*matches.Name, name) { + return false + } + } + // Exclude + // the resource name matches the exclude resource name then reject + if exclude.Name != nil { + if wildcard.Match(*exclude.Name, name) { + return false + } + } + // Matches + if matches.Namespace != nil && *matches.Namespace != namespace { + return false + } + // Exclude + if exclude.Namespace != nil && *exclude.Namespace == namespace { + return false + } + // Matches + if matches.Selector != nil { + selector, err := metav1.LabelSelectorAsSelector(matches.Selector) + if err != nil { + glog.Error(err) + return false + } + if !selector.Matches(labels.Set(resource.GetLabels())) { + return false + } + } + // Exclude + if exclude.Selector != nil { + selector, err := metav1.LabelSelectorAsSelector(exclude.Selector) + // if the label selector is incorrect, should be fail or + if err != nil { + glog.Error(err) + return false + } + if selector.Matches(labels.Set(resource.GetLabels())) { + return false + } + } + return true +} + // ResourceMeetsDescription checks requests kind, name and labels to fit the policy rule func ResourceMeetsDescription(resourceRaw []byte, matches v1alpha1.ResourceDescription, exclude v1alpha1.ResourceDescription, gvk metav1.GroupVersionKind) bool { if !findKind(matches.Kinds, gvk.Kind) { @@ -458,3 +518,13 @@ type resourceInfo struct { Resource unstructured.Unstructured Gvk *metav1.GroupVersionKind } + +func convertToUnstructured(data []byte) (*unstructured.Unstructured, error) { + resource := &unstructured.Unstructured{} + err := resource.UnmarshalJSON(data) + if err != nil { + glog.V(4).Infof("failed to unmarshall resource: %v", err) + return nil, err + } + return resource, nil +} diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 92a98e2451..aa49d68f7f 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -18,38 +18,53 @@ import ( // Validate handles validating admission request // Checks the target resources for rules defined in the policy func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]*info.RuleInfo, error) { - var resource interface{} - ris := []*info.RuleInfo{} - - err := json.Unmarshal(rawResource, &resource) + //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information + //TODO: pass unstructured instead of rawResource ? + resourceUnstr, err := convertToUnstructured(rawResource) if err != nil { + glog.Errorf("unable to convert raw resource to unstructured: %v", err) + } + resourceRaw, err := resourceUnstr.MarshalJSON() + if err != nil { + glog.V(4).Infof("unable to marshal resource : %v", err) return nil, err } + var resource interface{} + if err := json.Unmarshal(resourceRaw, &resource); err != nil { + glog.V(4).Infof("unable to unmarshal resource : %v", err) + return nil, err + } + + var ruleInfos []*info.RuleInfo for _, rule := range policy.Spec.Rules { if rule.Validation == nil { continue } - ri := info.NewRuleInfo(rule.Name, info.Validation) - ok := ResourceMeetsDescription(rawResource, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, gvk) + // check if the resource satisfies the filter conditions defined in the rule + //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that + // dont statisfy a policy rule resource description + ok := MatchesResourceDescription(resourceUnstr, rule, gvk) if !ok { - glog.V(3).Infof("Not applicable on specified resource kind%s", gvk.Kind) + glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resourceUnstr.GetNamespace(), resourceUnstr.GetName()) continue } + ruleInfo := info.NewRuleInfo(rule.Name, info.Validation) + err := validateResourceWithPattern(resource, rule.Validation.Pattern) if err != nil { - ri.Fail() - ri.Addf("validation has failed, err %v.", err) + ruleInfo.Fail() + ruleInfo.Addf("Failed to apply pattern: %v.", err) } else { - ri.Addf("Rule %s: Validation succesfully.", rule.Name) - + ruleInfo.Add("Pattern succesfully validated") + glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resourceUnstr.GetNamespace(), resourceUnstr.GetName()) } - ris = append(ris, ri) + ruleInfos = append(ruleInfos, ruleInfo) } - return ris, nil + return ruleInfos, nil } // validateResourceWithPattern is a start of element-by-element validation process diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 82c5f01aac..db94cab820 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -7,16 +7,19 @@ import ( v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" ) // HandleMutation handles mutating webhook admission request func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { - + var patches [][]byte + var policyInfos []*info.PolicyInfo glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) policies, err := ws.policyLister.List(labels.NewSelector()) if err != nil { + //TODO check if the CRD is created ? // Unable to connect to policy Lister to access policies glog.Error("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied") glog.Warning(err) @@ -24,74 +27,58 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be Allowed: true, } } - rname := engine.ParseNameFromObject(request.Object.Raw) - rns := engine.ParseNamespaceFromObject(request.Object.Raw) - rkind := request.Kind.Kind - if rkind == "" { - glog.Errorf("failed to parse KIND from request: Namespace=%s Name=%s UID=%s patchOperation=%s\n", request.Namespace, request.Name, request.UID, request.Operation) - } - var allPatches [][]byte - policyInfos := []*info.PolicyInfo{} + resource, err := convertToUnstructured(request.Object.Raw) + if err != nil { + glog.Errorf("unable to convert raw resource to unstructured: %v", err) + } + //TODO: check if resource gvk is available in raw resource, + // if not then set it from the api request + resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind}) + //TODO: check if the name and namespace is also passed right in the resource? + // all the patches to be applied on the resource + for _, policy := range policies { // check if policy has a rule for the admission request kind if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { continue } - //TODO: HACK Check if an update of annotations - if checkIfOnlyAnnotationsUpdate(request) { - return &v1beta1.AdmissionResponse{ - Allowed: true, - } - } policyInfo := info.NewPolicyInfo(policy.Name, - rkind, - rname, - rns, + resource.GetKind(), + resource.GetName(), + resource.GetNamespace(), policy.Spec.ValidationFailureAction) - glog.V(3).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", - request.Kind.Kind, rns, rname, request.UID, request.Operation) - - glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) + glog.V(4).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", + resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation) + glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) + //TODO: passing policy value as we dont wont to modify the policy policyPatches, ruleInfos := engine.Mutate(*policy, request.Object.Raw, request.Kind) - policyInfo.AddRuleInfos(ruleInfos) - - if !policyInfo.IsSuccessful() { - glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range ruleInfos { - glog.Warningf("%s: %s\n", r.Name, r.Msgs) - } - } else { - // CleanUp Violations if exists - err := ws.violationBuilder.RemoveInactiveViolation(policy.Name, request.Kind.Kind, rns, rname, info.Mutation) - if err != nil { - glog.Info(err) - } - allPatches = append(allPatches, policyPatches...) - glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns) - } policyInfos = append(policyInfos, policyInfo) - annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Mutation) - if annPatch != nil { - // add annotations - ws.annotationsController.Add(rkind, rns, rname, annPatch) + if !policyInfo.IsSuccessful() { + glog.V(4).Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName()) + glog.V(4).Info("Failed rule details") + for _, r := range ruleInfos { + glog.V(4).Infof("%s: %s\n", r.Name, r.Msgs) + } + continue } + patches = append(patches, policyPatches...) + glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) } - if len(allPatches) > 0 { - eventsInfo, _ := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation) - ws.eventController.Add(eventsInfo...) - } + // ADD ANNOTATIONS + // ADD EVENTS + ok, msg := isAdmSuccesful(policyInfos) if ok { patchType := v1beta1.PatchTypeJSONPatch return &v1beta1.AdmissionResponse{ Allowed: true, - Patch: engine.JoinPatches(allPatches), + Patch: engine.JoinPatches(patches), PatchType: &patchType, } } diff --git a/pkg/webhooks/utils.go b/pkg/webhooks/utils.go index 83ca8944d7..e22737bd2d 100644 --- a/pkg/webhooks/utils.go +++ b/pkg/webhooks/utils.go @@ -210,3 +210,13 @@ func setKindForObject(bytes []byte, kind string) []byte { } return data } + +func convertToUnstructured(data []byte) (*unstructured.Unstructured, error) { + resource := &unstructured.Unstructured{} + err := resource.UnmarshalJSON(data) + if err != nil { + glog.V(4).Infof("failed to unmarshall resource: %v", err) + return nil, err + } + return resource, nil +} From 135f241a4aae7d832e3a37caa333d266f394ccc3 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Fri, 9 Aug 2019 13:41:56 -0700 Subject: [PATCH 11/66] event generator cleanup --- main.go | 9 +++-- pkg/event/controller.go | 83 ++++++++++++++++++-------------------- pkg/webhooks/mutation.go | 7 +--- pkg/webhooks/server.go | 6 +-- pkg/webhooks/validation.go | 76 ++++++++++++---------------------- 5 files changed, 76 insertions(+), 105 deletions(-) diff --git a/main.go b/main.go index ee3a7745d0..45e6a026ad 100644 --- a/main.go +++ b/main.go @@ -62,7 +62,7 @@ func main() { glog.Fatalf("Error creating policy sharedinformer: %v\n", err) } kubeInformer := utils.NewKubeInformerFactory(clientConfig) - eventController := event.NewEventController(client, policyInformerFactory) + egen := event.NewEventGenerator(client, policyInformerFactory) // violationBuilder := violation.NewPolicyViolationBuilder(client, policyInformerFactory, eventController) annotationsController := annotations.NewAnnotationControler(client) // policyController := controller.NewPolicyController( @@ -78,7 +78,7 @@ func main() { if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, nil, annotationsController, filterK8Resources) + server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, egen, nil, annotationsController, filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) } @@ -98,11 +98,12 @@ func main() { pInformer.Start(stopCh) go pc.Run(1, stopCh) go pvc.Run(1, stopCh) + go egen.Run(1, stopCh) //TODO add WG for the go routine? //-------- policyInformerFactory.Run(stopCh) kubeInformer.Start(stopCh) - eventController.Run(stopCh) + // eventController.Run(stopCh) // genControler.Run(stopCh) annotationsController.Run(stopCh) // if err = policyController.Run(stopCh); err != nil { @@ -113,7 +114,7 @@ func main() { <-stopCh server.Stop() // genControler.Stop() - eventController.Stop() + // eventController.Stop() annotationsController.Stop() // policyController.Stop() } diff --git a/pkg/event/controller.go b/pkg/event/controller.go index 5c5e9fca0b..bcbde5fb91 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -18,35 +18,31 @@ import ( "k8s.io/client-go/util/workqueue" ) -type controller struct { +//Generator generate events +type Generator struct { client *client.Client policyLister v1alpha1.PolicyLister queue workqueue.RateLimitingInterface recorder record.EventRecorder } -//Generator to generate event -type Generator interface { +//Interface to generate event +type Interface interface { Add(infoList ...*Info) } -//Controller api -type Controller interface { - Generator - Run(stopCh <-chan struct{}) - Stop() -} +//NewEventGenerator to generate a new event controller +func NewEventGenerator(client *client.Client, + shareInformer sharedinformer.PolicyInformer) *Generator { -//NewEventController to generate a new event controller -func NewEventController(client *client.Client, - shareInformer sharedinformer.PolicyInformer) Controller { - - return &controller{ + gen := Generator{ client: client, policyLister: shareInformer.GetLister(), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), eventWorkQueueName), recorder: initRecorder(client), } + + return &gen } func initRecorder(client *client.Client) record.EventRecorder { @@ -72,67 +68,67 @@ func initRecorder(client *client.Client) record.EventRecorder { return recorder } -func (c *controller) Add(infos ...*Info) { +//Add queues an event for generation +func (gen *Generator) Add(infos ...*Info) { for _, info := range infos { - c.queue.Add(*info) + gen.queue.Add(*info) } } -func (c *controller) Run(stopCh <-chan struct{}) { +// Run begins generator +func (gen *Generator) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() + glog.Info("Starting event generator") + defer glog.Info("Shutting down event generator") - for i := 0; i < eventWorkerThreadCount; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) + for i := 0; i < workers; i++ { + go wait.Until(gen.runWorker, time.Second, stopCh) } - glog.Info("Started eventbuilder controller workers") + <-stopCh + } -func (c *controller) Stop() { - defer c.queue.ShutDown() - glog.Info("Shutting down eventbuilder controller workers") -} - -func (c *controller) runWorker() { - for c.processNextWorkItem() { +func (gen *Generator) runWorker() { + for gen.processNextWorkItem() { } } -func (c *controller) handleErr(err error, key interface{}) { +func (gen *Generator) handleErr(err error, key interface{}) { if err == nil { - c.queue.Forget(key) + gen.queue.Forget(key) return } // This controller retries if something goes wrong. After that, it stops trying. - if c.queue.NumRequeues(key) < workQueueRetryLimit { + if gen.queue.NumRequeues(key) < workQueueRetryLimit { glog.Warningf("Error syncing events %v: %v", key, err) // Re-enqueue the key rate limited. Based on the rate limiter on the // queue and the re-enqueue history, the key will be processed later again. - c.queue.AddRateLimited(key) + gen.queue.AddRateLimited(key) return } - c.queue.Forget(key) + gen.queue.Forget(key) glog.Error(err) glog.Warningf("Dropping the key out of the queue: %v", err) } -func (c *controller) processNextWorkItem() bool { - obj, shutdown := c.queue.Get() +func (gen *Generator) processNextWorkItem() bool { + obj, shutdown := gen.queue.Get() if shutdown { return false } err := func(obj interface{}) error { - defer c.queue.Done(obj) + defer gen.queue.Done(obj) var key Info var ok bool if key, ok = obj.(Info); !ok { - c.queue.Forget(obj) + gen.queue.Forget(obj) glog.Warningf("Expecting type info by got %v\n", obj) return nil } - err := c.syncHandler(key) - c.handleErr(err, obj) + err := gen.syncHandler(key) + gen.handleErr(err, obj) return nil }(obj) if err != nil { @@ -142,20 +138,20 @@ func (c *controller) processNextWorkItem() bool { return true } -func (c *controller) syncHandler(key Info) error { +func (gen *Generator) syncHandler(key Info) error { var robj runtime.Object var err error switch key.Kind { case "Policy": //TODO: policy is clustered resource so wont need namespace - robj, err = c.policyLister.Get(key.Name) + robj, err = gen.policyLister.Get(key.Name) if err != nil { glog.Errorf("Error creating event: unable to get policy %s, will retry ", key.Name) return err } default: - robj, err = c.client.GetResource(key.Kind, key.Namespace, key.Name) + robj, err = gen.client.GetResource(key.Kind, key.Namespace, key.Name) if err != nil { glog.Errorf("Error creating event: unable to get resource %s, %s, will retry ", key.Kind, key.Namespace+"/"+key.Name) return err @@ -163,13 +159,14 @@ func (c *controller) syncHandler(key Info) error { } if key.Reason == PolicyApplied.String() { - c.recorder.Event(robj, v1.EventTypeNormal, key.Reason, key.Message) + gen.recorder.Event(robj, v1.EventTypeNormal, key.Reason, key.Message) } else { - c.recorder.Event(robj, v1.EventTypeWarning, key.Reason, key.Message) + gen.recorder.Event(robj, v1.EventTypeWarning, key.Reason, key.Message) } return nil } +//TODO: check if we need this ? //NewEvent returns a new event func NewEvent(rkind string, rnamespace string, rname string, reason Reason, message MsgKey, args ...interface{}) *Info { msgText, err := getEventMsg(message, args...) diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index db94cab820..d518206289 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -43,11 +43,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { continue } - policyInfo := info.NewPolicyInfo(policy.Name, - resource.GetKind(), - resource.GetName(), - resource.GetNamespace(), - policy.Spec.ValidationFailureAction) + policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) glog.V(4).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation) @@ -72,6 +68,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be // ADD ANNOTATIONS // ADD EVENTS + // ADD POLICY VIOLATIONS ok, msg := isAdmSuccesful(policyInfos) if ok { diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 6c5ce351f7..8d4a339a1e 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -29,7 +29,7 @@ type WebhookServer struct { server http.Server client *client.Client policyLister v1alpha1.PolicyLister - eventController event.Generator + eventGen event.Interface violationBuilder violation.Generator annotationsController annotations.Controller filterK8Resources []utils.K8Resource @@ -41,7 +41,7 @@ func NewWebhookServer( client *client.Client, tlsPair *tlsutils.TlsPemPair, shareInformer sharedinformer.PolicyInformer, - eventController event.Generator, + eventGen event.Interface, violationBuilder violation.Generator, annotationsController annotations.Controller, filterK8Resources string) (*WebhookServer, error) { @@ -60,7 +60,7 @@ func NewWebhookServer( ws := &WebhookServer{ client: client, policyLister: shareInformer.GetLister(), - eventController: eventController, + eventGen: eventGen, violationBuilder: violationBuilder, annotationsController: annotationsController, filterK8Resources: utils.ParseKinds(filterK8Resources), diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 93c13955cc..cbe6457ee3 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -7,18 +7,21 @@ import ( v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" ) // HandleValidation handles validating webhook admission request // If there are no errors in validating rule we apply generation rules func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { + // var patches [][]byte + var policyInfos []*info.PolicyInfo glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) - policyInfos := []*info.PolicyInfo{} policies, err := ws.policyLister.List(labels.NewSelector()) if err != nil { + //TODO check if the CRD is created ? // Unable to connect to policy Lister to access policies glog.Error("Unable to connect to policy controller to access policies. Validation Rules are NOT being applied") glog.Warning(err) @@ -27,36 +30,29 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } } - rname := engine.ParseNameFromObject(request.Object.Raw) - rns := engine.ParseNamespaceFromObject(request.Object.Raw) - rkind := request.Kind.Kind - if rkind == "" { - glog.Errorf("failed to parse KIND from request: Namespace=%s Name=%s UID=%s patchOperation=%s\n", request.Namespace, request.Name, request.UID, request.Operation) + resource, err := convertToUnstructured(request.Object.Raw) + if err != nil { + glog.Errorf("unable to convert raw resource to unstructured: %v", err) } + //TODO: check if resource gvk is available in raw resource, + // if not then set it from the api request + resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind}) + //TODO: check if the name and namespace is also passed right in the resource? + // all the patches to be applied on the resource for _, policy := range policies { if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { continue } - //TODO: HACK Check if an update of annotations - if checkIfOnlyAnnotationsUpdate(request) { - // allow the update of resource to add annotations - return &v1beta1.AdmissionResponse{ - Allowed: true, - } - } - policyInfo := info.NewPolicyInfo(policy.Name, - rkind, - rname, - rns, - policy.Spec.ValidationFailureAction) + policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) - glog.V(3).Infof("Handling validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", - request.Kind.Kind, rns, rname, request.UID, request.Operation) + glog.V(4).Infof("Handling validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", + resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation) + + glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - glog.Infof("Validating resource %s/%s/%s with policy %s with %d rules", rkind, rns, rname, policy.ObjectMeta.Name, len(policy.Spec.Rules)) ruleInfos, err := engine.Validate(*policy, request.Object.Raw, request.Kind) if err != nil { // This is not policy error @@ -68,42 +64,22 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 policyInfo.AddRuleInfos(ruleInfos) if !policyInfo.IsSuccessful() { - glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) + glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName()) + glog.V(4).Info("Failed rule details") for _, r := range ruleInfos { - glog.Warningf("%s: %s\n", r.Name, r.Msgs) - } - } else { - // CleanUp Violations if exists - err := ws.violationBuilder.RemoveInactiveViolation(policy.Name, request.Kind.Kind, rns, rname, info.Validation) - if err != nil { - glog.Info(err) - } - - if len(ruleInfos) > 0 { - glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns) + glog.V(4).Infof("%s: %s\n", r.Name, r.Msgs) } + continue + } + if len(ruleInfos) > 0 { + glog.V(4).Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) } policyInfos = append(policyInfos, policyInfo) - // annotations - annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Validation) - if annPatch != nil { - ws.annotationsController.Add(rkind, rns, rname, annPatch) - } } - if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 { - eventsInfo, violations := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Validation) - // If the validationFailureAction flag is set "audit", - // then we dont block the request and report the violations - ws.violationBuilder.Add(violations...) - ws.eventController.Add(eventsInfo...) - } - // If Validation fails then reject the request + // ADD EVENTS + // ADD POLICY VIOLATIONS ok, msg := isAdmSuccesful(policyInfos) - // violations are created if "audit" flag is set - // and if there are any then we dont bock the resource creation - // Even if one the policy being applied - if !ok && toBlock(policyInfos) { return &v1beta1.AdmissionResponse{ Allowed: false, From 1e621146bee7834608cb8132bd8ae36059e21dab Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Fri, 9 Aug 2019 16:55:43 -0700 Subject: [PATCH 12/66] test policy engine on admission requests --- main.go | 64 ++++++++++++++++------------------ pkg/engine/engine.go | 23 ++++++------ pkg/engine/generation.go | 10 +++--- pkg/engine/mutation.go | 8 +++-- pkg/engine/overlay.go | 8 ++--- pkg/engine/patches.go | 7 ++-- pkg/engine/utils.go | 55 ++++++++++++++--------------- pkg/engine/validation.go | 6 ++-- pkg/event/controller.go | 24 ++++++------- pkg/info/info.go | 5 +-- pkg/webhooks/deleteresource.go | 60 +++++++++++++++---------------- pkg/webhooks/mutation.go | 7 ++-- pkg/webhooks/report.go | 2 +- pkg/webhooks/server.go | 32 +++++++---------- pkg/webhooks/utils.go | 8 ++--- pkg/webhooks/validation.go | 8 ++--- 16 files changed, 160 insertions(+), 167 deletions(-) diff --git a/main.go b/main.go index 45e6a026ad..6e3cab4839 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "flag" "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/annotations" clientNew "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" kyvernoinformer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions" "github.com/nirmata/kyverno/pkg/config" @@ -12,8 +11,6 @@ import ( event "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/policyviolation" - "github.com/nirmata/kyverno/pkg/sharedinformer" - "github.com/nirmata/kyverno/pkg/utils" "github.com/nirmata/kyverno/pkg/webhooks" "k8s.io/sample-controller/pkg/signals" ) @@ -27,58 +24,61 @@ var ( func main() { defer glog.Flush() printVersionInfo() + + // CLIENT CONFIG clientConfig, err := createClientConfig(kubeconfig) if err != nil { glog.Fatalf("Error building kubeconfig: %v\n", err) } + // DYNAMIC CLIENT + // - client for all registered resources client, err := client.NewClient(clientConfig) if err != nil { glog.Fatalf("Error creating client: %v\n", err) } - // check if the k8 server version is supported ? - // if !version.Supportedk8Server(client) { - // glog.Fatalf("the k8 server version is not supported. refer to https://github.com/nirmata/kyverno/blob/master/documentation/installation.md for more details") - // } - //------------------------------------- - // create policy client + // KYVENO CRD CLIENT + // access CRD resources + // - Policy + // - PolicyViolation pclient, err := clientNew.NewForConfig(clientConfig) if err != nil { glog.Fatalf("Error creating client: %v\n", err) } + + // KYVERNO CRD INFORMER + // watches CRD resources: + // - Policy + // - PolicyVolation + // - cache resync time: 10 seconds pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10) + + // POLICY CONTROLLER + // - reconciliation policy and policy violation + // - status: violation count pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) if err != nil { glog.Fatalf("error creating policy controller: %v\n", err) } + + // POLICY VIOLATION CONTROLLER + // status: lastUpdatTime pvc, err := policyviolation.NewPolicyViolationController(client, pclient, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) if err != nil { glog.Fatalf("error creating policy violation controller: %v\n", err) } - //------------------------------------- - policyInformerFactory, err := sharedinformer.NewSharedInformerFactory(clientConfig) - if err != nil { - glog.Fatalf("Error creating policy sharedinformer: %v\n", err) - } - kubeInformer := utils.NewKubeInformerFactory(clientConfig) - egen := event.NewEventGenerator(client, policyInformerFactory) - // violationBuilder := violation.NewPolicyViolationBuilder(client, policyInformerFactory, eventController) - annotationsController := annotations.NewAnnotationControler(client) - // policyController := controller.NewPolicyController( - // client, - // policyInformerFactory, - // violationBuilder, - // eventController, - // annotationsController, - // filterK8Resources) - // genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces(), annotationsController) + // EVENT GENERATOR + // - generate event with retry + egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies()) + + // TODO : Process Existing tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, egen, nil, annotationsController, filterK8Resources) + server, err := webhooks.NewWebhookServer(client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), egen, filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) } @@ -99,23 +99,21 @@ func main() { go pc.Run(1, stopCh) go pvc.Run(1, stopCh) go egen.Run(1, stopCh) - //TODO add WG for the go routine? + + //TODO add WG for the go routines? //-------- - policyInformerFactory.Run(stopCh) - kubeInformer.Start(stopCh) // eventController.Run(stopCh) // genControler.Run(stopCh) - annotationsController.Run(stopCh) + // annotationsController.Run(stopCh) // if err = policyController.Run(stopCh); err != nil { // glog.Fatalf("Error running PolicyController: %v\n", err) // } - server.RunAsync() <-stopCh server.Stop() // genControler.Stop() // eventController.Stop() - annotationsController.Stop() + // annotationsController.Stop() // policyController.Stop() } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 5698c51048..3befc6f3bf 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -3,7 +3,7 @@ package engine import ( jsonpatch "github.com/evanphx/json-patch" "github.com/golang/glog" - types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" @@ -11,11 +11,11 @@ import ( ) // ProcessExisting checks for mutation and validation violations of existing resources -func ProcessExisting(client *client.Client, policy *types.Policy, filterK8Resources []utils.K8Resource) []*info.PolicyInfo { +func ProcessExisting(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) []info.PolicyInfo { glog.Infof("Applying policy %s on existing resources", policy.Name) // key uid resourceMap := ListResourcesThatApplyToPolicy(client, policy, filterK8Resources) - policyInfos := []*info.PolicyInfo{} + policyInfos := []info.PolicyInfo{} // for the filtered resource apply policy for _, v := range resourceMap { @@ -31,25 +31,28 @@ func ProcessExisting(client *client.Client, policy *types.Policy, filterK8Resour return policyInfos } -func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) (*info.PolicyInfo, error) { - policyInfo := info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction) +func applyPolicy(client *client.Client, policy *kyverno.Policy, res resourceInfo) (info.PolicyInfo, error) { + var policyInfo info.PolicyInfo glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) rawResource, err := res.Resource.MarshalJSON() if err != nil { - return nil, err + return policyInfo, err } // Mutate mruleInfos, err := mutation(policy, rawResource, res.Gvk) policyInfo.AddRuleInfos(mruleInfos) if err != nil { - return nil, err + return policyInfo, err } // Validation + //TODO check by value or pointer vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) - policyInfo.AddRuleInfos(vruleInfos) if err != nil { - return nil, err + return policyInfo, err } + policyInfo.AddRuleInfos(vruleInfos) + policyInfo = info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction) + if res.Gvk.Kind == "Namespace" { // Generation @@ -60,7 +63,7 @@ func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) return policyInfo, nil } -func mutation(p *types.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +func mutation(p *kyverno.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { patches, ruleInfos := Mutate(*p, rawResource, *gvk) if len(ruleInfos) == 0 { // no rules were processed diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 2bc02fc2f5..891682be3e 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -5,7 +5,7 @@ import ( "errors" "github.com/golang/glog" - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" @@ -14,10 +14,10 @@ import ( ) //Generate apply generation rules on a resource -func Generate(client *client.Client, policy *v1alpha1.Policy, ns unstructured.Unstructured) []*info.RuleInfo { +func Generate(client *client.Client, policy *kyverno.Policy, ns unstructured.Unstructured) []*info.RuleInfo { ris := []*info.RuleInfo{} for _, rule := range policy.Spec.Rules { - if rule.Generation == nil { + if rule.Generation == (kyverno.Generation{}) { continue } ri := info.NewRuleInfo(rule.Name, info.Generation) @@ -34,7 +34,7 @@ func Generate(client *client.Client, policy *v1alpha1.Policy, ns unstructured.Un return ris } -func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen *v1alpha1.Generation) error { +func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen kyverno.Generation) error { var err error resource := &unstructured.Unstructured{} var rdata map[string]interface{} @@ -61,7 +61,7 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen return err } } - if gen.Clone != nil { + if gen.Clone != (kyverno.CloneFrom{}) { // 1> Check if resource exists _, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name) if err == nil { diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 1de615622c..8445bd8b97 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -1,15 +1,17 @@ package engine import ( + "reflect" + "github.com/golang/glog" - kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Mutate performs mutation. Overlay first and then mutation patches //TODO: check if gvk needs to be passed or can be set in resource -func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []*info.RuleInfo) { +func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []*info.RuleInfo) { //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information //TODO: pass unstructured instead of rawResource ? resource, err := convertToUnstructured(rawResource) @@ -20,7 +22,7 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio var ruleInfos []*info.RuleInfo for _, rule := range policy.Spec.Rules { - if rule.Mutation == nil { + if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) { continue } diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index 62af73e867..27118f5530 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -11,13 +11,13 @@ import ( "github.com/golang/glog" jsonpatch "github.com/evanphx/json-patch" - kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // ProcessOverlay handles validating admission request // Checks the target resources for rules defined in the policy -func processOverlay(resourceUnstr *unstructured.Unstructured, rule kubepolicy.Rule, rawResource []byte) ([][]byte, error) { +func processOverlay(resourceUnstr *unstructured.Unstructured, rule kyverno.Rule, rawResource []byte) ([][]byte, error) { //TODO check if there is better solution resourceRaw, err := resourceUnstr.MarshalJSON() @@ -32,9 +32,9 @@ func processOverlay(resourceUnstr *unstructured.Unstructured, rule kubepolicy.Ru } // resourceInfo := ParseResourceInfoFromObject(rawResource) - patches, err := processOverlayPatches(resource, *rule.Mutation.Overlay) + patches, err := processOverlayPatches(resource, rule.Mutation.Overlay) if err != nil && strings.Contains(err.Error(), "Conditions are not met") { - glog.V(4).Infof("overlay pattern %s does not match resource %s/%s", *rule.Mutation.Overlay, resourceUnstr.GetNamespace(), resourceUnstr.GetName()) + glog.V(4).Infof("overlay pattern %s does not match resource %s/%s", rule.Mutation.Overlay, resourceUnstr.GetNamespace(), resourceUnstr.GetName()) // glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name) return nil, nil } diff --git a/pkg/engine/patches.go b/pkg/engine/patches.go index 8f7d69c76e..b13c0971a3 100644 --- a/pkg/engine/patches.go +++ b/pkg/engine/patches.go @@ -3,21 +3,22 @@ package engine import ( "encoding/json" "errors" + "reflect" "github.com/golang/glog" jsonpatch "github.com/evanphx/json-patch" - kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" ) // ProcessPatches Returns array from separate patches that can be applied to the document // Returns error ONLY in case when creation of resource should be denied. -func processPatches(rule kubepolicy.Rule, resource []byte) (allPatches [][]byte, errs []error) { +func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, errs []error) { if len(resource) == 0 { errs = append(errs, errors.New("Source document for patching is empty")) return nil, errs } - if rule.Mutation == nil { + if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) { errs = append(errs, errors.New("No Mutation rules defined")) return nil, errs } diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index ba9c372a61..cf80c5b40c 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -9,8 +9,7 @@ import ( "github.com/golang/glog" "github.com/minio/minio/pkg/wildcard" - types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/utils" v1helper "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -21,7 +20,7 @@ import ( ) //ListResourcesThatApplyToPolicy returns list of resources that are filtered by policy rules -func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy, filterK8Resources []utils.K8Resource) map[string]resourceInfo { +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 { @@ -31,9 +30,9 @@ func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy, if k == "Namespace" { namespaces = []string{""} } else { - if rule.MatchResources.Namespace != nil { + if rule.MatchResources.Namespace != "" { // if namespace is specified then we add the namespace - namespaces = append(namespaces, *rule.MatchResources.Namespace) + namespaces = append(namespaces, rule.MatchResources.Namespace) } else { // no namespace specified, refer to all namespaces namespaces = getAllNamespaces(client) @@ -54,7 +53,7 @@ func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy, return resourceMap } -func getResourcesPerNamespace(kind string, client *client.Client, namespace string, rule types.Rule, filterK8Resources []utils.K8Resource) map[string]resourceInfo { +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) @@ -79,14 +78,14 @@ func getResourcesPerNamespace(kind string, client *client.Client, namespace stri continue } } - var name *string + var name string // match // name // wild card matching name = rule.MatchResources.Name - if name != nil { + if name != "" { // if does not match then we skip - if !wildcard.Match(*name, res.GetName()) { + if !wildcard.Match(name, res.GetName()) { continue } } @@ -94,9 +93,9 @@ func getResourcesPerNamespace(kind string, client *client.Client, namespace stri // name // wild card matching name = rule.ExcludeResources.Name - if name != nil { + if name != "nil" { // if matches then we skip - if wildcard.Match(*name, res.GetName()) { + if wildcard.Match(name, res.GetName()) { continue } } @@ -136,13 +135,13 @@ func getAllNamespaces(client *client.Client) []string { return namespaces } -func excludeNamespaces(namespaces []string, excludeNs *string) []string { - if excludeNs == nil { +func excludeNamespaces(namespaces []string, excludeNs string) []string { + if excludeNs == "" { return namespaces } filteredNamespaces := []string{} for _, n := range namespaces { - if n == *excludeNs { + if n == excludeNs { continue } filteredNamespaces = append(filteredNamespaces, n) @@ -151,7 +150,7 @@ func excludeNamespaces(namespaces []string, excludeNs *string) []string { } //MatchesResourceDescription checks if the resource matches resource desription of the rule or not -func MatchesResourceDescription(resource *unstructured.Unstructured, rule v1alpha1.Rule, gvk metav1.GroupVersionKind) bool { +func MatchesResourceDescription(resource *unstructured.Unstructured, rule kyverno.Rule, gvk metav1.GroupVersionKind) bool { matches := rule.MatchResources.ResourceDescription exclude := rule.ExcludeResources.ResourceDescription @@ -163,25 +162,25 @@ func MatchesResourceDescription(resource *unstructured.Unstructured, rule v1alph name := resource.GetName() namespace := resource.GetNamespace() - if matches.Name != nil { + if matches.Name != "" { // Matches - if !wildcard.Match(*matches.Name, name) { + if !wildcard.Match(matches.Name, name) { return false } } // Exclude // the resource name matches the exclude resource name then reject - if exclude.Name != nil { - if wildcard.Match(*exclude.Name, name) { + if exclude.Name != "" { + if wildcard.Match(exclude.Name, name) { return false } } // Matches - if matches.Namespace != nil && *matches.Namespace != namespace { + if matches.Namespace != "" && matches.Namespace != namespace { return false } // Exclude - if exclude.Namespace != nil && *exclude.Namespace == namespace { + if exclude.Namespace != "" && exclude.Namespace == namespace { return false } // Matches @@ -211,7 +210,7 @@ func MatchesResourceDescription(resource *unstructured.Unstructured, rule v1alph } // ResourceMeetsDescription checks requests kind, name and labels to fit the policy rule -func ResourceMeetsDescription(resourceRaw []byte, matches v1alpha1.ResourceDescription, exclude v1alpha1.ResourceDescription, gvk metav1.GroupVersionKind) bool { +func ResourceMeetsDescription(resourceRaw []byte, matches kyverno.ResourceDescription, exclude kyverno.ResourceDescription, gvk metav1.GroupVersionKind) bool { if !findKind(matches.Kinds, gvk.Kind) { return false } @@ -221,25 +220,25 @@ func ResourceMeetsDescription(resourceRaw []byte, matches v1alpha1.ResourceDescr name := ParseNameFromObject(resourceRaw) namespace := ParseNamespaceFromObject(resourceRaw) - if matches.Name != nil { + if matches.Name != "" { // Matches - if !wildcard.Match(*matches.Name, name) { + if !wildcard.Match(matches.Name, name) { return false } } // Exclude // the resource name matches the exclude resource name then reject - if exclude.Name != nil { - if wildcard.Match(*exclude.Name, name) { + if exclude.Name != "" { + if wildcard.Match(exclude.Name, name) { return false } } // Matches - if matches.Namespace != nil && *matches.Namespace != namespace { + if matches.Namespace != "" && matches.Namespace != namespace { return false } // Exclude - if exclude.Namespace != nil && *exclude.Namespace == namespace { + if exclude.Namespace != "" && exclude.Namespace == namespace { return false } // Matches diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index aa49d68f7f..b3edc32712 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -10,14 +10,14 @@ import ( "strings" "github.com/golang/glog" - kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Validate handles validating admission request // Checks the target resources for rules defined in the policy -func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +func Validate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]*info.RuleInfo, error) { //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information //TODO: pass unstructured instead of rawResource ? resourceUnstr, err := convertToUnstructured(rawResource) @@ -38,7 +38,7 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers var ruleInfos []*info.RuleInfo for _, rule := range policy.Spec.Rules { - if rule.Validation == nil { + if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) { continue } diff --git a/pkg/event/controller.go b/pkg/event/controller.go index bcbde5fb91..491f48bc85 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -6,9 +6,9 @@ import ( "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" policyscheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" - v1alpha1 "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" + informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" - "github.com/nirmata/kyverno/pkg/sharedinformer" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -20,10 +20,10 @@ import ( //Generator generate events type Generator struct { - client *client.Client - policyLister v1alpha1.PolicyLister - queue workqueue.RateLimitingInterface - recorder record.EventRecorder + client *client.Client + pLister lister.PolicyLister + queue workqueue.RateLimitingInterface + recorder record.EventRecorder } //Interface to generate event @@ -33,13 +33,13 @@ type Interface interface { //NewEventGenerator to generate a new event controller func NewEventGenerator(client *client.Client, - shareInformer sharedinformer.PolicyInformer) *Generator { + pInformer informer.PolicyInformer) *Generator { gen := Generator{ - client: client, - policyLister: shareInformer.GetLister(), - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), eventWorkQueueName), - recorder: initRecorder(client), + client: client, + pLister: pInformer.Lister(), + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), eventWorkQueueName), + recorder: initRecorder(client), } return &gen @@ -145,7 +145,7 @@ func (gen *Generator) syncHandler(key Info) error { switch key.Kind { case "Policy": //TODO: policy is clustered resource so wont need namespace - robj, err = gen.policyLister.Get(key.Name) + robj, err = gen.pLister.Get(key.Name) if err != nil { glog.Errorf("Error creating event: unable to get policy %s, will retry ", key.Name) return err diff --git a/pkg/info/info.go b/pkg/info/info.go index 05777a4cdc..65f43167ac 100644 --- a/pkg/info/info.go +++ b/pkg/info/info.go @@ -25,8 +25,8 @@ type PolicyInfo struct { } //NewPolicyInfo returns a new policy info -func NewPolicyInfo(policyName, rKind, rName, rNamespace, validationFailureAction string) *PolicyInfo { - return &PolicyInfo{ +func NewPolicyInfo(policyName, rKind, rName, rNamespace, validationFailureAction string) PolicyInfo { + pi := PolicyInfo{ Name: policyName, RKind: rKind, RName: rName, @@ -34,6 +34,7 @@ func NewPolicyInfo(policyName, rKind, rName, rNamespace, validationFailureAction success: true, // fail to be set explicity ValidationFailureAction: validationFailureAction, } + return pi } //IsSuccessful checks if policy is succesful diff --git a/pkg/webhooks/deleteresource.go b/pkg/webhooks/deleteresource.go index e1a5bfcb35..61c62c0e1d 100644 --- a/pkg/webhooks/deleteresource.go +++ b/pkg/webhooks/deleteresource.go @@ -1,40 +1,38 @@ package webhooks import ( - "errors" - - engine "github.com/nirmata/kyverno/pkg/engine" v1beta1 "k8s.io/api/admission/v1beta1" - "k8s.io/apimachinery/pkg/labels" ) func (ws *WebhookServer) removePolicyViolation(request *v1beta1.AdmissionRequest) error { - // Get the list of policies that apply on the resource - policies, err := ws.policyLister.List(labels.NewSelector()) - if err != nil { - // Unable to connect to policy Lister to access policies - return errors.New("Unable to connect to policy controller to access policies. Clean Up of Policy Violations is not being done") - } - for _, policy := range policies { - // check if policy has a rule for the admission request kind - if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { - continue - } - // get the details from the request - rname := request.Name - rns := request.Namespace - rkind := request.Kind.Kind - // check if the resource meets the policy Resource description - for _, rule := range policy.Spec.Rules { - ok := engine.ResourceMeetsDescription(request.Object.Raw, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, request.Kind) - if ok { - // Check if the policy has a violation for this resource - err := ws.violationBuilder.ResourceRemoval(policy.Name, rkind, rns, rname) - if err != nil { - return err - } - } - } - } + //TODO: ClenUp will be handled by the policycontroller + + // // Get the list of policies that apply on the resource + // policies, err := ws.policyLister.List(labels.NewSelector()) + // if err != nil { + // // Unable to connect to policy Lister to access policies + // return errors.New("Unable to connect to policy controller to access policies. Clean Up of Policy Violations is not being done") + // } + // for _, policy := range policies { + // // check if policy has a rule for the admission request kind + // if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { + // continue + // } + // // get the details from the request + // rname := request.Name + // rns := request.Namespace + // rkind := request.Kind.Kind + // // check if the resource meets the policy Resource description + // for _, rule := range policy.Spec.Rules { + // ok := engine.ResourceMeetsDescription(request.Object.Raw, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, request.Kind) + // if ok { + // // Check if the policy has a violation for this resource + // err := ws.violationBuilder.ResourceRemoval(policy.Name, rkind, rns, rname) + // if err != nil { + // return err + // } + // } + // } + // } return nil } diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index d518206289..bcd37e231a 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -13,11 +13,11 @@ import ( // HandleMutation handles mutating webhook admission request func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { var patches [][]byte - var policyInfos []*info.PolicyInfo + var policyInfos []info.PolicyInfo glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) - policies, err := ws.policyLister.List(labels.NewSelector()) + policies, err := ws.pLister.List(labels.NewSelector()) if err != nil { //TODO check if the CRD is created ? // Unable to connect to policy Lister to access policies @@ -53,8 +53,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be policyPatches, ruleInfos := engine.Mutate(*policy, request.Object.Raw, request.Kind) policyInfo.AddRuleInfos(ruleInfos) policyInfos = append(policyInfos, policyInfo) - - if !policyInfo.IsSuccessful() { + if !policyInfo.IsSuccessful() { glog.V(4).Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName()) glog.V(4).Info("Failed rule details") for _, r := range ruleInfos { diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go index 7f9b4697af..f0e171e1d8 100644 --- a/pkg/webhooks/report.go +++ b/pkg/webhooks/report.go @@ -12,7 +12,7 @@ import ( ) //TODO: change validation from bool -> enum(validation, mutation) -func newEventInfoFromPolicyInfo(policyInfoList []*info.PolicyInfo, onUpdate bool, ruleType info.RuleType) ([]*event.Info, []*violation.Info) { +func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, ruleType info.RuleType) ([]*event.Info, []*violation.Info) { var eventsInfo []*event.Info var violations []*violation.Info ok, msg := isAdmSuccesful(policyInfoList) diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 8d4a339a1e..cd36edf8d2 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -11,28 +11,24 @@ import ( "time" "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/annotations" - "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" + informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/sharedinformer" tlsutils "github.com/nirmata/kyverno/pkg/tls" "github.com/nirmata/kyverno/pkg/utils" - "github.com/nirmata/kyverno/pkg/violation" v1beta1 "k8s.io/api/admission/v1beta1" ) // WebhookServer contains configured TLS server with MutationWebhook. // MutationWebhook gets policies from policyController and takes control of the cluster with kubeclient. type WebhookServer struct { - server http.Server - client *client.Client - policyLister v1alpha1.PolicyLister - eventGen event.Interface - violationBuilder violation.Generator - annotationsController annotations.Controller - filterK8Resources []utils.K8Resource + server http.Server + client *client.Client + pLister lister.PolicyLister + eventGen event.Interface + filterK8Resources []utils.K8Resource } // NewWebhookServer creates new instance of WebhookServer accordingly to given configuration @@ -40,10 +36,8 @@ type WebhookServer struct { func NewWebhookServer( client *client.Client, tlsPair *tlsutils.TlsPemPair, - shareInformer sharedinformer.PolicyInformer, + pInformer informer.PolicyInformer, eventGen event.Interface, - violationBuilder violation.Generator, - annotationsController annotations.Controller, filterK8Resources string) (*WebhookServer, error) { if tlsPair == nil { @@ -58,12 +52,10 @@ func NewWebhookServer( tlsConfig.Certificates = []tls.Certificate{pair} ws := &WebhookServer{ - client: client, - policyLister: shareInformer.GetLister(), - eventGen: eventGen, - violationBuilder: violationBuilder, - annotationsController: annotationsController, - filterK8Resources: utils.ParseKinds(filterK8Resources), + client: client, + pLister: pInformer.Lister(), + eventGen: eventGen, + filterK8Resources: utils.ParseKinds(filterK8Resources), } mux := http.NewServeMux() mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve) diff --git a/pkg/webhooks/utils.go b/pkg/webhooks/utils.go index e22737bd2d..78fd64835e 100644 --- a/pkg/webhooks/utils.go +++ b/pkg/webhooks/utils.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" v1beta1 "k8s.io/api/admission/v1beta1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -15,7 +15,7 @@ import ( const policyKind = "Policy" -func isAdmSuccesful(policyInfos []*info.PolicyInfo) (bool, string) { +func isAdmSuccesful(policyInfos []info.PolicyInfo) (bool, string) { var admSuccess = true var errMsgs []string for _, pi := range policyInfos { @@ -73,7 +73,7 @@ func (i *ArrayFlags) Set(value string) error { } // extract the kinds that the policy rules apply to -func getApplicableKindsForPolicy(p *v1alpha1.Policy) []string { +func getApplicableKindsForPolicy(p *kyverno.Policy) []string { kindsMap := map[string]interface{}{} kinds := []string{} // iterate over the rules an identify all kinds @@ -106,7 +106,7 @@ const ( // returns true -> if there is even one policy that blocks resource requst // returns false -> if all the policies are meant to report only, we dont block resource request -func toBlock(pis []*info.PolicyInfo) bool { +func toBlock(pis []info.PolicyInfo) bool { for _, pi := range pis { if pi.ValidationFailureAction != ReportViolation { return true diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index cbe6457ee3..f043be3e91 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -13,13 +13,12 @@ import ( // HandleValidation handles validating webhook admission request // If there are no errors in validating rule we apply generation rules func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { - // var patches [][]byte - var policyInfos []*info.PolicyInfo + var policyInfos []info.PolicyInfo glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) - policies, err := ws.policyLister.List(labels.NewSelector()) + policies, err := ws.pLister.List(labels.NewSelector()) if err != nil { //TODO check if the CRD is created ? // Unable to connect to policy Lister to access policies @@ -62,6 +61,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 continue } policyInfo.AddRuleInfos(ruleInfos) + policyInfos = append(policyInfos, policyInfo) if !policyInfo.IsSuccessful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName()) @@ -74,10 +74,10 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 if len(ruleInfos) > 0 { glog.V(4).Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) } - policyInfos = append(policyInfos, policyInfo) } // ADD EVENTS + // ADD POLICY VIOLATIONS ok, msg := isAdmSuccesful(policyInfos) if !ok && toBlock(policyInfos) { From e5b4e5a116e81c2b4089d53c1f0a3c6a732aaac2 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Fri, 9 Aug 2019 17:28:49 -0700 Subject: [PATCH 13/66] generate events for resource & policy --- pkg/webhooks/mutation.go | 7 ++++++- pkg/webhooks/validation.go | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index bcd37e231a..5fd48d198c 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -53,7 +53,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be policyPatches, ruleInfos := engine.Mutate(*policy, request.Object.Raw, request.Kind) policyInfo.AddRuleInfos(ruleInfos) policyInfos = append(policyInfos, policyInfo) - if !policyInfo.IsSuccessful() { + if !policyInfo.IsSuccessful() { glog.V(4).Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName()) glog.V(4).Info("Failed rule details") for _, r := range ruleInfos { @@ -66,7 +66,12 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be } // ADD ANNOTATIONS + // TODO: merge the annotation patch with the patch response // ADD EVENTS + if len(patches) > 0 { + eventsInfo, _ := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation) + ws.eventGen.Add(eventsInfo...) + } // ADD POLICY VIOLATIONS ok, msg := isAdmSuccesful(policyInfos) diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index f043be3e91..75f12ffa2d 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -77,8 +77,15 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } // ADD EVENTS + if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 { + eventsInfo, _ := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Validation) + // If the validationFailureAction flag is set "audit", + // then we dont block the request and report the violations + ws.eventGen.Add(eventsInfo...) + } // ADD POLICY VIOLATIONS + ok, msg := isAdmSuccesful(policyInfos) if !ok && toBlock(policyInfos) { return &v1beta1.AdmissionResponse{ From 2cdeac59883fcd03d75cb91bcda6e8a7f7b17a36 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Fri, 9 Aug 2019 19:12:50 -0700 Subject: [PATCH 14/66] start creation policy violation --- main.go | 15 ++++--- pkg/policyviolation/helpers.go | 2 + pkg/webhooks/mutation.go | 3 ++ pkg/webhooks/report.go | 75 ++++++++++++++++++++++++++++++++++ pkg/webhooks/server.go | 4 ++ pkg/webhooks/validation.go | 5 ++- 6 files changed, 94 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index 6e3cab4839..b9e1d34bc8 100644 --- a/main.go +++ b/main.go @@ -31,13 +31,6 @@ func main() { glog.Fatalf("Error building kubeconfig: %v\n", err) } - // DYNAMIC CLIENT - // - client for all registered resources - client, err := client.NewClient(clientConfig) - if err != nil { - glog.Fatalf("Error creating client: %v\n", err) - } - // KYVENO CRD CLIENT // access CRD resources // - Policy @@ -46,6 +39,12 @@ func main() { if err != nil { glog.Fatalf("Error creating client: %v\n", err) } + // DYNAMIC CLIENT + // - client for all registered resources + client, err := client.NewClient(clientConfig) + if err != nil { + glog.Fatalf("Error creating client: %v\n", err) + } // KYVERNO CRD INFORMER // watches CRD resources: @@ -78,7 +77,7 @@ func main() { if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - server, err := webhooks.NewWebhookServer(client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), egen, filterK8Resources) + server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), egen, filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) } diff --git a/pkg/policyviolation/helpers.go b/pkg/policyviolation/helpers.go index 69d8d85ae4..ee3aa94bf2 100644 --- a/pkg/policyviolation/helpers.go +++ b/pkg/policyviolation/helpers.go @@ -13,5 +13,7 @@ func BuildPolicyViolation(policy string, resource kyverno.ResourceSpec, fRules [ ViolatedRules: fRules, }, } + //TODO: check if this can be removed or use unstructured? + // pv.Kind = "PolicyViolation" return pv } diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 5fd48d198c..99447f85e6 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -14,6 +14,8 @@ import ( func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { var patches [][]byte var policyInfos []info.PolicyInfo + // map to store the mutation changes on the resource + // mAnn := map[string]string{} glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) @@ -61,6 +63,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be } continue } + // build annotations per policy being applied to show the mutation changes patches = append(patches, policyPatches...) glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) } diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go index f0e171e1d8..1f6b93087f 100644 --- a/pkg/webhooks/report.go +++ b/pkg/webhooks/report.go @@ -1,14 +1,18 @@ package webhooks import ( + "reflect" "strings" "github.com/nirmata/kyverno/pkg/annotations" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" "github.com/nirmata/kyverno/pkg/violation" "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/info" + "github.com/nirmata/kyverno/pkg/policyviolation" ) //TODO: change validation from bool -> enum(validation, mutation) @@ -75,3 +79,74 @@ func addAnnotationsToResource(rawResource []byte, pi *info.PolicyInfo, ruleType } return patch } + +//buildAnnotation we add annotations for the successful application of JSON patches +//TODO +func buildAnnotation(mAnn map[string]string, pi *info.PolicyInfo) { + if len(pi.Rules) == 0 { + return + } + var mchanges []string + + for _, r := range pi.Rules { + if r.Changes != "" { + // the rule generate a patch + // key policy name will be updated to right format during creation of annotations + mchanges = append(mchanges) + } + } +} + +// buildPolicyViolationsForAPolicy returns a policy violation object if there are any rules that fail +func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation { + var fRules []kyverno.ViolatedRule + var pv kyverno.PolicyViolation + for _, r := range pi.Rules { + if !r.IsSuccessful() { + fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()}) + } + } + if len(fRules) > 0 { + glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName) + // there is an error + pv = policyviolation.BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{ + Kind: pi.RKind, + Namespace: pi.RNamespace, + Name: pi.RName, + }, + fRules, + ) + + } + return pv +} + +//generatePolicyViolations generate policyViolation resources for the rules that failed +func generatePolicyViolations(client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { + var pvs []kyverno.PolicyViolation + for _, policyInfo := range policyInfos { + if !policyInfo.IsSuccessful() { + if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) { + pvs = append(pvs, pv) + } + } + } + + if len(pvs) > 0 { + for _, newPv := range pvs { + // generate PolicyViolation objects + glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name) + + // check if there was a previous violation for policy & resource combination + //TODO: check for existing ov using label selectors on resource and policy + // curPv, err:= client.KyvernoV1alpha1().PolicyViolations(). + + // _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv) + // // _, err := client.CreateResource("PolicyViolation", "", &pv, false) + // if err != nil { + // glog.Error(err) + // continue + // } + } + } +} diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index cd36edf8d2..5ef509f639 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -11,6 +11,7 @@ import ( "time" "github.com/golang/glog" + kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/config" @@ -26,6 +27,7 @@ import ( type WebhookServer struct { server http.Server client *client.Client + kyvernoClient *kyvernoclient.Clientset pLister lister.PolicyLister eventGen event.Interface filterK8Resources []utils.K8Resource @@ -34,6 +36,7 @@ type WebhookServer struct { // NewWebhookServer creates new instance of WebhookServer accordingly to given configuration // Policy Controller and Kubernetes Client should be initialized in configuration func NewWebhookServer( + kyvernoClient *kyvernoclient.Clientset, client *client.Client, tlsPair *tlsutils.TlsPemPair, pInformer informer.PolicyInformer, @@ -53,6 +56,7 @@ func NewWebhookServer( ws := &WebhookServer{ client: client, + kyvernoClient: kyvernoClient, pLister: pInformer.Lister(), eventGen: eventGen, filterK8Resources: utils.ParseKinds(filterK8Resources), diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 75f12ffa2d..23e733e2eb 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -84,8 +84,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 ws.eventGen.Add(eventsInfo...) } - // ADD POLICY VIOLATIONS - ok, msg := isAdmSuccesful(policyInfos) if !ok && toBlock(policyInfos) { return &v1beta1.AdmissionResponse{ @@ -96,6 +94,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } } + // ADD POLICY VIOLATIONS + generatePolicyViolations(ws.kyvernoClient, policyInfos) + return &v1beta1.AdmissionResponse{ Allowed: true, } From a5e1b43eb7ccc1515c0642880df1b9840ec6d62b Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Fri, 9 Aug 2019 20:08:22 -0700 Subject: [PATCH 15/66] policy violation support (incomplete) --- main.go | 2 +- pkg/api/kyverno/v1alpha1/utils.go | 8 ++++ pkg/policy/controller.go | 49 ++++++++++++++++++--- pkg/webhooks/report.go | 73 ++++++++++++++++++++++++++----- pkg/webhooks/server.go | 3 ++ pkg/webhooks/validation.go | 2 +- 6 files changed, 120 insertions(+), 17 deletions(-) diff --git a/main.go b/main.go index b9e1d34bc8..92095d417e 100644 --- a/main.go +++ b/main.go @@ -77,7 +77,7 @@ func main() { if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), egen, filterK8Resources) + server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) } diff --git a/pkg/api/kyverno/v1alpha1/utils.go b/pkg/api/kyverno/v1alpha1/utils.go index 892a52e74f..32bd4f58be 100644 --- a/pkg/api/kyverno/v1alpha1/utils.go +++ b/pkg/api/kyverno/v1alpha1/utils.go @@ -101,3 +101,11 @@ func (gen *Generation) DeepCopyInto(out *Generation) { *out = *gen } } + +//ToKey generates the key string used for adding label to polivy violation +func (rs ResourceSpec) ToKey() string { + if rs.Namespace == "" { + return rs.Kind + "." + rs.Name + } + return rs.Kind + "." + rs.Namespace + "." + rs.Name +} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 4022a8a9be..8ad0e8b124 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -716,7 +716,7 @@ type patchLabelMapValue struct { Value map[string]string `json:"value"` } -func createLabelPatch(policy string) ([]byte, error) { +func createPolicyLabelPatch(policy string) ([]byte, error) { payload := []patchLabelValue{{ Op: "add", Path: "/metadata/labels/policy", @@ -725,11 +725,20 @@ func createLabelPatch(policy string) ([]byte, error) { return json.Marshal(payload) } -func createLabelMapPatch(policy string) ([]byte, error) { +func createResourceLabelPatch(resource string) ([]byte, error) { + payload := []patchLabelValue{{ + Op: "add", + Path: "/metadata/labels/resource", + Value: resource, + }} + return json.Marshal(payload) +} + +func createLabelMapPatch(policy string, resource string) ([]byte, error) { payload := []patchLabelMapValue{{ Op: "add", Path: "/metadata/labels", - Value: map[string]string{"policy": policy}, + Value: map[string]string{"policy": policy, "resource": resource}, }} return json.Marshal(payload) } @@ -739,6 +748,7 @@ func createLabelMapPatch(policy string) ([]byte, error) { func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.PolicyViolation) bool { updateLabel := func() bool { glog.V(4).Infof("adding label 'policy:%s' to PolicyViolation %s", pv.Spec.Policy, pv.Name) + glog.V(4).Infof("adding label 'resource:%s' to PolicyViolation %s", pv.Spec.ResourceSpec.ToKey(), pv.Name) // add label based on the policy spec labels := pv.GetLabels() if pv.Spec.Policy == "" { @@ -748,7 +758,7 @@ func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.Pol } if labels == nil { // create a patch to generate the labels map with policy label - patch, err := createLabelMapPatch(pv.Spec.Policy) + patch, err := createLabelMapPatch(pv.Spec.Policy, pv.Spec.ResourceSpec.ToKey()) if err != nil { glog.Errorf("unable to init label map. %v", err) return false @@ -761,11 +771,23 @@ func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.Pol return true } // JSON Patch to add exact label - labelPatch, err := createLabelPatch(pv.Spec.Policy) + policyLabelPatch, err := createPolicyLabelPatch(pv.Spec.Policy) if err != nil { glog.Errorf("failed to generate patch to add label 'policy': %v", err) return false } + resourceLabelPatch, err := createResourceLabelPatch(pv.Spec.ResourceSpec.ToKey()) + if err != nil { + glog.Errorf("failed to generate patch to add label 'resource': %v", err) + return false + } + //join patches + labelPatch := joinPatches(policyLabelPatch, resourceLabelPatch) + if labelPatch == nil { + glog.Errorf("failed to join patches : %v", err) + return false + } + glog.V(4).Infof("patching policy violation %s with patch %s", pv.Name, string(labelPatch)) if err := pvControl.PatchPolicyViolation(pv.Name, labelPatch); err != nil { glog.Errorf("Unable to add 'policy' label to PolicyViolation %s: %v", pv.Name, err) return false @@ -789,3 +811,20 @@ func updatePolicyLabelIfNotDefined(pvControl PVControlInterface, pv *kyverno.Pol } return false } + +func joinPatches(patches ...[]byte) []byte { + var result []byte + if patches == nil { + //nothing tot join + return result + } + result = append(result, []byte("[\n")...) + for index, patch := range patches { + result = append(result, patch...) + if index != len(patches)-1 { + result = append(result, []byte(",\n")...) + } + } + result = append(result, []byte("\n]")...) + return result +} diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go index 1f6b93087f..64c5484158 100644 --- a/pkg/webhooks/report.go +++ b/pkg/webhooks/report.go @@ -1,13 +1,16 @@ package webhooks import ( + "fmt" "reflect" "strings" "github.com/nirmata/kyverno/pkg/annotations" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/violation" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/event" @@ -122,7 +125,7 @@ func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation } //generatePolicyViolations generate policyViolation resources for the rules that failed -func generatePolicyViolations(client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { +func generatePolicyViolations(pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { var pvs []kyverno.PolicyViolation for _, policyInfo := range policyInfos { if !policyInfo.IsSuccessful() { @@ -138,15 +141,65 @@ func generatePolicyViolations(client *kyvernoclient.Clientset, policyInfos []inf glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name) // check if there was a previous violation for policy & resource combination - //TODO: check for existing ov using label selectors on resource and policy - // curPv, err:= client.KyvernoV1alpha1().PolicyViolations(). - - // _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv) - // // _, err := client.CreateResource("PolicyViolation", "", &pv, false) - // if err != nil { - // glog.Error(err) - // continue - // } + curPv, err := getExistingPolicyViolationIfAny(pvLister, newPv) + if err != nil { + continue + } + if curPv == nil { + // no existing policy violation, create a new one + _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv) + if err != nil { + glog.Error(err) + } + continue + } + // compare the policyviolation spec for existing resource if present else + if reflect.DeepEqual(curPv.Spec, newPv.Spec) { + // if they are equal there has been no change so dont update the polivy violation + glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec) + continue + } + // spec changed so update the policyviolation + //TODO: wont work, as name is not defined yet + _, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv) + if err != nil { + glog.Error(err) + continue + } } } } + +//TODO: change the name +func getExistingPolicyViolationIfAny(pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { + // TODO: check for existing ov using label selectors on resource and policy + labelMap := map[string]string{"policy": newPv.Spec.Name, "resource": newPv.Spec.ResourceSpec.ToKey()} + ls := &metav1.LabelSelector{} + err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil) + if err != nil { + glog.Errorf("failed to generate label sector of Policy name %s: %v", newPv.Spec.Policy, err) + return nil, err + } + policyViolationSelector, err := metav1.LabelSelectorAsSelector(ls) + if err != nil { + glog.Errorf("invalid label selector: %v", err) + return nil, err + } + + pvs, err := pvLister.List(policyViolationSelector) + if err != nil { + glog.Errorf("unable to list policy violations with label selector %v: %v", policyViolationSelector, err) + return nil, err + } + //TODO: ideally there should be only one policy violation returned + if len(pvs) > 1 { + glog.Errorf("more than one policy violation exists with labels %v", labelMap) + return nil, fmt.Errorf("more than one policy violation exists with labels %v", labelMap) + } + + if len(pvs) == 0 { + glog.Infof("policy violation does not exist with labels %v", labelMap) + return nil, nil + } + return pvs[0], nil +} diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 5ef509f639..cb8507bcc7 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -29,6 +29,7 @@ type WebhookServer struct { client *client.Client kyvernoClient *kyvernoclient.Clientset pLister lister.PolicyLister + pvLister lister.PolicyViolationLister eventGen event.Interface filterK8Resources []utils.K8Resource } @@ -40,6 +41,7 @@ func NewWebhookServer( client *client.Client, tlsPair *tlsutils.TlsPemPair, pInformer informer.PolicyInformer, + pvInormer informer.PolicyViolationInformer, eventGen event.Interface, filterK8Resources string) (*WebhookServer, error) { @@ -58,6 +60,7 @@ func NewWebhookServer( client: client, kyvernoClient: kyvernoClient, pLister: pInformer.Lister(), + pvLister: pvInormer.Lister(), eventGen: eventGen, filterK8Resources: utils.ParseKinds(filterK8Resources), } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 23e733e2eb..7a8978ef4e 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -95,7 +95,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } // ADD POLICY VIOLATIONS - generatePolicyViolations(ws.kyvernoClient, policyInfos) + generatePolicyViolations(ws.pvLister, ws.kyvernoClient, policyInfos) return &v1beta1.AdmissionResponse{ Allowed: true, From 9af6bf9003a8b6174ec4a8115fe91965dbd24020 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 12 Aug 2019 10:02:07 -0700 Subject: [PATCH 16/66] update testing v1 --- definitions/install_debug.yaml | 1 - .../kyverno/v1alpha1/zz_generated.deepcopy.go | 18 ++++++++++++++++++ pkg/policyviolation/controller.go | 2 +- pkg/policyviolation/helpers.go | 1 + pkg/webhooks/report.go | 17 +++++++++++++---- pkg/webhooks/server.go | 5 +++++ pkg/webhooks/validation.go | 2 +- 7 files changed, 39 insertions(+), 7 deletions(-) diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 9aadecd9a5..0d50728fdd 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -218,6 +218,5 @@ spec: required: - name - type - - status - message --- \ No newline at end of file diff --git a/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go b/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go index 72e050cb39..16fa7b7f56 100644 --- a/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go @@ -178,6 +178,7 @@ func (in *PolicyViolation) DeepCopyInto(out *PolicyViolation) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) return } @@ -254,6 +255,23 @@ func (in *PolicyViolationSpec) DeepCopy() *PolicyViolationSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyViolationStatus) DeepCopyInto(out *PolicyViolationStatus) { + *out = *in + in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyViolationStatus. +func (in *PolicyViolationStatus) DeepCopy() *PolicyViolationStatus { + if in == nil { + return nil + } + out := new(PolicyViolationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) { *out = *in diff --git a/pkg/policyviolation/controller.go b/pkg/policyviolation/controller.go index 86922390c9..001e0ebe58 100644 --- a/pkg/policyviolation/controller.go +++ b/pkg/policyviolation/controller.go @@ -84,7 +84,7 @@ func NewPolicyViolationController(client *client.Client, kyvernoClient *kyvernoc pvc.pLister = pInformer.Lister() pvc.pvLister = pvInformer.Lister() pvc.pListerSynced = pInformer.Informer().HasSynced - pvc.pvListerSynced = pInformer.Informer().HasSynced + pvc.pvListerSynced = pvInformer.Informer().HasSynced return &pvc, nil } diff --git a/pkg/policyviolation/helpers.go b/pkg/policyviolation/helpers.go index ee3aa94bf2..cff6e6f0c6 100644 --- a/pkg/policyviolation/helpers.go +++ b/pkg/policyviolation/helpers.go @@ -15,5 +15,6 @@ func BuildPolicyViolation(policy string, resource kyverno.ResourceSpec, fRules [ } //TODO: check if this can be removed or use unstructured? // pv.Kind = "PolicyViolation" + pv.SetGenerateName("pv-") return pv } diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go index 64c5484158..c81f867a03 100644 --- a/pkg/webhooks/report.go +++ b/pkg/webhooks/report.go @@ -16,6 +16,7 @@ import ( "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/policyviolation" + "k8s.io/client-go/tools/cache" ) //TODO: change validation from bool -> enum(validation, mutation) @@ -125,7 +126,7 @@ func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation } //generatePolicyViolations generate policyViolation resources for the rules that failed -func generatePolicyViolations(pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { +func generatePolicyViolations(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { var pvs []kyverno.PolicyViolation for _, policyInfo := range policyInfos { if !policyInfo.IsSuccessful() { @@ -141,7 +142,7 @@ func generatePolicyViolations(pvLister lister.PolicyViolationLister, client *kyv glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name) // check if there was a previous violation for policy & resource combination - curPv, err := getExistingPolicyViolationIfAny(pvLister, newPv) + curPv, err := getExistingPolicyViolationIfAny(pvListerSynced, pvLister, newPv) if err != nil { continue } @@ -171,9 +172,9 @@ func generatePolicyViolations(pvLister lister.PolicyViolationLister, client *kyv } //TODO: change the name -func getExistingPolicyViolationIfAny(pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { +func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { // TODO: check for existing ov using label selectors on resource and policy - labelMap := map[string]string{"policy": newPv.Spec.Name, "resource": newPv.Spec.ResourceSpec.ToKey()} + labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} ls := &metav1.LabelSelector{} err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil) if err != nil { @@ -186,6 +187,14 @@ func getExistingPolicyViolationIfAny(pvLister lister.PolicyViolationLister, newP return nil, err } + //TODO: sync the cache before reading from it ? + // check is this is needed ? + // stopCh := make(chan struct{}, 0) + // if !cache.WaitForCacheSync(stopCh, pvListerSynced) { + // //TODO: can this be handled or avoided ? + // glog.Info("unable to sync policy violation shared informer cache, might be out of sync") + // } + pvs, err := pvLister.List(policyViolationSelector) if err != nil { glog.Errorf("unable to list policy violations with label selector %v: %v", policyViolationSelector, err) diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index cb8507bcc7..265090e977 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -20,6 +20,7 @@ import ( tlsutils "github.com/nirmata/kyverno/pkg/tls" "github.com/nirmata/kyverno/pkg/utils" v1beta1 "k8s.io/api/admission/v1beta1" + "k8s.io/client-go/tools/cache" ) // WebhookServer contains configured TLS server with MutationWebhook. @@ -30,6 +31,8 @@ type WebhookServer struct { kyvernoClient *kyvernoclient.Clientset pLister lister.PolicyLister pvLister lister.PolicyViolationLister + pListerSynced cache.InformerSynced + pvListerSynced cache.InformerSynced eventGen event.Interface filterK8Resources []utils.K8Resource } @@ -61,6 +64,8 @@ func NewWebhookServer( kyvernoClient: kyvernoClient, pLister: pInformer.Lister(), pvLister: pvInormer.Lister(), + pListerSynced: pInformer.Informer().HasSynced, + pvListerSynced: pInformer.Informer().HasSynced, eventGen: eventGen, filterK8Resources: utils.ParseKinds(filterK8Resources), } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 7a8978ef4e..34d628e82d 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -95,7 +95,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } // ADD POLICY VIOLATIONS - generatePolicyViolations(ws.pvLister, ws.kyvernoClient, policyInfos) + generatePolicyViolations(ws.pvListerSynced, ws.pvLister, ws.kyvernoClient, policyInfos) return &v1beta1.AdmissionResponse{ Allowed: true, From 9f0e5d2fe378d205d9de3c247a5c5f6292e40069 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 12 Aug 2019 10:51:17 -0700 Subject: [PATCH 17/66] increase resync time --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 92095d417e..70dc30fd7f 100644 --- a/main.go +++ b/main.go @@ -50,8 +50,8 @@ func main() { // watches CRD resources: // - Policy // - PolicyVolation - // - cache resync time: 10 seconds - pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10) + // - cache resync time: 30 seconds + pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 30) // POLICY CONTROLLER // - reconciliation policy and policy violation From 0d6760cb19006c45472935804a6d3bad7d755101 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 12 Aug 2019 10:52:02 -0700 Subject: [PATCH 18/66] pv controller to clean pv, for state references to resources --- pkg/policyviolation/controller.go | 38 +++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/pkg/policyviolation/controller.go b/pkg/policyviolation/controller.go index 001e0ebe58..3c30229521 100644 --- a/pkg/policyviolation/controller.go +++ b/pkg/policyviolation/controller.go @@ -35,7 +35,11 @@ const ( var controllerKind = kyverno.SchemeGroupVersion.WithKind("PolicyViolation") +// PolicyViolationController manages the policy violation resource +// - sync the lastupdate time +// - check if the resource is active type PolicyViolationController struct { + client *client.Client kyvernoClient *kyvernoclient.Clientset eventRecorder record.EventRecorder syncHandler func(pKey string) error @@ -67,7 +71,7 @@ func NewPolicyViolationController(client *client.Client, kyvernoClient *kyvernoc pvc := PolicyViolationController{ kyvernoClient: kyvernoClient, - + client: client, eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policyviolation_controller"}), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policyviolation"), } @@ -207,10 +211,40 @@ func (pvc *PolicyViolationController) syncPolicyViolation(key string) error { // TODO: Deep-copy only when needed. pv := policyViolation.DeepCopy() // TODO: Update Status to update ObserverdGeneration + // TODO: check if the policy violation refers to a resource thats active ? + // TODO: additional check on deleted webhook for a resource, to delete a policy violation it has a policy violation + // list the resource with label selectors, but this can be expensive for each delete request of a resource + if err := pvc.syncActiveResource(pv); err != nil { + glog.V(4).Infof("not syncing policy violation status") + return err + } return pvc.syncStatusOnly(pv) } +func (pvc *PolicyViolationController) syncActiveResource(curPv *kyverno.PolicyViolation) error { + // check if the resource is active or not ? + rspec := curPv.Spec.ResourceSpec + // get resource + _, err := pvc.client.GetResource(rspec.Kind, rspec.Namespace, rspec.Name) + if errors.IsNotFound(err) { + // TODO: does it help to retry? + // resource is not found + // remove the violation + + if err := pvc.pvControl.RemovePolicyViolation(curPv.Name); err != nil { + glog.Infof("unable to delete the policy violation %s: %v", curPv.Name, err) + return err + } + glog.V(4).Infof("removing policy violation %s as the corresponding resource %s/%s/%s does not exist anymore", curPv.Name, rspec.Kind, rspec.Namespace, rspec.Name) + } + if err != nil { + glog.V(4).Infof("error while retrieved resource %s/%s/%s: %v", rspec.Kind, rspec.Namespace, rspec.Name, err) + return err + } + return nil +} + //syncStatusOnly updates the policyviolation status subresource // status: func (pvc *PolicyViolationController) syncStatusOnly(curPv *kyverno.PolicyViolation) error { @@ -257,5 +291,5 @@ func (r RealPVControl) UpdateStatusPolicyViolation(newPv *kyverno.PolicyViolatio //RemovePolicyViolation removes the policy violation func (r RealPVControl) RemovePolicyViolation(name string) error { - return nil + return r.Client.KyvernoV1alpha1().PolicyViolations().Delete(name, &metav1.DeleteOptions{}) } From cc368b618248182945278dc9273015734cdeb89e Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 13 Aug 2019 09:37:02 -0700 Subject: [PATCH 19/66] existing resource processing v1 --- pkg/policy/controller.go | 9 ++ pkg/policy/existing.go | 236 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 pkg/policy/existing.go diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 8ad0e8b124..9cb1247822 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -14,6 +14,7 @@ import ( informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/utils" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -61,6 +62,10 @@ type PolicyController struct { pListerSynced cache.InformerSynced // pvListerSynced retrns true if the Policy store has been synced at least once pvListerSynced cache.InformerSynced + // Resource manager, manages the mapping for already processed resource + rm resourceManager + // filter the resources defined in the list + filterK8Resources []utils.K8Resource } // NewPolicyController create a new PolicyController @@ -103,6 +108,10 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. pc.pListerSynced = pInformer.Informer().HasSynced pc.pvListerSynced = pInformer.Informer().HasSynced + // resource manager + // rebuild after 300 seconds/ 5 mins + pc.rm = NewResourceManager(300) + return &pc, nil } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go new file mode 100644 index 0000000000..33cf66664b --- /dev/null +++ b/pkg/policy/existing.go @@ -0,0 +1,236 @@ +package policy + +import ( + "sync" + "time" + + "github.com/golang/glog" + "github.com/minio/minio/pkg/wildcard" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/utils" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func (pc *PolicyController) processExistingResources(policy *kyverno.Policy) { + // Parse through all the resources + // drops the cache after configured rebuild time + pc.rm.Drop() + + // get resource that are satisfy the resource description defined in the rules + resourceMap := listResources(pc.client, policy, pc.filterK8Resources) + for _, resource := range resourceMap { + // pre-processing, check if the policy and resource version has been processed before + if !pc.rm.ProcessResource(policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) { + continue + } + // apply the policy on each + glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + // post-processing, register the resource as processed + pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + } +} + +func listResources(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]unstructured.Unstructured { + // key uid + resourceMap := map[string]unstructured.Unstructured{} + + for _, rule := range policy.Spec.Rules { + // resources that match + for _, k := range rule.MatchResources.Kinds { + if kindIsExcluded(k, rule.ExcludeResources.Kinds) { + glog.V(4).Infof("processing policy %s rule %s: kind %s is exluded", policy.Name, rule.Name, k) + continue + } + var namespaces []string + if k == "Namespace" { + // TODO + // this is handled by generator controller + glog.V(4).Infof("skipping processing policy %s rule %s for kind Namespace", policy.Name, rule.Name) + continue + } + //TODO: if namespace is not define can we default to * + if rule.MatchResources.Namespace != "" { + namespaces = append(namespaces, rule.MatchResources.Namespace) + } else { + glog.V(4).Infof("processing policy %s rule %s, namespace not defined, getting all namespaces ", policy.Name, rule.Name) + // get all namespaces + namespaces = getAllNamespaces(client) + } + // check if exclude namespace is not clashing + namespaces = excludeNamespaces(namespaces, rule.ExcludeResources.Namespace) + + // get resources in the namespaces + 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]unstructured.Unstructured { + resourceMap := map[string]unstructured.Unstructured{} + // merge include and exclude label selector values + ls := mergeLabelSectors(rule.MatchResources.Selector, rule.ExcludeResources.Selector) + // list resources + glog.V(4).Infof("get resources for kind %s, namespace %s, selector %v", kind, namespace, rule.MatchResources.Selector) + list, err := client.ListResource(kind, namespace, ls) + if err != nil { + glog.Infof("unable to get resources: err %v", err) + return nil + } + // filter based on name + for _, r := range list.Items { + // match name + if rule.MatchResources.Name != "" { + if !wildcard.Match(rule.MatchResources.Name, r.GetName()) { + glog.V(4).Infof("skipping resource %s/%s due to include condition name=%s mistatch", r.GetNamespace(), r.GetName(), rule.MatchResources.Name) + continue + } + } + // exclude name + if rule.ExcludeResources.Name != "" { + if wildcard.Match(rule.ExcludeResources.Name, r.GetName()) { + glog.V(4).Infof("skipping resource %s/%s due to exclude condition name=%s mistatch", r.GetNamespace(), r.GetName(), rule.MatchResources.Name) + continue + } + } + // Skip the filtered resources + if utils.SkipFilteredResources(r.GetKind(), r.GetNamespace(), r.GetName(), filterK8Resources) { + continue + } + + //TODO check if the group version kind is present or not + resourceMap[string(r.GetUID())] = r + } + return resourceMap +} + +// merge b into a map +func mergeresources(a, b map[string]unstructured.Unstructured) { + for k, v := range b { + a[k] = v + } +} +func mergeLabelSectors(include, exclude *metav1.LabelSelector) *metav1.LabelSelector { + if exclude == nil { + return include + } + // negate the exclude information + // copy the label selector + //TODO: support exclude expressions in exclude + ls := include.DeepCopy() + for k, v := range exclude.MatchLabels { + lsreq := metav1.LabelSelectorRequirement{ + Key: k, + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{v}, + } + ls.MatchExpressions = append(ls.MatchExpressions, lsreq) + } + return ls +} + +func kindIsExcluded(kind string, list []string) bool { + for _, b := range list { + if b == kind { + return true + } + } + return false +} + +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 +} + +func getAllNamespaces(client *client.Client) []string { + var 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 NewResourceManager(rebuildTime int64) *ResourceManager { + rm := ResourceManager{ + data: make(map[string]interface{}), + time: time.Now(), + rebuildTime: rebuildTime, + } + // set time it was built + return &rm +} + +// ResourceManager +type ResourceManager struct { + // we drop and re-build the cache + // based on the memory consumer of by the map + data map[string]interface{} + mux sync.RWMutex + time time.Time + rebuildTime int64 // after how many seconds should we rebuild the cache +} + +type resourceManager interface { + ProcessResource(policy, pv, kind, ns, name, rv string) bool + //TODO removeResource(kind, ns, name string) error + RegisterResource(policy, pv, kind, ns, name, rv string) + // reload + Drop() +} + +//Drop drop the cache after every rebuild interval mins +//TODO: or drop based on the size +func (rm *ResourceManager) Drop() { + if time.Since(rm.time) > time.Duration(rm.rebuildTime)*time.Nanosecond { + rm.mux.Lock() + defer rm.mux.Unlock() + rm.data = map[string]interface{}{} + } +} + +var empty struct{} + +//RegisterResource stores if the policy is processed on this resource version +func (rm *ResourceManager) RegisterResource(policy, pv, kind, ns, name, rv string) { + rm.mux.Lock() + defer rm.mux.Unlock() + // add the resource + key := buildKey(policy, pv, kind, ns, name, rv) + rm.data[key] = empty +} + +//ProcessResource returns true if the policy was not applied on the resource +func (rm *ResourceManager) ProcessResource(policy, pv, kind, ns, name, rv string) bool { + rm.mux.RLock() + defer rm.mux.RUnlock() + + key := buildKey(policy, pv, kind, ns, name, rv) + _, ok := rm.data[key] + return ok == false +} + +func buildKey(policy, pv, kind, ns, name, rv string) string { + return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv +} From 4bf3043a184abd08102c4889956092849084cca2 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 13 Aug 2019 10:03:00 -0700 Subject: [PATCH 20/66] check cache drop for process existing --- pkg/policy/controller.go | 2 ++ pkg/policy/existing.go | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 9cb1247822..5f5df4f107 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -396,6 +396,8 @@ func (pc *PolicyController) syncPolicy(key string) error { if err != nil { return err } + // process policies on existing resources + pc.processExistingResources(p) return pc.syncStatusOnly(p, pvList) } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 33cf66664b..59dfc68e9a 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -23,6 +23,7 @@ func (pc *PolicyController) processExistingResources(policy *kyverno.Policy) { for _, resource := range resourceMap { // pre-processing, check if the policy and resource version has been processed before if !pc.rm.ProcessResource(policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) { + glog.V(4).Infof("policy %s with resource versio %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) continue } // apply the policy on each @@ -203,10 +204,15 @@ type resourceManager interface { //Drop drop the cache after every rebuild interval mins //TODO: or drop based on the size func (rm *ResourceManager) Drop() { - if time.Since(rm.time) > time.Duration(rm.rebuildTime)*time.Nanosecond { + timeSince := time.Since(rm.time) + glog.V(4).Infof("time since last cache reset time %v is %v", rm.time, timeSince) + glog.V(4).Infof("cache rebuild time %v", time.Duration(rm.rebuildTime)*time.Second) + if timeSince > time.Duration(rm.rebuildTime)*time.Second { rm.mux.Lock() defer rm.mux.Unlock() rm.data = map[string]interface{}{} + rm.time = time.Now() + glog.V(4).Infof("dropping cache at time %v", rm.time) } } From e7b538be794eb33fe94f3516ac4616b301fb827b Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 13 Aug 2019 11:32:12 -0700 Subject: [PATCH 21/66] add process existing for mutation & validation + come cleanup --- pkg/annotations/annotations.go | 2 +- pkg/engine/engine.go | 174 ++++++++++++++++----------------- pkg/engine/generation.go | 4 +- pkg/engine/mutation.go | 17 ++-- pkg/engine/overlay.go | 2 +- pkg/engine/patches.go | 12 ++- pkg/engine/utils.go | 4 +- pkg/engine/validation.go | 28 +++--- pkg/info/info.go | 10 +- pkg/policy/apply.go | 93 ++++++++++++++++++ pkg/policy/controller.go | 2 +- pkg/policy/existing.go | 17 +++- pkg/utils/json.go | 18 ++++ pkg/utils/util.go | 14 +++ pkg/webhooks/mutation.go | 11 ++- pkg/webhooks/validation.go | 2 +- 16 files changed, 279 insertions(+), 131 deletions(-) create mode 100644 pkg/policy/apply.go diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go index 46725b8cc2..be48530ae7 100644 --- a/pkg/annotations/annotations.go +++ b/pkg/annotations/annotations.go @@ -48,7 +48,7 @@ func (p *Policy) getOverAllStatus() string { return "Success" } -func getRules(rules []*pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule { +func getRules(rules []pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule { if len(rules) == 0 { return nil } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 3befc6f3bf..93ca34e1b5 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -1,107 +1,105 @@ package engine import ( - jsonpatch "github.com/evanphx/json-patch" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // ProcessExisting checks for mutation and validation violations of existing resources func ProcessExisting(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) []info.PolicyInfo { - glog.Infof("Applying policy %s on existing resources", policy.Name) - // key uid - resourceMap := ListResourcesThatApplyToPolicy(client, policy, filterK8Resources) - policyInfos := []info.PolicyInfo{} - // for the filtered resource apply policy - for _, v := range resourceMap { + return nil + // glog.Infof("Applying policy %s on existing resources", policy.Name) + // // key uid + // resourceMap := ListResourcesThatApplyToPolicy(client, policy, filterK8Resources) + // policyInfos := []info.PolicyInfo{} + // // for the filtered resource apply policy + // for _, v := range resourceMap { - policyInfo, err := applyPolicy(client, policy, v) - if err != nil { - glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.Resource.GetName(), v.Resource.GetNamespace()) - glog.Error(err) - continue - } - policyInfos = append(policyInfos, policyInfo) - } + // policyInfo, err := applyPolicy(client, policy, v) + // if err != nil { + // glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.Resource.GetName(), v.Resource.GetNamespace()) + // glog.Error(err) + // continue + // } + // policyInfos = append(policyInfos, policyInfo) + // } - return policyInfos + // return policyInfos } -func applyPolicy(client *client.Client, policy *kyverno.Policy, res resourceInfo) (info.PolicyInfo, error) { - var policyInfo info.PolicyInfo - glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - rawResource, err := res.Resource.MarshalJSON() - if err != nil { - return policyInfo, err - } - // Mutate - mruleInfos, err := mutation(policy, rawResource, res.Gvk) - policyInfo.AddRuleInfos(mruleInfos) - if err != nil { - return policyInfo, err - } - // Validation - //TODO check by value or pointer - vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) - if err != nil { - return policyInfo, err - } - policyInfo.AddRuleInfos(vruleInfos) - policyInfo = info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction) +// func applyPolicy(client *client.Client, policy *kyverno.Policy, res resourceInfo) (info.PolicyInfo, error) { +// var policyInfo info.PolicyInfo +// glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) +// rawResource, err := res.Resource.MarshalJSON() +// if err != nil { +// return policyInfo, err +// } +// // Mutate +// mruleInfos, err := mutation(policy, rawResource, res.Gvk) +// policyInfo.AddRuleInfos(mruleInfos) +// if err != nil { +// return policyInfo, err +// } +// // Validation +// //TODO check by value or pointer +// vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) +// if err != nil { +// return policyInfo, err +// } +// policyInfo.AddRuleInfos(vruleInfos) +// policyInfo = info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction) - if res.Gvk.Kind == "Namespace" { +// if res.Gvk.Kind == "Namespace" { - // Generation - gruleInfos := Generate(client, policy, res.Resource) - policyInfo.AddRuleInfos(gruleInfos) - } +// // Generation +// gruleInfos := Generate(client, policy, res.Resource) +// policyInfo.AddRuleInfos(gruleInfos) +// } - return policyInfo, nil -} +// return policyInfo, nil +// } -func mutation(p *kyverno.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { - patches, ruleInfos := Mutate(*p, rawResource, *gvk) - if len(ruleInfos) == 0 { - // no rules were processed - return nil, nil - } - // if there are any errors return - for _, r := range ruleInfos { - if !r.IsSuccessful() { - return ruleInfos, nil - } - } - // if there are no patches // for overlay - if len(patches) == 0 { - return ruleInfos, nil - } - // option 2: (original Resource + patch) compare with (original resource) - mergePatches := JoinPatches(patches) - // merge the patches - patch, err := jsonpatch.DecodePatch(mergePatches) - if err != nil { - return nil, err - } - // apply the patches returned by mutate to the original resource - patchedResource, err := patch.Apply(rawResource) - if err != nil { - return nil, err - } - // compare (original Resource + patch) vs (original resource) - // to verify if they are equal - ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation) - if !jsonpatch.Equal(patchedResource, rawResource) { - //resource does not match so there was a mutation rule violated - // TODO : check the rule name "mutation rules" - ruleInfo.Fail() - ruleInfo.Add("resource does not satisfy mutation rules") - } else { - ruleInfo.Add("resource satisfys the mutation rule") - } - ruleInfos = append(ruleInfos, ruleInfo) - return ruleInfos, nil -} +// func mutation(p *kyverno.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +// patches, ruleInfos := Mutate(*p, rawResource, *gvk) +// if len(ruleInfos) == 0 { +// // no rules were processed +// return nil, nil +// } +// // if there are any errors return +// for _, r := range ruleInfos { +// if !r.IsSuccessful() { +// return ruleInfos, nil +// } +// } +// // if there are no patches // for overlay +// if len(patches) == 0 { +// return ruleInfos, nil +// } +// // option 2: (original Resource + patch) compare with (original resource) +// mergePatches := JoinPatches(patches) +// // merge the patches +// patch, err := jsonpatch.DecodePatch(mergePatches) +// if err != nil { +// return nil, err +// } +// // apply the patches returned by mutate to the original resource +// patchedResource, err := patch.Apply(rawResource) +// if err != nil { +// return nil, err +// } +// // compare (original Resource + patch) vs (original resource) +// // to verify if they are equal +// ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation) +// if !jsonpatch.Equal(patchedResource, rawResource) { +// //resource does not match so there was a mutation rule violated +// // TODO : check the rule name "mutation rules" +// ruleInfo.Fail() +// ruleInfo.Add("resource does not satisfy mutation rules") +// } else { +// ruleInfo.Add("resource satisfys the mutation rule") +// } +// ruleInfos = append(ruleInfos, ruleInfo) +// return ruleInfos, nil +// } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 891682be3e..52076832b7 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -14,8 +14,8 @@ import ( ) //Generate apply generation rules on a resource -func Generate(client *client.Client, policy *kyverno.Policy, ns unstructured.Unstructured) []*info.RuleInfo { - ris := []*info.RuleInfo{} +func Generate(client *client.Client, policy *kyverno.Policy, ns unstructured.Unstructured) []info.RuleInfo { + ris := []info.RuleInfo{} for _, rule := range policy.Spec.Rules { if rule.Generation == (kyverno.Generation{}) { continue diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 8445bd8b97..392913ee61 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -6,20 +6,15 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Mutate performs mutation. Overlay first and then mutation patches //TODO: check if gvk needs to be passed or can be set in resource -func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []*info.RuleInfo) { +func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte, []info.RuleInfo) { //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information - //TODO: pass unstructured instead of rawResource ? - resource, err := convertToUnstructured(rawResource) - if err != nil { - glog.Errorf("unable to convert raw resource to unstructured: %v", err) - } var patches [][]byte - var ruleInfos []*info.RuleInfo + var ruleInfos []info.RuleInfo for _, rule := range policy.Spec.Rules { if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) { @@ -29,7 +24,7 @@ func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKi // check if the resource satisfies the filter conditions defined in the rule //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that // dont statisfy a policy rule resource description - ok := MatchesResourceDescription(resource, rule, gvk) + ok := MatchesResourceDescription(resource, rule) if !ok { glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName()) continue @@ -39,7 +34,7 @@ func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKi // Process Overlay if rule.Mutation.Overlay != nil { - oPatches, err := processOverlay(resource, rule, rawResource) + oPatches, err := processOverlay(resource, rule) if err == nil { if len(oPatches) == 0 { // if array elements dont match then we skip(nil patch, no error) @@ -66,7 +61,7 @@ func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKi // Process Patches if len(rule.Mutation.Patches) != 0 { - jsonPatches, errs := processPatches(rule, rawResource) + jsonPatches, errs := processPatches(resource, rule) if len(errs) > 0 { ruleInfo.Fail() for _, err := range errs { diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index 27118f5530..1ae274bb8c 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -17,7 +17,7 @@ import ( // ProcessOverlay handles validating admission request // Checks the target resources for rules defined in the policy -func processOverlay(resourceUnstr *unstructured.Unstructured, rule kyverno.Rule, rawResource []byte) ([][]byte, error) { +func processOverlay(resourceUnstr unstructured.Unstructured, rule kyverno.Rule) ([][]byte, error) { //TODO check if there is better solution resourceRaw, err := resourceUnstr.MarshalJSON() diff --git a/pkg/engine/patches.go b/pkg/engine/patches.go index b13c0971a3..0debccdfe1 100644 --- a/pkg/engine/patches.go +++ b/pkg/engine/patches.go @@ -3,9 +3,11 @@ package engine import ( "encoding/json" "errors" + "fmt" "reflect" "github.com/golang/glog" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" jsonpatch "github.com/evanphx/json-patch" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" @@ -13,7 +15,15 @@ import ( // ProcessPatches Returns array from separate patches that can be applied to the document // Returns error ONLY in case when creation of resource should be denied. -func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, errs []error) { +func processPatches(resourceUnstr unstructured.Unstructured, rule kyverno.Rule) (allPatches [][]byte, errs []error) { + //TODO check if there is better solution + resource, err := resourceUnstr.MarshalJSON() + if err != nil { + glog.V(4).Infof("unable to marshal resource : %v", err) + errs = append(errs, fmt.Errorf("unable to marshal resource : %v", err)) + return nil, errs + } + if len(resource) == 0 { errs = append(errs, errors.New("Source document for patching is empty")) return nil, errs diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index cf80c5b40c..6e18124d8f 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -150,11 +150,11 @@ func excludeNamespaces(namespaces []string, excludeNs string) []string { } //MatchesResourceDescription checks if the resource matches resource desription of the rule or not -func MatchesResourceDescription(resource *unstructured.Unstructured, rule kyverno.Rule, gvk metav1.GroupVersionKind) bool { +func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno.Rule) bool { matches := rule.MatchResources.ResourceDescription exclude := rule.ExcludeResources.ResourceDescription - if !findKind(matches.Kinds, gvk.Kind) { + if !findKind(matches.Kinds, resource.GetKind()) { return false } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index b3edc32712..d850820e3c 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -12,30 +12,30 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Validate handles validating admission request // Checks the target resources for rules defined in the policy -func Validate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) { //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information //TODO: pass unstructured instead of rawResource ? - resourceUnstr, err := convertToUnstructured(rawResource) - if err != nil { - glog.Errorf("unable to convert raw resource to unstructured: %v", err) - } - resourceRaw, err := resourceUnstr.MarshalJSON() + // resourceUnstr, err := convertToUnstructured(rawResource) + // if err != nil { + // glog.Errorf("unable to convert raw resource to unstructured: %v", err) + // } + resourceRaw, err := resource.MarshalJSON() if err != nil { glog.V(4).Infof("unable to marshal resource : %v", err) return nil, err } - var resource interface{} - if err := json.Unmarshal(resourceRaw, &resource); err != nil { + var resourceInt interface{} + if err := json.Unmarshal(resourceRaw, &resourceInt); err != nil { glog.V(4).Infof("unable to unmarshal resource : %v", err) return nil, err } - var ruleInfos []*info.RuleInfo + var ruleInfos []info.RuleInfo for _, rule := range policy.Spec.Rules { if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) { @@ -45,21 +45,21 @@ func Validate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersion // check if the resource satisfies the filter conditions defined in the rule //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that // dont statisfy a policy rule resource description - ok := MatchesResourceDescription(resourceUnstr, rule, gvk) + ok := MatchesResourceDescription(resource, rule) if !ok { - glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resourceUnstr.GetNamespace(), resourceUnstr.GetName()) + glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName()) continue } ruleInfo := info.NewRuleInfo(rule.Name, info.Validation) - err := validateResourceWithPattern(resource, rule.Validation.Pattern) + err := validateResourceWithPattern(resourceInt, rule.Validation.Pattern) if err != nil { ruleInfo.Fail() ruleInfo.Addf("Failed to apply pattern: %v.", err) } else { ruleInfo.Add("Pattern succesfully validated") - glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resourceUnstr.GetNamespace(), resourceUnstr.GetName()) + glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) } ruleInfos = append(ruleInfos, ruleInfo) } diff --git a/pkg/info/info.go b/pkg/info/info.go index 65f43167ac..1118aa4c4e 100644 --- a/pkg/info/info.go +++ b/pkg/info/info.go @@ -20,7 +20,7 @@ type PolicyInfo struct { RNamespace string //TODO: add check/enum for types ValidationFailureAction string // BlockChanges, ReportViolation - Rules []*RuleInfo + Rules []RuleInfo success bool } @@ -135,8 +135,8 @@ func (ri *RuleInfo) GetErrorString() string { } //NewRuleInfo creates a new RuleInfo -func NewRuleInfo(ruleName string, ruleType RuleType) *RuleInfo { - return &RuleInfo{ +func NewRuleInfo(ruleName string, ruleType RuleType) RuleInfo { + return RuleInfo{ Name: ruleName, Msgs: []string{}, RuleType: ruleType, @@ -165,7 +165,7 @@ func (ri *RuleInfo) Addf(msg string, args ...interface{}) { } //RulesSuccesfuly check if the any rule has failed or not -func RulesSuccesfuly(rules []*RuleInfo) bool { +func RulesSuccesfuly(rules []RuleInfo) bool { for _, r := range rules { if !r.success { return false @@ -175,7 +175,7 @@ func RulesSuccesfuly(rules []*RuleInfo) bool { } //AddRuleInfos sets the rule information -func (pi *PolicyInfo) AddRuleInfos(rules []*RuleInfo) { +func (pi *PolicyInfo) AddRuleInfos(rules []RuleInfo) { if rules == nil { return } diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go new file mode 100644 index 0000000000..c07358d46c --- /dev/null +++ b/pkg/policy/apply.go @@ -0,0 +1,93 @@ +package policy + +import ( + "time" + + jsonpatch "github.com/evanphx/json-patch" + "github.com/golang/glog" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/engine" + "github.com/nirmata/kyverno/pkg/info" + "github.com/nirmata/kyverno/pkg/utils" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// applyPolicy applies policy on a resource +//TODO: generation rules +func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (info.PolicyInfo, error) { + startTime := time.Now() + glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime) + defer func() { + glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime)) + }() + // glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + var policyInfo info.PolicyInfo + //MUTATION + mruleInfos, err := mutation(policy, resource) + policyInfo.AddRuleInfos(mruleInfos) + if err != nil { + return policyInfo, err + } + + //VALIDATION + vruleInfos, err := engine.Validate(policy, resource) + policyInfo.AddRuleInfos(vruleInfos) + if err != nil { + return policyInfo, err + } + + //TODO: GENERATION + return policyInfo, nil +} + +func mutation(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) { + patches, ruleInfos := engine.Mutate(policy, resource) + if len(ruleInfos) == 0 { + //no rules processed + return nil, nil + } + + for _, r := range ruleInfos { + if !r.IsSuccessful() { + // no failures while processing rule + return ruleInfos, nil + } + } + if len(patches) == 0 { + // no patches for the resources + // either there were failures or the overlay already was satisfied + return ruleInfos, nil + } + + // (original resource + patch) == (original resource) + mergePatches := utils.JoinPatches(patches) + patch, err := jsonpatch.DecodePatch(mergePatches) + if err != nil { + return nil, err + } + rawResource, err := resource.MarshalJSON() + if err != nil { + glog.V(4).Infof("unable to marshal resource : %v", err) + return nil, err + } + + // apply the patches returned by mutate to the original resource + patchedResource, err := patch.Apply(rawResource) + if err != nil { + return nil, err + } + //TODO: this will be removed after the support for patching for each rule + ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation) + + if !jsonpatch.Equal(patchedResource, rawResource) { + //resource does not match so there was a mutation rule violated + // TODO : check the rule name "mutation rules" + ruleInfo.Fail() + ruleInfo.Add("resource does not satisfy mutation rules") + } else { + ruleInfo.Add("resource satisfys the mutation rule") + } + + ruleInfos = append(ruleInfos, ruleInfo) + return ruleInfos, nil +} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 5f5df4f107..8cee3e168d 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -397,7 +397,7 @@ func (pc *PolicyController) syncPolicy(key string) error { return err } // process policies on existing resources - pc.processExistingResources(p) + pc.processExistingResources(*p) return pc.syncStatusOnly(p, pvList) } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 59dfc68e9a..0879a306da 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -8,12 +8,13 @@ import ( "github.com/minio/minio/pkg/wildcard" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func (pc *PolicyController) processExistingResources(policy *kyverno.Policy) { +func (pc *PolicyController) processExistingResources(policy kyverno.Policy) { // Parse through all the resources // drops the cache after configured rebuild time pc.rm.Drop() @@ -23,17 +24,27 @@ func (pc *PolicyController) processExistingResources(policy *kyverno.Policy) { for _, resource := range resourceMap { // pre-processing, check if the policy and resource version has been processed before if !pc.rm.ProcessResource(policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) { - glog.V(4).Infof("policy %s with resource versio %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + glog.V(4).Infof("policy %s with resource version %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) continue } // apply the policy on each glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + applyPolicyOnResource(policy, resource) // post-processing, register the resource as processed pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) } } -func listResources(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]unstructured.Unstructured { +func applyPolicyOnResource(policy kyverno.Policy, resource unstructured.Unstructured) *info.PolicyInfo { + policyInfo, err := applyPolicy(policy, resource) + if err != nil { + glog.V(4).Infof("failed to process policy %s on resource %s/%s/%s: %v", policy.GetName(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) + return nil + } + return &policyInfo +} + +func listResources(client *client.Client, policy kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]unstructured.Unstructured { // key uid resourceMap := map[string]unstructured.Unstructured{} diff --git a/pkg/utils/json.go b/pkg/utils/json.go index c098307737..5542d2275c 100644 --- a/pkg/utils/json.go +++ b/pkg/utils/json.go @@ -187,3 +187,21 @@ func subsetSlice(a, b []interface{}) bool { } return true } + +// JoinPatches joins array of serialized JSON patches to the single JSONPatch array +func JoinPatches(patches [][]byte) []byte { + var result []byte + if len(patches) == 0 { + return result + } + + result = append(result, []byte("[\n")...) + for index, patch := range patches { + result = append(result, patch...) + if index != len(patches)-1 { + result = append(result, []byte(",\n")...) + } + } + result = append(result, []byte("\n]")...) + return result +} diff --git a/pkg/utils/util.go b/pkg/utils/util.go index fe6c95e538..19b728babd 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -4,8 +4,11 @@ import ( "regexp" "strings" + "github.com/golang/glog" + "github.com/minio/minio/pkg/wildcard" "k8s.io/api/admission/v1beta1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func Contains(list []string, element string) bool { @@ -74,3 +77,14 @@ func ParseKinds(list string) []K8Resource { } return resources } + +//ConvertToUnstructured coverts a raw resource into unstructured struct +func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) { + resource := &unstructured.Unstructured{} + err := resource.UnmarshalJSON(data) + if err != nil { + glog.V(4).Infof("failed to unmarshall resource: %v", err) + return nil, err + } + return resource, nil +} diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 99447f85e6..9fc6f83540 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -51,8 +51,17 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation) glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) + // resource, err := utils.ConvertToUnstructured(request.Object.Raw) + // if err != nil { + // glog.Errorf("unable to process policy %s resource %v: %v", policy.GetName(), request.Resource, err) + // continue + // } + //TODO: check if the GVK information is present in the request of we set it explicity here ? + glog.V(4).Infof("GVK is %v", resource.GroupVersionKind()) + // resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind}) //TODO: passing policy value as we dont wont to modify the policy - policyPatches, ruleInfos := engine.Mutate(*policy, request.Object.Raw, request.Kind) + + policyPatches, ruleInfos := engine.Mutate(*policy, *resource) policyInfo.AddRuleInfos(ruleInfos) policyInfos = append(policyInfos, policyInfo) if !policyInfo.IsSuccessful() { diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 34d628e82d..51514c8740 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -52,7 +52,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - ruleInfos, err := engine.Validate(*policy, request.Object.Raw, request.Kind) + ruleInfos, err := engine.Validate(*policy, *resource) if err != nil { // This is not policy error // but if unable to parse request raw resource From c04a935300cfb8f845199efbd7641c43a372d113 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 13 Aug 2019 13:15:04 -0700 Subject: [PATCH 22/66] existing resource reporting --- main.go | 9 +- pkg/policy/controller.go | 10 +- pkg/policy/existing.go | 8 +- pkg/policy/report.go | 60 +++++++++ pkg/policyviolation/helpers.go | 122 +++++++++++++++++++ pkg/webhooks/report.go | 215 ++++++++++++++++----------------- pkg/webhooks/validation.go | 3 +- 7 files changed, 304 insertions(+), 123 deletions(-) create mode 100644 pkg/policy/report.go diff --git a/main.go b/main.go index 70dc30fd7f..8afe006da9 100644 --- a/main.go +++ b/main.go @@ -52,11 +52,14 @@ func main() { // - PolicyVolation // - cache resync time: 30 seconds pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 30) + // EVENT GENERATOR + // - generate event with retry + egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies()) // POLICY CONTROLLER // - reconciliation policy and policy violation // - status: violation count - pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) + pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) if err != nil { glog.Fatalf("error creating policy controller: %v\n", err) } @@ -68,10 +71,6 @@ func main() { glog.Fatalf("error creating policy violation controller: %v\n", err) } - // EVENT GENERATOR - // - generate event with retry - egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies()) - // TODO : Process Existing tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 8cee3e168d..26fc4401b9 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -14,6 +14,7 @@ import ( informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/utils" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -46,6 +47,7 @@ var controllerKind = kyverno.SchemeGroupVersion.WithKind("Policy") type PolicyController struct { client *client.Client kyvernoClient *kyvernoclient.Clientset + eventGen event.Interface eventRecorder record.EventRecorder syncHandler func(pKey string) error enqueuePolicy func(policy *kyverno.Policy) @@ -69,7 +71,7 @@ type PolicyController struct { } // NewPolicyController create a new PolicyController -func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer informer.PolicyInformer, pvInformer informer.PolicyViolationInformer) (*PolicyController, error) { +func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer informer.PolicyInformer, pvInformer informer.PolicyViolationInformer, eventGen event.Interface) (*PolicyController, error) { // Event broad caster eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) @@ -82,6 +84,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. pc := PolicyController{ client: client, kyvernoClient: kyvernoClient, + eventGen: eventGen, eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policy_controller"}), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policy"), } @@ -397,8 +400,9 @@ func (pc *PolicyController) syncPolicy(key string) error { return err } // process policies on existing resources - pc.processExistingResources(*p) - + policyInfos := pc.processExistingResources(*p) + // report errors + pc.report(policyInfos) return pc.syncStatusOnly(p, pvList) } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 0879a306da..629ad0fb58 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -14,11 +14,11 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func (pc *PolicyController) processExistingResources(policy kyverno.Policy) { +func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []info.PolicyInfo { // Parse through all the resources // drops the cache after configured rebuild time pc.rm.Drop() - + var policyInfos []info.PolicyInfo // get resource that are satisfy the resource description defined in the rules resourceMap := listResources(pc.client, policy, pc.filterK8Resources) for _, resource := range resourceMap { @@ -29,10 +29,12 @@ func (pc *PolicyController) processExistingResources(policy kyverno.Policy) { } // apply the policy on each glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) - applyPolicyOnResource(policy, resource) + policyInfo := applyPolicyOnResource(policy, resource) + policyInfos = append(policyInfos, *policyInfo) // post-processing, register the resource as processed pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) } + return policyInfos } func applyPolicyOnResource(policy kyverno.Policy, resource unstructured.Unstructured) *info.PolicyInfo { diff --git a/pkg/policy/report.go b/pkg/policy/report.go new file mode 100644 index 0000000000..c232494cbc --- /dev/null +++ b/pkg/policy/report.go @@ -0,0 +1,60 @@ +package policy + +import ( + "fmt" + + "github.com/nirmata/kyverno/pkg/event" + "github.com/nirmata/kyverno/pkg/info" + "github.com/nirmata/kyverno/pkg/policyviolation" +) + +func (pc *PolicyController) report(policyInfos []info.PolicyInfo) { + // generate events + // generate policy violations + + for _, policyInfo := range policyInfos { + // events + // success - policy applied on resource + // failure - policy/rule failed to apply on the resource + reportPolicy(policyInfo, pc.eventGen) + // policy violations + // failure - policy/rule failed to apply on the resource + } + + // generate policy violation + policyviolation.GeneratePolicyViolations(pc.pvListerSynced, pc.pvLister, pc.kyvernoClient, policyInfos) + +} + +func reportPolicy(policyInfo info.PolicyInfo, eventGen event.Interface) { + + if policyInfo.IsSuccessful() { + return + } + + for _, rule := range policyInfo.Rules { + if rule.IsSuccessful() { + continue + } + + // generate event on resource for each failed rule + e := &event.Info{} + e.Kind = policyInfo.RKind + e.Namespace = policyInfo.RNamespace + e.Name = policyInfo.RName + e.Reason = "Failure" + e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", policyInfo.Name, rule.RuleType.String(), rule.Name, rule.GetErrorString()) + eventGen.Add(e) + + // generate policy violation for each failed rule + + } + // generate a event on policy for all failed rules + e := &event.Info{} + e.Kind = "Policy" + e.Namespace = "" + e.Name = policyInfo.Name + e.Reason = "Failure" + e.Message = fmt.Sprintf("failed to apply rules %s on resource %s/%s/%s", policyInfo.FailedRules(), policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName) + eventGen.Add(e) +} diff --git a/pkg/policyviolation/helpers.go b/pkg/policyviolation/helpers.go index cff6e6f0c6..ae62e87b5a 100644 --- a/pkg/policyviolation/helpers.go +++ b/pkg/policyviolation/helpers.go @@ -1,7 +1,16 @@ package policyviolation import ( + "fmt" + "reflect" + + "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/info" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/cache" ) //BuildPolicyViolation returns an value of type PolicyViolation @@ -18,3 +27,116 @@ func BuildPolicyViolation(policy string, resource kyverno.ResourceSpec, fRules [ pv.SetGenerateName("pv-") return pv } + +// buildPolicyViolationsForAPolicy returns a policy violation object if there are any rules that fail +func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation { + var fRules []kyverno.ViolatedRule + var pv kyverno.PolicyViolation + for _, r := range pi.Rules { + if !r.IsSuccessful() { + fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()}) + } + } + if len(fRules) > 0 { + glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName) + // there is an error + pv = BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{ + Kind: pi.RKind, + Namespace: pi.RNamespace, + Name: pi.RName, + }, + fRules, + ) + + } + return pv +} + +//generatePolicyViolations generate policyViolation resources for the rules that failed +//TODO: check if pvListerSynced is needed +func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { + var pvs []kyverno.PolicyViolation + for _, policyInfo := range policyInfos { + if !policyInfo.IsSuccessful() { + if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) { + pvs = append(pvs, pv) + } + } + } + + if len(pvs) > 0 { + for _, newPv := range pvs { + // generate PolicyViolation objects + glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name) + + // check if there was a previous violation for policy & resource combination + curPv, err := getExistingPolicyViolationIfAny(pvListerSynced, pvLister, newPv) + if err != nil { + continue + } + if curPv == nil { + // no existing policy violation, create a new one + _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv) + if err != nil { + glog.Error(err) + } + continue + } + // compare the policyviolation spec for existing resource if present else + if reflect.DeepEqual(curPv.Spec, newPv.Spec) { + // if they are equal there has been no change so dont update the polivy violation + glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec) + continue + } + // spec changed so update the policyviolation + //TODO: wont work, as name is not defined yet + _, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv) + if err != nil { + glog.Error(err) + continue + } + } + } +} + +//TODO: change the name +func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { + // TODO: check for existing ov using label selectors on resource and policy + labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} + ls := &metav1.LabelSelector{} + err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil) + if err != nil { + glog.Errorf("failed to generate label sector of Policy name %s: %v", newPv.Spec.Policy, err) + return nil, err + } + policyViolationSelector, err := metav1.LabelSelectorAsSelector(ls) + if err != nil { + glog.Errorf("invalid label selector: %v", err) + return nil, err + } + + //TODO: sync the cache before reading from it ? + // check is this is needed ? + // stopCh := make(chan struct{}, 0) + // if !cache.WaitForCacheSync(stopCh, pvListerSynced) { + // //TODO: can this be handled or avoided ? + // glog.Info("unable to sync policy violation shared informer cache, might be out of sync") + // } + + pvs, err := pvLister.List(policyViolationSelector) + if err != nil { + glog.Errorf("unable to list policy violations with label selector %v: %v", policyViolationSelector, err) + return nil, err + } + //TODO: ideally there should be only one policy violation returned + if len(pvs) > 1 { + glog.Errorf("more than one policy violation exists with labels %v", labelMap) + return nil, fmt.Errorf("more than one policy violation exists with labels %v", labelMap) + } + + if len(pvs) == 0 { + glog.Infof("policy violation does not exist with labels %v", labelMap) + return nil, nil + } + return pvs[0], nil +} diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go index c81f867a03..d9b8042834 100644 --- a/pkg/webhooks/report.go +++ b/pkg/webhooks/report.go @@ -1,22 +1,14 @@ package webhooks import ( - "fmt" - "reflect" "strings" "github.com/nirmata/kyverno/pkg/annotations" - kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/violation" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/info" - "github.com/nirmata/kyverno/pkg/policyviolation" - "k8s.io/client-go/tools/cache" ) //TODO: change validation from bool -> enum(validation, mutation) @@ -101,114 +93,115 @@ func buildAnnotation(mAnn map[string]string, pi *info.PolicyInfo) { } } -// buildPolicyViolationsForAPolicy returns a policy violation object if there are any rules that fail -func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation { - var fRules []kyverno.ViolatedRule - var pv kyverno.PolicyViolation - for _, r := range pi.Rules { - if !r.IsSuccessful() { - fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()}) - } - } - if len(fRules) > 0 { - glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName) - // there is an error - pv = policyviolation.BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{ - Kind: pi.RKind, - Namespace: pi.RNamespace, - Name: pi.RName, - }, - fRules, - ) +// // buildPolicyViolationsForAPolicy returns a policy violation object if there are any rules that fail +// func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation { +// var fRules []kyverno.ViolatedRule +// var pv kyverno.PolicyViolation +// for _, r := range pi.Rules { +// if !r.IsSuccessful() { +// fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()}) +// } +// } +// if len(fRules) > 0 { +// glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName) +// // there is an error +// pv = policyviolation.BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{ +// Kind: pi.RKind, +// Namespace: pi.RNamespace, +// Name: pi.RName, +// }, +// fRules, +// ) - } - return pv -} +// } +// return pv +// } -//generatePolicyViolations generate policyViolation resources for the rules that failed -func generatePolicyViolations(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { - var pvs []kyverno.PolicyViolation - for _, policyInfo := range policyInfos { - if !policyInfo.IsSuccessful() { - if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) { - pvs = append(pvs, pv) - } - } - } +// //generatePolicyViolations generate policyViolation resources for the rules that failed +// //TODO: check if pvListerSynced is needed +// func generatePolicyViolations(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { +// var pvs []kyverno.PolicyViolation +// for _, policyInfo := range policyInfos { +// if !policyInfo.IsSuccessful() { +// if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) { +// pvs = append(pvs, pv) +// } +// } +// } - if len(pvs) > 0 { - for _, newPv := range pvs { - // generate PolicyViolation objects - glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name) +// if len(pvs) > 0 { +// for _, newPv := range pvs { +// // generate PolicyViolation objects +// glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name) - // check if there was a previous violation for policy & resource combination - curPv, err := getExistingPolicyViolationIfAny(pvListerSynced, pvLister, newPv) - if err != nil { - continue - } - if curPv == nil { - // no existing policy violation, create a new one - _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv) - if err != nil { - glog.Error(err) - } - continue - } - // compare the policyviolation spec for existing resource if present else - if reflect.DeepEqual(curPv.Spec, newPv.Spec) { - // if they are equal there has been no change so dont update the polivy violation - glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec) - continue - } - // spec changed so update the policyviolation - //TODO: wont work, as name is not defined yet - _, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv) - if err != nil { - glog.Error(err) - continue - } - } - } -} +// // check if there was a previous violation for policy & resource combination +// curPv, err := getExistingPolicyViolationIfAny(pvListerSynced, pvLister, newPv) +// if err != nil { +// continue +// } +// if curPv == nil { +// // no existing policy violation, create a new one +// _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv) +// if err != nil { +// glog.Error(err) +// } +// continue +// } +// // compare the policyviolation spec for existing resource if present else +// if reflect.DeepEqual(curPv.Spec, newPv.Spec) { +// // if they are equal there has been no change so dont update the polivy violation +// glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec) +// continue +// } +// // spec changed so update the policyviolation +// //TODO: wont work, as name is not defined yet +// _, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv) +// if err != nil { +// glog.Error(err) +// continue +// } +// } +// } +// } -//TODO: change the name -func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { - // TODO: check for existing ov using label selectors on resource and policy - labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} - ls := &metav1.LabelSelector{} - err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil) - if err != nil { - glog.Errorf("failed to generate label sector of Policy name %s: %v", newPv.Spec.Policy, err) - return nil, err - } - policyViolationSelector, err := metav1.LabelSelectorAsSelector(ls) - if err != nil { - glog.Errorf("invalid label selector: %v", err) - return nil, err - } +// //TODO: change the name +// func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { +// // TODO: check for existing ov using label selectors on resource and policy +// labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} +// ls := &metav1.LabelSelector{} +// err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil) +// if err != nil { +// glog.Errorf("failed to generate label sector of Policy name %s: %v", newPv.Spec.Policy, err) +// return nil, err +// } +// policyViolationSelector, err := metav1.LabelSelectorAsSelector(ls) +// if err != nil { +// glog.Errorf("invalid label selector: %v", err) +// return nil, err +// } - //TODO: sync the cache before reading from it ? - // check is this is needed ? - // stopCh := make(chan struct{}, 0) - // if !cache.WaitForCacheSync(stopCh, pvListerSynced) { - // //TODO: can this be handled or avoided ? - // glog.Info("unable to sync policy violation shared informer cache, might be out of sync") - // } +// //TODO: sync the cache before reading from it ? +// // check is this is needed ? +// // stopCh := make(chan struct{}, 0) +// // if !cache.WaitForCacheSync(stopCh, pvListerSynced) { +// // //TODO: can this be handled or avoided ? +// // glog.Info("unable to sync policy violation shared informer cache, might be out of sync") +// // } - pvs, err := pvLister.List(policyViolationSelector) - if err != nil { - glog.Errorf("unable to list policy violations with label selector %v: %v", policyViolationSelector, err) - return nil, err - } - //TODO: ideally there should be only one policy violation returned - if len(pvs) > 1 { - glog.Errorf("more than one policy violation exists with labels %v", labelMap) - return nil, fmt.Errorf("more than one policy violation exists with labels %v", labelMap) - } +// pvs, err := pvLister.List(policyViolationSelector) +// if err != nil { +// glog.Errorf("unable to list policy violations with label selector %v: %v", policyViolationSelector, err) +// return nil, err +// } +// //TODO: ideally there should be only one policy violation returned +// if len(pvs) > 1 { +// glog.Errorf("more than one policy violation exists with labels %v", labelMap) +// return nil, fmt.Errorf("more than one policy violation exists with labels %v", labelMap) +// } - if len(pvs) == 0 { - glog.Infof("policy violation does not exist with labels %v", labelMap) - return nil, nil - } - return pvs[0], nil -} +// if len(pvs) == 0 { +// glog.Infof("policy violation does not exist with labels %v", labelMap) +// return nil, nil +// } +// return pvs[0], nil +// } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 51514c8740..e5915e4eaf 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -4,6 +4,7 @@ import ( "github.com/golang/glog" engine "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" + "github.com/nirmata/kyverno/pkg/policyviolation" v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -95,7 +96,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } // ADD POLICY VIOLATIONS - generatePolicyViolations(ws.pvListerSynced, ws.pvLister, ws.kyvernoClient, policyInfos) + policyviolation.GeneratePolicyViolations(ws.pvListerSynced, ws.pvLister, ws.kyvernoClient, policyInfos) return &v1beta1.AdmissionResponse{ Allowed: true, From b0d7cdba6fab9021552282a95cc857aedb5cf4e6 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 13 Aug 2019 13:15:32 -0700 Subject: [PATCH 23/66] policy store test --- pkg/policystore/policystore.go | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pkg/policystore/policystore.go diff --git a/pkg/policystore/policystore.go b/pkg/policystore/policystore.go new file mode 100644 index 0000000000..2c3c6bcd66 --- /dev/null +++ b/pkg/policystore/policystore.go @@ -0,0 +1,63 @@ +package policystore + +import ( + "sync" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type Interface interface { + Register(policy *kyverno.Policy) error + UnRegister(policy *kyverno.Policy) error // check if the controller can see the policy spec for details? + LookUp(kind, namespace, name string, ls *metav1.LabelSelector) // returns a list of policies and rules that apply +} + +type Store struct { + data map[string]string + mux sync.RWMutex +} + +func NewStore() *Store { + s := Store{ + data: make(map[string]string), //key: kind, value is the name of the policy + } + + return &s +} + +var empty struct{} + +func (s *Store) Register(policy *kyverno.Policy) error { + // check if this policy is already registered for this resource kind + kinds := map[string]string{} + // get kinds from the rules + for _, r := range policy.Spec.Rules { + rkinds := map[string]string{} + // matching resources + for _, k := range r.MatchResources.Kinds { + rkinds[k] = policy.Name + } + for _, k := range r.ExcludeResources.Kinds { + delete(rkinds, k) + } + // merge the result + mergeMap(kinds, rkinds) + + } + + // have all the kinds that the policy has rule on + s.mux.Lock() + defer s.mux.Unlock() + // merge kinds + mergeMap(s.data, kinds) + + return nil +} + +// merge m2 into m2 +func mergeMap(m1, m2 map[string]string) { + for k, v := range m2 { + m1[k] = v + } +} From d8c6dc3bc640110c81f65c4fd3ee765f10f890a4 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 13 Aug 2019 17:24:05 -0700 Subject: [PATCH 24/66] construct policy Info --- pkg/policy/apply.go | 3 ++- pkg/policy/report.go | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go index c07358d46c..019fb931e6 100644 --- a/pkg/policy/apply.go +++ b/pkg/policy/apply.go @@ -21,7 +21,8 @@ func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (inf glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime)) }() // glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) - var policyInfo info.PolicyInfo + policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) + //MUTATION mruleInfos, err := mutation(policy, resource) policyInfo.AddRuleInfos(mruleInfos) diff --git a/pkg/policy/report.go b/pkg/policy/report.go index c232494cbc..f3b80097a9 100644 --- a/pkg/policy/report.go +++ b/pkg/policy/report.go @@ -11,7 +11,6 @@ import ( func (pc *PolicyController) report(policyInfos []info.PolicyInfo) { // generate events // generate policy violations - for _, policyInfo := range policyInfos { // events // success - policy applied on resource From aed0ed0dc1318391a08a9f0cf2fbfb855c577c85 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 14 Aug 2019 10:01:47 -0700 Subject: [PATCH 25/66] clean up --- main.go | 15 +- pkg/annotations/annotations.go | 401 ------------------ pkg/annotations/annotations_test.go | 36 -- pkg/annotations/controller.go | 108 ----- pkg/annotations/info.go | 18 - pkg/annotations/utils.go | 21 - pkg/api/kyverno/v1alpha1/register.go | 4 +- pkg/apis/policy/register.go | 6 - pkg/apis/policy/v1alpha1/doc.go | 4 - pkg/apis/policy/v1alpha1/register.go | 37 -- pkg/apis/policy/v1alpha1/types.go | 119 ------ pkg/apis/policy/v1alpha1/utils.go | 148 ------- pkg/apis/policy/v1alpha1/utils_test.go | 88 ---- .../policy/v1alpha1/zz_generated.deepcopy.go | 316 -------------- pkg/client/clientset/versioned/clientset.go | 90 ---- pkg/client/clientset/versioned/doc.go | 20 - .../versioned/fake/clientset_generated.go | 77 ---- pkg/client/clientset/versioned/fake/doc.go | 20 - .../clientset/versioned/fake/register.go | 56 --- pkg/client/clientset/versioned/scheme/doc.go | 20 - .../clientset/versioned/scheme/register.go | 56 --- .../versioned/typed/policy/v1alpha1/doc.go | 20 - .../typed/policy/v1alpha1/fake/doc.go | 20 - .../typed/policy/v1alpha1/fake/fake_policy.go | 131 ------ .../v1alpha1/fake/fake_policy_client.go | 40 -- .../policy/v1alpha1/generated_expansion.go | 21 - .../versioned/typed/policy/v1alpha1/policy.go | 180 -------- .../typed/policy/v1alpha1/policy_client.go | 90 ---- .../informers/externalversions/factory.go | 180 -------- .../informers/externalversions/generic.go | 62 --- .../internalinterfaces/factory_interfaces.go | 40 -- .../externalversions/policy/interface.go | 46 -- .../policy/v1alpha1/interface.go | 45 -- .../policy/v1alpha1/policy.go | 88 ---- .../policy/v1alpha1/expansion_generated.go | 23 - pkg/client/listers/policy/v1alpha1/policy.go | 65 --- pkg/controller/cleanup.go | 45 -- pkg/controller/controller.go | 333 --------------- pkg/controller/controller_test.go | 148 ------- pkg/controller/utils.go | 21 - pkg/dclient/client.go | 6 +- pkg/engine/overlayPatch.go | 6 +- pkg/engine/utils.go | 10 - pkg/engine/validation.go | 5 +- pkg/event/controller.go | 5 +- pkg/gencontroller/controller.go | 4 - pkg/gencontroller/generation.go | 5 +- pkg/gencontroller/utils.go | 1 - pkg/info/info.go | 27 +- pkg/policy/controller.go | 7 + pkg/policy/existing.go | 3 +- pkg/policy/report.go | 11 +- pkg/policyviolation/controller.go | 2 +- pkg/result/reason.go | 21 - pkg/result/result.go | 182 -------- pkg/result/result_test.go | 54 --- pkg/sharedinformer/sharedinformerfactory.go | 57 --- pkg/sharedinformer/utils.go | 15 - pkg/utils/util.go | 27 +- pkg/violation/builder.go | 276 ------------ pkg/violation/util.go | 27 -- pkg/webhooks/deleteresource.go | 38 -- pkg/webhooks/mutation.go | 2 +- pkg/webhooks/policyvalidation.go | 4 +- pkg/webhooks/report.go | 158 +------ pkg/webhooks/server.go | 33 +- pkg/webhooks/utils.go | 114 ----- pkg/webhooks/validation.go | 2 +- 68 files changed, 54 insertions(+), 4306 deletions(-) delete mode 100644 pkg/annotations/annotations.go delete mode 100644 pkg/annotations/annotations_test.go delete mode 100644 pkg/annotations/controller.go delete mode 100644 pkg/annotations/info.go delete mode 100644 pkg/annotations/utils.go delete mode 100644 pkg/apis/policy/register.go delete mode 100644 pkg/apis/policy/v1alpha1/doc.go delete mode 100644 pkg/apis/policy/v1alpha1/register.go delete mode 100644 pkg/apis/policy/v1alpha1/types.go delete mode 100644 pkg/apis/policy/v1alpha1/utils.go delete mode 100644 pkg/apis/policy/v1alpha1/utils_test.go delete mode 100644 pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go delete mode 100644 pkg/client/clientset/versioned/clientset.go delete mode 100644 pkg/client/clientset/versioned/doc.go delete mode 100644 pkg/client/clientset/versioned/fake/clientset_generated.go delete mode 100644 pkg/client/clientset/versioned/fake/doc.go delete mode 100644 pkg/client/clientset/versioned/fake/register.go delete mode 100644 pkg/client/clientset/versioned/scheme/doc.go delete mode 100644 pkg/client/clientset/versioned/scheme/register.go delete mode 100644 pkg/client/clientset/versioned/typed/policy/v1alpha1/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy.go delete mode 100644 pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy_client.go delete mode 100644 pkg/client/clientset/versioned/typed/policy/v1alpha1/generated_expansion.go delete mode 100644 pkg/client/clientset/versioned/typed/policy/v1alpha1/policy.go delete mode 100644 pkg/client/clientset/versioned/typed/policy/v1alpha1/policy_client.go delete mode 100644 pkg/client/informers/externalversions/factory.go delete mode 100644 pkg/client/informers/externalversions/generic.go delete mode 100644 pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go delete mode 100644 pkg/client/informers/externalversions/policy/interface.go delete mode 100644 pkg/client/informers/externalversions/policy/v1alpha1/interface.go delete mode 100644 pkg/client/informers/externalversions/policy/v1alpha1/policy.go delete mode 100644 pkg/client/listers/policy/v1alpha1/expansion_generated.go delete mode 100644 pkg/client/listers/policy/v1alpha1/policy.go delete mode 100644 pkg/controller/cleanup.go delete mode 100644 pkg/controller/controller.go delete mode 100644 pkg/controller/controller_test.go delete mode 100644 pkg/controller/utils.go delete mode 100644 pkg/result/reason.go delete mode 100644 pkg/result/result.go delete mode 100644 pkg/result/result_test.go delete mode 100644 pkg/sharedinformer/sharedinformerfactory.go delete mode 100644 pkg/sharedinformer/utils.go delete mode 100644 pkg/violation/builder.go delete mode 100644 pkg/violation/util.go delete mode 100644 pkg/webhooks/deleteresource.go diff --git a/main.go b/main.go index 8afe006da9..ef44747238 100644 --- a/main.go +++ b/main.go @@ -58,7 +58,9 @@ func main() { // POLICY CONTROLLER // - reconciliation policy and policy violation + // - process policy on existing resources // - status: violation count + pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) if err != nil { glog.Fatalf("error creating policy controller: %v\n", err) @@ -71,7 +73,6 @@ func main() { glog.Fatalf("error creating policy violation controller: %v\n", err) } - // TODO : Process Existing tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) @@ -92,27 +93,15 @@ func main() { glog.Fatalf("Failed registering Admission Webhooks: %v\n", err) } - //-------- pInformer.Start(stopCh) go pc.Run(1, stopCh) go pvc.Run(1, stopCh) go egen.Run(1, stopCh) //TODO add WG for the go routines? - //-------- - // eventController.Run(stopCh) - // genControler.Run(stopCh) - // annotationsController.Run(stopCh) - // if err = policyController.Run(stopCh); err != nil { - // glog.Fatalf("Error running PolicyController: %v\n", err) - // } server.RunAsync() <-stopCh server.Stop() - // genControler.Stop() - // eventController.Stop() - // annotationsController.Stop() - // policyController.Stop() } func init() { diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go deleted file mode 100644 index be48530ae7..0000000000 --- a/pkg/annotations/annotations.go +++ /dev/null @@ -1,401 +0,0 @@ -package annotations - -import ( - "encoding/json" - "reflect" - - "github.com/golang/glog" - pinfo "github.com/nirmata/kyverno/pkg/info" - - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -//Policy information for annotations -type Policy struct { - Status string `json:"status"` - // Key Type/Name - MutationRules map[string]Rule `json:"mutationrules,omitempty"` - ValidationRules map[string]Rule `json:"validationrules,omitempty"` - GenerationRules map[string]Rule `json:"generationrules,omitempty"` -} - -//Rule information for annotations -type Rule struct { - Status string `json:"status"` - Changes string `json:"changes,omitempty"` // TODO for mutation changes - Message string `json:"message,omitempty"` -} - -func (p *Policy) getOverAllStatus() string { - // mutation - for _, v := range p.MutationRules { - if v.Status == "Failure" { - return "Failure" - } - } - // validation - for _, v := range p.ValidationRules { - if v.Status == "Failure" { - return "Failure" - } - } - // generation - for _, v := range p.GenerationRules { - if v.Status == "Failure" { - return "Failure" - } - } - return "Success" -} - -func getRules(rules []pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule { - if len(rules) == 0 { - return nil - } - annrules := make(map[string]Rule, 0) - // var annrules map[string]Rule - for _, r := range rules { - if r.RuleType != ruleType { - continue - } - - rule := Rule{Status: getStatus(r.IsSuccessful())} - if !r.IsSuccessful() { - rule.Message = r.GetErrorString() - } else { - if ruleType == pinfo.Mutation { - // If ruleType is Mutation - // then for succesful mutation we store the json patch being applied in the annotation information - rule.Changes = r.Changes - } - } - annrules[r.Name] = rule - } - return annrules -} - -func (p *Policy) updatePolicy(obj *Policy, ruleType pinfo.RuleType) bool { - updates := false - // Check Mutation rules - switch ruleType { - case pinfo.All: - if p.compareMutationRules(obj.MutationRules) { - updates = true - } - if p.compareValidationRules(obj.ValidationRules) { - updates = true - } - if p.compareGenerationRules(obj.GenerationRules) { - updates = true - } - case pinfo.Mutation: - if p.compareMutationRules(obj.MutationRules) { - updates = true - } - case pinfo.Validation: - if p.compareValidationRules(obj.ValidationRules) { - updates = true - } - case pinfo.Generation: - if p.compareGenerationRules(obj.GenerationRules) { - updates = true - } - if p.Status != obj.Status { - updates = true - } - } - // check if any rules failed - p.Status = p.getOverAllStatus() - // If there are any updates then the annotation can be updated, can skip - return updates -} - -func (p *Policy) compareMutationRules(rules map[string]Rule) bool { - // check if the rules have changed - if !reflect.DeepEqual(p.MutationRules, rules) { - p.MutationRules = rules - return true - } - return false -} - -func (p *Policy) compareValidationRules(rules map[string]Rule) bool { - // check if the rules have changed - if !reflect.DeepEqual(p.ValidationRules, rules) { - p.ValidationRules = rules - return true - } - return false -} - -func (p *Policy) compareGenerationRules(rules map[string]Rule) bool { - // check if the rules have changed - if !reflect.DeepEqual(p.GenerationRules, rules) { - p.GenerationRules = rules - return true - } - return false -} - -func newAnnotationForPolicy(pi *pinfo.PolicyInfo) *Policy { - return &Policy{Status: getStatus(pi.IsSuccessful()), - MutationRules: getRules(pi.Rules, pinfo.Mutation), - ValidationRules: getRules(pi.Rules, pinfo.Validation), - GenerationRules: getRules(pi.Rules, pinfo.Generation), - } -} - -//AddPolicy will add policy annotation if not present or update if present -// modifies obj -// returns true, if there is any update -> caller need to update the obj -// returns false, if there is no change -> caller can skip the update -func AddPolicy(obj *unstructured.Unstructured, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) bool { - PolicyObj := newAnnotationForPolicy(pi) - // get annotation - ann := obj.GetAnnotations() - // check if policy already has annotation - cPolicy, ok := ann[BuildKey(pi.Name)] - if !ok { - PolicyByte, err := json.Marshal(PolicyObj) - if err != nil { - glog.Error(err) - return false - } - // insert policy information - ann[BuildKey(pi.Name)] = string(PolicyByte) - // set annotation back to unstr - obj.SetAnnotations(ann) - return true - } - cPolicyObj := Policy{} - err := json.Unmarshal([]byte(cPolicy), &cPolicyObj) - if err != nil { - return false - } - // update policy information inside the annotation - // 1> policy status - // 2> Mutation, Validation, Generation - if cPolicyObj.updatePolicy(PolicyObj, ruleType) { - - cPolicyByte, err := json.Marshal(cPolicyObj) - if err != nil { - return false - } - // update policy information - ann[BuildKey(pi.Name)] = string(cPolicyByte) - // set annotation back to unstr - obj.SetAnnotations(ann) - return true - } - return false -} - -//RemovePolicy to remove annotations -// return true -> if there was an entry and we deleted it -// return false -> if there is no entry, caller need not update -func RemovePolicy(obj *unstructured.Unstructured, policy string) bool { - // get annotations - ann := obj.GetAnnotations() - if ann == nil { - return false - } - if _, ok := ann[BuildKey(policy)]; !ok { - return false - } - delete(ann, BuildKey(policy)) - // set annotation back to unstr - obj.SetAnnotations(ann) - return true -} - -//ParseAnnotationsFromObject extracts annotations from the JSON obj -func ParseAnnotationsFromObject(bytes []byte) map[string]string { - var objectJSON map[string]interface{} - json.Unmarshal(bytes, &objectJSON) - meta, ok := objectJSON["metadata"].(map[string]interface{}) - if !ok { - glog.Error("unable to parse") - return nil - } - ann, ok, err := unstructured.NestedStringMap(meta, "annotations") - if err != nil || !ok { - return nil - } - return ann -} - -func PatchAnnotations(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) ([]byte, error) { - if ruleType != pinfo.All && !pi.ContainsRuleType(ruleType) { - // the rule was not proceesed in the current policy application - return nil, nil - } - // transform the PolicyInfo to anotation struct - policyObj := newAnnotationForPolicy(pi) - if ann == nil { - ann = make(map[string]string, 0) - policyByte, err := json.Marshal(policyObj) - if err != nil { - return nil, err - } - // create a json patch to add annotation object - ann[BuildKeyString(pi.Name)] = string(policyByte) - // patch adds the annotation map with the policy information - jsonPatch, err := createAddJSONPatchMap(ann) - return jsonPatch, err - } - // if the annotations map already exists then we need to update it by adding a patch to the field inside the annotation - cPolicy, ok := ann[BuildKey(pi.Name)] - if !ok { - // annotations does not contain the policy - policyByte, err := json.Marshal(policyObj) - if err != nil { - return nil, err - } - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(policyByte)) - return jsonPatch, err - } - // an annotaion exists for the policy, we need to update the information if anything has changed - cPolicyObj := Policy{} - err := json.Unmarshal([]byte(cPolicy), &cPolicyObj) - if err != nil { - // error while unmarshallign the content - return nil, err - } - // update policy information inside the annotation - // 1> policy status - // 2> rule (name, status,changes,type) - update := cPolicyObj.updatePolicy(policyObj, ruleType) - if !update { - // there is not update, so we dont - return nil, nil - } - policyByte, err := json.Marshal(cPolicyObj) - if err != nil { - return nil, err - } - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(policyByte)) - return jsonPatch, err -} - -//AddPolicyJSONPatch generate JSON Patch to add policy informatino JSON patch -func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) (map[string]string, []byte, error) { - if !pi.ContainsRuleType(ruleType) { - return nil, nil, nil - } - PolicyObj := newAnnotationForPolicy(pi) - if ann == nil { - ann = make(map[string]string, 0) - PolicyByte, err := json.Marshal(PolicyObj) - if err != nil { - return nil, nil, err - } - // create a json patch to add annotation object - ann[BuildKey(pi.Name)] = string(PolicyByte) - // create add JSON patch - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(PolicyByte)) - return ann, jsonPatch, err - } - // if the annotations map is present then we - cPolicy, ok := ann[BuildKey(pi.Name)] - if !ok { - PolicyByte, err := json.Marshal(PolicyObj) - if err != nil { - return nil, nil, err - } - // insert policy information - ann[BuildKey(pi.Name)] = string(PolicyByte) - // create add JSON patch - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(PolicyByte)) - - return ann, jsonPatch, err - } - cPolicyObj := Policy{} - err := json.Unmarshal([]byte(cPolicy), &cPolicyObj) - // update policy information inside the annotation - // 1> policy status - // 2> rule (name, status,changes,type) - update := cPolicyObj.updatePolicy(PolicyObj, ruleType) - if !update { - return nil, nil, err - } - - cPolicyByte, err := json.Marshal(cPolicyObj) - if err != nil { - return nil, nil, err - } - // update policy information - ann[BuildKey(pi.Name)] = string(cPolicyByte) - // create update JSON patch - jsonPatch, err := createReplaceJSONPatch(BuildKey(pi.Name), string(cPolicyByte)) - return ann, jsonPatch, err -} - -//RemovePolicyJSONPatch remove JSON patch -func RemovePolicyJSONPatch(ann map[string]string, policy string) (map[string]string, []byte, error) { - if ann == nil { - return nil, nil, nil - } - jsonPatch, err := createRemoveJSONPatchKey(policy) - return ann, jsonPatch, err -} - -type patchMapValue struct { - Op string `json:"op"` - Path string `json:"path"` - Value map[string]string `json:"value"` -} - -type patchStringValue struct { - Op string `json:"op"` - Path string `json:"path"` - Value string `json:"value"` -} - -func createRemoveJSONPatchMap() ([]byte, error) { - payload := []patchMapValue{{ - Op: "remove", - Path: "/metadata/annotations", - }} - return json.Marshal(payload) - -} -func createAddJSONPatchMap(ann map[string]string) ([]byte, error) { - - payload := []patchMapValue{{ - Op: "add", - Path: "/metadata/annotations", - Value: ann, - }} - return json.Marshal(payload) -} - -func createAddJSONPatch(key, value string) ([]byte, error) { - - payload := []patchStringValue{{ - Op: "add", - Path: "/metadata/annotations/" + key, - Value: value, - }} - return json.Marshal(payload) -} - -func createReplaceJSONPatch(key, value string) ([]byte, error) { - // if ann == nil { - // ann = make(map[string]string, 0) - // } - payload := []patchStringValue{{ - Op: "replace", - Path: "/metadata/annotations/" + key, - Value: value, - }} - return json.Marshal(payload) -} - -func createRemoveJSONPatchKey(key string) ([]byte, error) { - payload := []patchStringValue{{ - Op: "remove", - Path: "/metadata/annotations/" + key, - }} - return json.Marshal(payload) - -} diff --git a/pkg/annotations/annotations_test.go b/pkg/annotations/annotations_test.go deleted file mode 100644 index 558746a936..0000000000 --- a/pkg/annotations/annotations_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package annotations - -import ( - "encoding/json" - "testing" - - pinfo "github.com/nirmata/kyverno/pkg/info" -) - -func TestAddPatch(t *testing.T) { - // Create - objRaw := []byte(`{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"nginx-deployment","namespace":"default","creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"containers":[{"name":"nginx","image":"nginx:latest","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"},{"name":"ghost","image":"ghost:latest","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"}},"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxUnavailable":"25%","maxSurge":"25%"}},"revisionHistoryLimit":10,"progressDeadlineSeconds":600},"status":{}}`) - piRaw := []byte(`{"Name":"set-image-pull-policy","RKind":"Deployment","RName":"nginx-deployment","RNamespace":"default","ValidationFailureAction":"","Rules":[{"Name":"nginx-deployment","Msgs":["Rule nginx-deployment: Overlay succesfully applied."],"RuleType":0}]}`) - ann := ParseAnnotationsFromObject(objRaw) - pi := pinfo.PolicyInfo{} - err := json.Unmarshal(piRaw, &pi) - if err != nil { - panic(err) - } - ann, _, err = AddPolicyJSONPatch(ann, &pi, pinfo.Mutation) - if err != nil { - panic(err) - } - // Update - piRaw = []byte(`{"Name":"set-image-pull-policy","RKind":"Deployment","RName":"nginx-deployment","RNamespace":"default","ValidationFailureAction":"","Rules":[{"Name":"nginx-deployment","Msgs":["Rule nginx-deployment1: Overlay succesfully applied."],"RuleType":0}]}`) - // ann = ParseAnnotationsFromObject(objRaw) - pi = pinfo.PolicyInfo{} - err = json.Unmarshal(piRaw, &pi) - if err != nil { - panic(err) - } - ann, _, err = AddPolicyJSONPatch(ann, &pi, pinfo.Mutation) - if err != nil { - panic(err) - } -} diff --git a/pkg/annotations/controller.go b/pkg/annotations/controller.go deleted file mode 100644 index e251719492..0000000000 --- a/pkg/annotations/controller.go +++ /dev/null @@ -1,108 +0,0 @@ -package annotations - -import ( - "fmt" - "time" - - "github.com/golang/glog" - client "github.com/nirmata/kyverno/pkg/dclient" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/util/workqueue" -) - -type controller struct { - client *client.Client - queue workqueue.RateLimitingInterface -} - -type Interface interface { - Add(rkind, rns, rname string, patch []byte) -} - -type Controller interface { - Interface - Run(stopCh <-chan struct{}) - Stop() -} - -func NewAnnotationControler(client *client.Client) Controller { - return &controller{ - client: client, - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), annotationQueueName), - } -} - -func (c *controller) Add(rkind, rns, rname string, patch []byte) { - c.queue.Add(newInfo(rkind, rns, rname, &patch)) -} - -func (c *controller) Run(stopCh <-chan struct{}) { - defer utilruntime.HandleCrash() - for i := 0; i < workerThreadCount; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - glog.Info("Started annotation controller workers") -} - -func (c *controller) Stop() { - c.queue.ShutDown() - glog.Info("Shutting down annotation controller workers") -} - -func (c *controller) runWorker() { - for c.processNextWorkItem() { - } -} - -func (pc *controller) processNextWorkItem() bool { - obj, shutdown := pc.queue.Get() - if shutdown { - return false - } - - err := func(obj interface{}) error { - defer pc.queue.Done(obj) - err := pc.syncHandler(obj) - pc.handleErr(err, obj) - return nil - }(obj) - if err != nil { - glog.Error(err) - return true - } - return true -} -func (pc *controller) handleErr(err error, key interface{}) { - if err == nil { - pc.queue.Forget(key) - return - } - // This controller retries if something goes wrong. After that, it stops trying. - if pc.queue.NumRequeues(key) < workQueueRetryLimit { - glog.Warningf("Error syncing events %v: %v", key, err) - // Re-enqueue the key rate limited. Based on the rate limiter on the - // queue and the re-enqueue history, the key will be processed later again. - pc.queue.AddRateLimited(key) - return - } - pc.queue.Forget(key) - glog.Error(err) - glog.Warningf("Dropping the key out of the queue: %v", err) -} - -func (c *controller) syncHandler(obj interface{}) error { - var key info - var ok bool - if key, ok = obj.(info); !ok { - return fmt.Errorf("expected string in workqueue but got %#v", obj) - } - - var err error - _, err = c.client.PatchResource(key.RKind, key.RNs, key.RName, *key.patch) - if err != nil { - glog.Errorf("Error creating annotation: unable to get resource %s/%s/%s, will retry: %s", key.RKind, key.RNs, key.RName, err) - return err - } - return nil -} diff --git a/pkg/annotations/info.go b/pkg/annotations/info.go deleted file mode 100644 index 1f76fe506f..0000000000 --- a/pkg/annotations/info.go +++ /dev/null @@ -1,18 +0,0 @@ -package annotations - -type info struct { - RKind string - RNs string - RName string - //TODO:Hack as slice makes the struct unhasable - patch *[]byte -} - -func newInfo(rkind, rns, rname string, patch *[]byte) info { - return info{ - RKind: rkind, - RNs: rns, - RName: rname, - patch: patch, - } -} diff --git a/pkg/annotations/utils.go b/pkg/annotations/utils.go deleted file mode 100644 index 929fc9dc0b..0000000000 --- a/pkg/annotations/utils.go +++ /dev/null @@ -1,21 +0,0 @@ -package annotations - -const annotationQueueName = "annotation-queue" -const workerThreadCount = 1 -const workQueueRetryLimit = 5 - -func getStatus(status bool) string { - if status { - return "Success" - } - return "Failure" -} - -func BuildKey(policyName string) string { - //JSON Pointers - return "policies.kyverno.io~1" + policyName -} - -func BuildKeyString(policyName string) string { - return "policies.kyverno.io/" + policyName -} diff --git a/pkg/api/kyverno/v1alpha1/register.go b/pkg/api/kyverno/v1alpha1/register.go index 500bdb7670..86f19d5512 100644 --- a/pkg/api/kyverno/v1alpha1/register.go +++ b/pkg/api/kyverno/v1alpha1/register.go @@ -5,11 +5,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/nirmata/kyverno/pkg/apis/policy" + "github.com/nirmata/kyverno/pkg/api/kyverno" ) // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: policy.GroupName, Version: "v1alpha1"} +var SchemeGroupVersion = schema.GroupVersion{Group: kyverno.GroupName, Version: "v1alpha1"} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) schema.GroupKind { diff --git a/pkg/apis/policy/register.go b/pkg/apis/policy/register.go deleted file mode 100644 index eb0717d576..0000000000 --- a/pkg/apis/policy/register.go +++ /dev/null @@ -1,6 +0,0 @@ -package policy - -const ( - // GroupName must be the same as specified in Policy CRD - GroupName = "kyverno.io" -) diff --git a/pkg/apis/policy/v1alpha1/doc.go b/pkg/apis/policy/v1alpha1/doc.go deleted file mode 100644 index 4fa2b53292..0000000000 --- a/pkg/apis/policy/v1alpha1/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// +k8s:deepcopy-gen=package -// +groupName=kyverno.io - -package v1alpha1 diff --git a/pkg/apis/policy/v1alpha1/register.go b/pkg/apis/policy/v1alpha1/register.go deleted file mode 100644 index 1edab1cf77..0000000000 --- a/pkg/apis/policy/v1alpha1/register.go +++ /dev/null @@ -1,37 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - - "github.com/nirmata/kyverno/pkg/apis/policy" -) - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: policy.GroupName, Version: "v1alpha1"} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Policy{}, - &PolicyList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/pkg/apis/policy/v1alpha1/types.go b/pkg/apis/policy/v1alpha1/types.go deleted file mode 100644 index c68326dacd..0000000000 --- a/pkg/apis/policy/v1alpha1/types.go +++ /dev/null @@ -1,119 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Policy contains rules to be applied to created resources -type Policy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Spec Spec `json:"spec"` - Status Status `json:"status"` -} - -// Spec describes policy behavior by its rules -type Spec struct { - Rules []Rule `json:"rules"` - ValidationFailureAction string `json:"validationFailureAction"` -} - -// Rule is set of mutation, validation and generation actions -// for the single resource description -type Rule struct { - Name string `json:"name"` - MatchResources MatchResources `json:"match"` - ExcludeResources ExcludeResources `json:"exclude,omitempty"` - Mutation *Mutation `json:"mutate"` - Validation *Validation `json:"validate"` - Generation *Generation `json:"generate"` -} - -//MatchResources contains resource description of the resources that the rule is to apply on -type MatchResources struct { - ResourceDescription `json:"resources"` -} - -//ExcludeResources container resource description of the resources that are to be excluded from the applying the policy rule -type ExcludeResources struct { - ResourceDescription `json:"resources"` -} - -// ResourceDescription describes the resource to which the PolicyRule will be applied. -type ResourceDescription struct { - Kinds []string `json:"kinds"` - Name *string `json:"name"` - Namespace *string `json:"namespace,omitempty"` - Selector *metav1.LabelSelector `json:"selector"` -} - -// Mutation describes the way how Mutating Webhook will react on resource creation -type Mutation struct { - Overlay *interface{} `json:"overlay"` - Patches []Patch `json:"patches"` -} - -// +k8s:deepcopy-gen=false - -// Patch declares patch operation for created object according to RFC 6902 -type Patch struct { - Path string `json:"path"` - Operation string `json:"op"` - Value interface{} `json:"value"` -} - -// Validation describes the way how Validating Webhook will check the resource on creation -type Validation struct { - Message *string `json:"message"` - Pattern interface{} `json:"pattern"` -} - -// Generation describes which resources will be created when other resource is created -type Generation struct { - Kind string `json:"kind"` - Name string `json:"name"` - Data interface{} `json:"data"` - Clone *CloneFrom `json:"clone"` -} - -// CloneFrom - location of a Secret or a ConfigMap -// which will be used as source when applying 'generate' -type CloneFrom struct { - Namespace string `json:"namespace"` - Name string `json:"name"` -} - -// Status contains violations for existing resources -type Status struct { - // Violations map[kind/namespace/resource]Violation - Violations map[string]Violation `json:"violations,omitempty"` -} - -// Violation for the policy -type Violation struct { - Kind string `json:"kind,omitempty"` - Name string `json:"name,omitempty"` - Namespace string `json:"namespace,omitempty"` - Rules []FailedRule `json:"rules"` - Reason string `json:"reason,omitempty"` -} - -// FailedRule stored info and type of failed rules -type FailedRule struct { - Name string `json:"name"` - Type string `json:"type"` //Mutation, Validation, Genertaion - Error string `json:"error"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PolicyList is a list of Policy resources -type PolicyList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - Items []Policy `json:"items"` -} diff --git a/pkg/apis/policy/v1alpha1/utils.go b/pkg/apis/policy/v1alpha1/utils.go deleted file mode 100644 index 5ae5e8cb07..0000000000 --- a/pkg/apis/policy/v1alpha1/utils.go +++ /dev/null @@ -1,148 +0,0 @@ -package v1alpha1 - -import ( - "errors" - "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Validate checks if rule is not empty and all substructures are valid -func (r *Rule) Validate() error { - // check matches Resoource Description of match resource - err := r.MatchResources.ResourceDescription.Validate() - if err != nil { - return err - } - - if r.Mutation == nil && r.Validation == nil && r.Generation == nil { - return errors.New("The rule is empty") - } - - return nil -} - -// Validate checks if all necesarry fields are present and have values. Also checks a Selector. -// Returns error if -// - kinds is not defined -func (pr *ResourceDescription) Validate() error { - if len(pr.Kinds) == 0 { - return errors.New("The Kind is not specified") - } - - if pr.Selector != nil { - selector, err := metav1.LabelSelectorAsSelector(pr.Selector) - if err != nil { - return err - } - requirements, _ := selector.Requirements() - if len(requirements) == 0 { - return errors.New("The requirements are not specified in selector") - } - } - - return nil -} - -// Validate if all mandatory PolicyPatch fields are set -func (pp *Patch) Validate() error { - if pp.Path == "" { - return errors.New("JSONPatch field 'path' is mandatory") - } - - if pp.Operation == "add" || pp.Operation == "replace" { - if pp.Value == nil { - return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation) - } - - return nil - } else if pp.Operation == "remove" { - return nil - } - - return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation) -} - -// Validate returns error if generator is configured incompletely -func (pcg *Generation) Validate() error { - if pcg.Data == nil && pcg.Clone == nil { - return fmt.Errorf("Neither data nor clone (source) of %s is specified", pcg.Kind) - } - if pcg.Data != nil && pcg.Clone != nil { - return fmt.Errorf("Both data nor clone (source) of %s are specified", pcg.Kind) - } - return nil -} - -// DeepCopyInto is declared because k8s:deepcopy-gen is -// not able to generate this method for interface{} member -func (in *Mutation) DeepCopyInto(out *Mutation) { - if out != nil { - *out = *in - } -} - -// DeepCopyInto is declared because k8s:deepcopy-gen is -// not able to generate this method for interface{} member -func (pp *Patch) DeepCopyInto(out *Patch) { - if out != nil { - *out = *pp - } -} - -// DeepCopyInto is declared because k8s:deepcopy-gen is -// not able to generate this method for interface{} member -func (in *Validation) DeepCopyInto(out *Validation) { - if out != nil { - *out = *in - } -} - -// DeepCopyInto is declared because k8s:deepcopy-gen is -// not able to generate this method for interface{} member -func (in *Generation) DeepCopyInto(out *Generation) { - if out != nil { - *out = *in - } -} - -// return true -> if there were any removals -// return false -> if it looks the same -func (v *Violation) RemoveRulesOfType(ruleType string) bool { - removed := false - updatedRules := []FailedRule{} - for _, r := range v.Rules { - if r.Type == ruleType { - removed = true - continue - } - updatedRules = append(updatedRules, r) - } - - if removed { - v.Rules = updatedRules - return true - } - return false -} - -//IsEqual Check if violatiosn are equal -func (v *Violation) IsEqual(nv Violation) bool { - // We do not need to compare resource info as it will be same - // Reason - if v.Reason != nv.Reason { - return false - } - // Rule - if len(v.Rules) != len(nv.Rules) { - return false - } - // assumes the rules will be in order, as the rule are proceeed in order - // if the rule order changes, it means the policy has changed.. as it will afffect the order in which mutation rules are applied - for i, r := range v.Rules { - if r != nv.Rules[i] { - return false - } - } - return true -} diff --git a/pkg/apis/policy/v1alpha1/utils_test.go b/pkg/apis/policy/v1alpha1/utils_test.go deleted file mode 100644 index f77c53f044..0000000000 --- a/pkg/apis/policy/v1alpha1/utils_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package v1alpha1 - -import ( - "testing" - - "gotest.tools/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -var defaultResourceDescriptionName = "defaultResourceDescription" - -var defaultResourceDescription = ResourceDescription{ - Kinds: []string{"Deployment"}, - Name: &defaultResourceDescriptionName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"LabelForSelector": "defaultResourceDescription"}, - }, -} - -func Test_EmptyRule(t *testing.T) { - emptyRule := Rule{ - Name: "defaultRule", - MatchResources: MatchResources{ResourceDescription: defaultResourceDescription}, - } - err := emptyRule.Validate() - assert.Assert(t, err != nil) -} - -func Test_ResourceDescription(t *testing.T) { - err := defaultResourceDescription.Validate() - assert.NilError(t, err) -} - -func Test_ResourceDescription_EmptyKind(t *testing.T) { - resourceDescription := ResourceDescription{ - Name: &defaultResourceDescriptionName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"LabelForSelector": "defaultResourceDescription"}, - }, - } - err := resourceDescription.Validate() - assert.Assert(t, err != nil) -} - -func Test_ResourceDescription_EmptyNameAndSelector(t *testing.T) { - resourceDescription := ResourceDescription{ - Kinds: []string{"Deployment"}, - } - err := resourceDescription.Validate() - assert.NilError(t, err) -} - -func Test_Patch_EmptyPath(t *testing.T) { - patch := Patch{ - Operation: "add", - Value: "true", - } - err := patch.Validate() - assert.Assert(t, err != nil) -} - -func Test_Patch_EmptyValueWithAdd(t *testing.T) { - patch := Patch{ - Path: "/metadata/labels/is-mutated", - Operation: "add", - } - err := patch.Validate() - assert.Assert(t, err != nil) -} - -func Test_Patch_UnsupportedOperation(t *testing.T) { - patch := Patch{ - Path: "/metadata/labels/is-mutated", - Operation: "overwrite", - Value: "true", - } - err := patch.Validate() - assert.Assert(t, err != nil) -} - -func Test_Generation_EmptyCopyFrom(t *testing.T) { - generation := Generation{ - Kind: "ConfigMap", - Name: "comfigmapGenerator", - } - err := generation.Validate() - assert.Assert(t, err != nil) -} diff --git a/pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 2b2015a3c1..0000000000 --- a/pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,316 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CloneFrom) DeepCopyInto(out *CloneFrom) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloneFrom. -func (in *CloneFrom) DeepCopy() *CloneFrom { - if in == nil { - return nil - } - out := new(CloneFrom) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExcludeResources) DeepCopyInto(out *ExcludeResources) { - *out = *in - in.ResourceDescription.DeepCopyInto(&out.ResourceDescription) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludeResources. -func (in *ExcludeResources) DeepCopy() *ExcludeResources { - if in == nil { - return nil - } - out := new(ExcludeResources) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FailedRule) DeepCopyInto(out *FailedRule) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FailedRule. -func (in *FailedRule) DeepCopy() *FailedRule { - if in == nil { - return nil - } - out := new(FailedRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Generation. -func (in *Generation) DeepCopy() *Generation { - if in == nil { - return nil - } - out := new(Generation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MatchResources) DeepCopyInto(out *MatchResources) { - *out = *in - in.ResourceDescription.DeepCopyInto(&out.ResourceDescription) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchResources. -func (in *MatchResources) DeepCopy() *MatchResources { - if in == nil { - return nil - } - out := new(MatchResources) - in.DeepCopyInto(out) - return out -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Mutation. -func (in *Mutation) DeepCopy() *Mutation { - if in == nil { - return nil - } - out := new(Mutation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Policy) DeepCopyInto(out *Policy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Policy. -func (in *Policy) DeepCopy() *Policy { - if in == nil { - return nil - } - out := new(Policy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Policy) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PolicyList) DeepCopyInto(out *PolicyList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Policy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList. -func (in *PolicyList) DeepCopy() *PolicyList { - if in == nil { - return nil - } - out := new(PolicyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PolicyList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) { - *out = *in - if in.Kinds != nil { - in, out := &in.Kinds, &out.Kinds - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Name != nil { - in, out := &in.Name, &out.Name - *out = new(string) - **out = **in - } - if in.Namespace != nil { - in, out := &in.Namespace, &out.Namespace - *out = new(string) - **out = **in - } - if in.Selector != nil { - in, out := &in.Selector, &out.Selector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceDescription. -func (in *ResourceDescription) DeepCopy() *ResourceDescription { - if in == nil { - return nil - } - out := new(ResourceDescription) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Rule) DeepCopyInto(out *Rule) { - *out = *in - in.MatchResources.DeepCopyInto(&out.MatchResources) - in.ExcludeResources.DeepCopyInto(&out.ExcludeResources) - if in.Mutation != nil { - in, out := &in.Mutation, &out.Mutation - *out = (*in).DeepCopy() - } - if in.Validation != nil { - in, out := &in.Validation, &out.Validation - *out = (*in).DeepCopy() - } - if in.Generation != nil { - in, out := &in.Generation, &out.Generation - *out = (*in).DeepCopy() - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule. -func (in *Rule) DeepCopy() *Rule { - if in == nil { - return nil - } - out := new(Rule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Spec) DeepCopyInto(out *Spec) { - *out = *in - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]Rule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spec. -func (in *Spec) DeepCopy() *Spec { - if in == nil { - return nil - } - out := new(Spec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Status) DeepCopyInto(out *Status) { - *out = *in - if in.Violations != nil { - in, out := &in.Violations, &out.Violations - *out = make(map[string]Violation, len(*in)) - for key, val := range *in { - (*out)[key] = *val.DeepCopy() - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status. -func (in *Status) DeepCopy() *Status { - if in == nil { - return nil - } - out := new(Status) - in.DeepCopyInto(out) - return out -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Validation. -func (in *Validation) DeepCopy() *Validation { - if in == nil { - return nil - } - out := new(Validation) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Violation) DeepCopyInto(out *Violation) { - *out = *in - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]FailedRule, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Violation. -func (in *Violation) DeepCopy() *Violation { - if in == nil { - return nil - } - out := new(Violation) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go deleted file mode 100644 index b0088672a3..0000000000 --- a/pkg/client/clientset/versioned/clientset.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package versioned - -import ( - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1" - discovery "k8s.io/client-go/discovery" - rest "k8s.io/client-go/rest" - flowcontrol "k8s.io/client-go/util/flowcontrol" -) - -type Interface interface { - Discovery() discovery.DiscoveryInterface - KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface -} - -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. -type Clientset struct { - *discovery.DiscoveryClient - kyvernoV1alpha1 *kyvernov1alpha1.KyvernoV1alpha1Client -} - -// KyvernoV1alpha1 retrieves the KyvernoV1alpha1Client -func (c *Clientset) KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface { - return c.kyvernoV1alpha1 -} - -// Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - if c == nil { - return nil - } - return c.DiscoveryClient -} - -// NewForConfig creates a new Clientset for the given config. -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - var cs Clientset - var err error - cs.kyvernoV1alpha1, err = kyvernov1alpha1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - return &cs, nil -} - -// NewForConfigOrDie creates a new Clientset for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *Clientset { - var cs Clientset - cs.kyvernoV1alpha1 = kyvernov1alpha1.NewForConfigOrDie(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) - return &cs -} - -// New creates a new Clientset for the given RESTClient. -func New(c rest.Interface) *Clientset { - var cs Clientset - cs.kyvernoV1alpha1 = kyvernov1alpha1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go deleted file mode 100644 index 41721ca52d..0000000000 --- a/pkg/client/clientset/versioned/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go deleted file mode 100644 index 9176faa6ad..0000000000 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - clientset "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1" - fakekyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/discovery" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/testing" -) - -// NewSimpleClientset returns a clientset that will respond with the provided objects. -// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement -// for a real clientset and is mostly useful in simple unit tests. -func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - cs := &Clientset{} - cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} - cs.AddReactor("*", "*", testing.ObjectReaction(o)) - cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { - gvr := action.GetResource() - ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) - if err != nil { - return false, nil, err - } - return true, watch, nil - }) - - return cs -} - -// Clientset implements clientset.Interface. Meant to be embedded into a -// struct to get a default implementation. This makes faking out just the method -// you want to test easier. -type Clientset struct { - testing.Fake - discovery *fakediscovery.FakeDiscovery -} - -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - return c.discovery -} - -var _ clientset.Interface = &Clientset{} - -// KyvernoV1alpha1 retrieves the KyvernoV1alpha1Client -func (c *Clientset) KyvernoV1alpha1() kyvernov1alpha1.KyvernoV1alpha1Interface { - return &fakekyvernov1alpha1.FakeKyvernoV1alpha1{Fake: &c.Fake} -} diff --git a/pkg/client/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go deleted file mode 100644 index 9b99e71670..0000000000 --- a/pkg/client/clientset/versioned/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated fake clientset. -package fake diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go deleted file mode 100644 index 1a81a8ac07..0000000000 --- a/pkg/client/clientset/versioned/fake/register.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var scheme = runtime.NewScheme() -var codecs = serializer.NewCodecFactory(scheme) -var parameterCodec = runtime.NewParameterCodec(scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - kyvernov1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(scheme)) -} diff --git a/pkg/client/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go deleted file mode 100644 index 7dc3756168..0000000000 --- a/pkg/client/clientset/versioned/scheme/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package contains the scheme of the automatically generated clientset. -package scheme diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go deleted file mode 100644 index 372f8cbeda..0000000000 --- a/pkg/client/clientset/versioned/scheme/register.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package scheme - -import ( - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var Scheme = runtime.NewScheme() -var Codecs = serializer.NewCodecFactory(Scheme) -var ParameterCodec = runtime.NewParameterCodec(Scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - kyvernov1alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(Scheme)) -} diff --git a/pkg/client/clientset/versioned/typed/policy/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/policy/v1alpha1/doc.go deleted file mode 100644 index df51baa4d4..0000000000 --- a/pkg/client/clientset/versioned/typed/policy/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/doc.go deleted file mode 100644 index 16f4439906..0000000000 --- a/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy.go b/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy.go deleted file mode 100644 index 57f59a1917..0000000000 --- a/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakePolicies implements PolicyInterface -type FakePolicies struct { - Fake *FakeKyvernoV1alpha1 -} - -var policiesResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policies"} - -var policiesKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "Policy"} - -// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. -func (c *FakePolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(policiesResource, name), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// List takes label and field selectors, and returns the list of Policies that match those selectors. -func (c *FakePolicies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(policiesResource, policiesKind, opts), &v1alpha1.PolicyList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.PolicyList{ListMeta: obj.(*v1alpha1.PolicyList).ListMeta} - for _, item := range obj.(*v1alpha1.PolicyList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested policies. -func (c *FakePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(policiesResource, opts)) -} - -// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *FakePolicies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(policiesResource, policy), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *FakePolicies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(policiesResource, policy), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePolicies) UpdateStatus(policy *v1alpha1.Policy) (*v1alpha1.Policy, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(policiesResource, "status", policy), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// Delete takes name of the policy and deletes it. Returns an error if one occurs. -func (c *FakePolicies) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(policiesResource, name), &v1alpha1.Policy{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(policiesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.PolicyList{}) - return err -} - -// Patch applies the patch and returns the patched policy. -func (c *FakePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(policiesResource, name, pt, data, subresources...), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} diff --git a/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy_client.go b/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy_client.go deleted file mode 100644 index 06754d7b01..0000000000 --- a/pkg/client/clientset/versioned/typed/policy/v1alpha1/fake/fake_policy_client.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/policy/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeKyvernoV1alpha1 struct { - *testing.Fake -} - -func (c *FakeKyvernoV1alpha1) Policies() v1alpha1.PolicyInterface { - return &FakePolicies{c} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeKyvernoV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/policy/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/policy/v1alpha1/generated_expansion.go deleted file mode 100644 index c8b772ddb9..0000000000 --- a/pkg/client/clientset/versioned/typed/policy/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type PolicyExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/policy/v1alpha1/policy.go b/pkg/client/clientset/versioned/typed/policy/v1alpha1/policy.go deleted file mode 100644 index 8f6c66f7e6..0000000000 --- a/pkg/client/clientset/versioned/typed/policy/v1alpha1/policy.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - scheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// PoliciesGetter has a method to return a PolicyInterface. -// A group's client should implement this interface. -type PoliciesGetter interface { - Policies() PolicyInterface -} - -// PolicyInterface has methods to work with Policy resources. -type PolicyInterface interface { - Create(*v1alpha1.Policy) (*v1alpha1.Policy, error) - Update(*v1alpha1.Policy) (*v1alpha1.Policy, error) - UpdateStatus(*v1alpha1.Policy) (*v1alpha1.Policy, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Policy, error) - List(opts v1.ListOptions) (*v1alpha1.PolicyList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) - PolicyExpansion -} - -// policies implements PolicyInterface -type policies struct { - client rest.Interface -} - -// newPolicies returns a Policies -func newPolicies(c *KyvernoV1alpha1Client) *policies { - return &policies{ - client: c.RESTClient(), - } -} - -// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. -func (c *policies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Get(). - Resource("policies"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Policies that match those selectors. -func (c *policies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.PolicyList{} - err = c.client.Get(). - Resource("policies"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested policies. -func (c *policies) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("policies"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *policies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Post(). - Resource("policies"). - Body(policy). - Do(). - Into(result) - return -} - -// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *policies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Put(). - Resource("policies"). - Name(policy.Name). - Body(policy). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *policies) UpdateStatus(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Put(). - Resource("policies"). - Name(policy.Name). - SubResource("status"). - Body(policy). - Do(). - Into(result) - return -} - -// Delete takes name of the policy and deletes it. Returns an error if one occurs. -func (c *policies) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("policies"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *policies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("policies"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched policy. -func (c *policies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Patch(pt). - Resource("policies"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/policy/v1alpha1/policy_client.go b/pkg/client/clientset/versioned/typed/policy/v1alpha1/policy_client.go deleted file mode 100644 index 7fecaa320e..0000000000 --- a/pkg/client/clientset/versioned/typed/policy/v1alpha1/policy_client.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" -) - -type KyvernoV1alpha1Interface interface { - RESTClient() rest.Interface - PoliciesGetter -} - -// KyvernoV1alpha1Client is used to interact with features provided by the kyverno.io group. -type KyvernoV1alpha1Client struct { - restClient rest.Interface -} - -func (c *KyvernoV1alpha1Client) Policies() PolicyInterface { - return newPolicies(c) -} - -// NewForConfig creates a new KyvernoV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*KyvernoV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &KyvernoV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new KyvernoV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *KyvernoV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new KyvernoV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *KyvernoV1alpha1Client { - return &KyvernoV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *KyvernoV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go deleted file mode 100644 index 65dff3735d..0000000000 --- a/pkg/client/informers/externalversions/factory.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - reflect "reflect" - sync "sync" - time "time" - - versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" - policy "github.com/nirmata/kyverno/pkg/client/informers/externalversions/policy" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// SharedInformerOption defines the functional option type for SharedInformerFactory. -type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory - -type sharedInformerFactory struct { - client versioned.Interface - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc - lock sync.Mutex - defaultResync time.Duration - customResync map[reflect.Type]time.Duration - - informers map[reflect.Type]cache.SharedIndexInformer - // startedInformers is used for tracking which informers have been started. - // This allows Start() to be called multiple times safely. - startedInformers map[reflect.Type]bool -} - -// WithCustomResyncConfig sets a custom resync period for the specified informer types. -func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - for k, v := range resyncConfig { - factory.customResync[reflect.TypeOf(k)] = v - } - return factory - } -} - -// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. -func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.tweakListOptions = tweakListOptions - return factory - } -} - -// WithNamespace limits the SharedInformerFactory to the specified namespace. -func WithNamespace(namespace string) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.namespace = namespace - return factory - } -} - -// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. -func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync) -} - -// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. -// Listers obtained via this SharedInformerFactory will be subject to the same filters -// as specified here. -// Deprecated: Please use NewSharedInformerFactoryWithOptions instead -func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) -} - -// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. -func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { - factory := &sharedInformerFactory{ - client: client, - namespace: v1.NamespaceAll, - defaultResync: defaultResync, - informers: make(map[reflect.Type]cache.SharedIndexInformer), - startedInformers: make(map[reflect.Type]bool), - customResync: make(map[reflect.Type]time.Duration), - } - - // Apply all options - for _, opt := range options { - factory = opt(factory) - } - - return factory -} - -// Start initializes all requested informers. -func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { - f.lock.Lock() - defer f.lock.Unlock() - - for informerType, informer := range f.informers { - if !f.startedInformers[informerType] { - go informer.Run(stopCh) - f.startedInformers[informerType] = true - } - } -} - -// WaitForCacheSync waits for all started informers' cache were synced. -func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { - informers := func() map[reflect.Type]cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informers := map[reflect.Type]cache.SharedIndexInformer{} - for informerType, informer := range f.informers { - if f.startedInformers[informerType] { - informers[informerType] = informer - } - } - return informers - }() - - res := map[reflect.Type]bool{} - for informType, informer := range informers { - res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) - } - return res -} - -// InternalInformerFor returns the SharedIndexInformer for obj using an internal -// client. -func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informerType := reflect.TypeOf(obj) - informer, exists := f.informers[informerType] - if exists { - return informer - } - - resyncPeriod, exists := f.customResync[informerType] - if !exists { - resyncPeriod = f.defaultResync - } - - informer = newFunc(f.client, resyncPeriod) - f.informers[informerType] = informer - - return informer -} - -// SharedInformerFactory provides shared informers for resources in all known -// API group versions. -type SharedInformerFactory interface { - internalinterfaces.SharedInformerFactory - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) - WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - - Kyverno() policy.Interface -} - -func (f *sharedInformerFactory) Kyverno() policy.Interface { - return policy.New(f, f.namespace, f.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go deleted file mode 100644 index 1e6c023866..0000000000 --- a/pkg/client/informers/externalversions/generic.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - "fmt" - - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// GenericInformer is type of SharedIndexInformer which will locate and delegate to other -// sharedInformers based on type -type GenericInformer interface { - Informer() cache.SharedIndexInformer - Lister() cache.GenericLister -} - -type genericInformer struct { - informer cache.SharedIndexInformer - resource schema.GroupResource -} - -// Informer returns the SharedIndexInformer. -func (f *genericInformer) Informer() cache.SharedIndexInformer { - return f.informer -} - -// Lister returns the GenericLister. -func (f *genericInformer) Lister() cache.GenericLister { - return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) -} - -// ForResource gives generic access to a shared informer of the matching type -// TODO extend this to unknown resources with a client pool -func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { - switch resource { - // Group=kyverno.io, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("policies"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Kyverno().V1alpha1().Policies().Informer()}, nil - - } - - return nil, fmt.Errorf("no informer found for %v", resource) -} diff --git a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go deleted file mode 100644 index 65a1d1de8f..0000000000 --- a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package internalinterfaces - -import ( - time "time" - - versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - cache "k8s.io/client-go/tools/cache" -) - -// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. -type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer - -// SharedInformerFactory a small interface to allow for adding an informer without an import cycle -type SharedInformerFactory interface { - Start(stopCh <-chan struct{}) - InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer -} - -// TweakListOptionsFunc is a function that transforms a v1.ListOptions. -type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/client/informers/externalversions/policy/interface.go b/pkg/client/informers/externalversions/policy/interface.go deleted file mode 100644 index bf148658a6..0000000000 --- a/pkg/client/informers/externalversions/policy/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package kyverno - -import ( - internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/nirmata/kyverno/pkg/client/informers/externalversions/policy/v1alpha1" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha1 provides access to shared informers for resources in V1alpha1. - V1alpha1() v1alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha1 returns a new v1alpha1.Interface. -func (g *group) V1alpha1() v1alpha1.Interface { - return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/policy/v1alpha1/interface.go b/pkg/client/informers/externalversions/policy/v1alpha1/interface.go deleted file mode 100644 index 040d488950..0000000000 --- a/pkg/client/informers/externalversions/policy/v1alpha1/interface.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // Policies returns a PolicyInformer. - Policies() PolicyInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// Policies returns a PolicyInformer. -func (v *version) Policies() PolicyInformer { - return &policyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/policy/v1alpha1/policy.go b/pkg/client/informers/externalversions/policy/v1alpha1/policy.go deleted file mode 100644 index 936f4dc2ff..0000000000 --- a/pkg/client/informers/externalversions/policy/v1alpha1/policy.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - policyv1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// PolicyInformer provides access to a shared informer and lister for -// Policies. -type PolicyInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.PolicyLister -} - -type policyInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewPolicyInformer constructs a new informer for Policy type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPolicyInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredPolicyInformer constructs a new informer for Policy type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KyvernoV1alpha1().Policies().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KyvernoV1alpha1().Policies().Watch(options) - }, - }, - &policyv1alpha1.Policy{}, - resyncPeriod, - indexers, - ) -} - -func (f *policyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *policyInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&policyv1alpha1.Policy{}, f.defaultInformer) -} - -func (f *policyInformer) Lister() v1alpha1.PolicyLister { - return v1alpha1.NewPolicyLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/listers/policy/v1alpha1/expansion_generated.go b/pkg/client/listers/policy/v1alpha1/expansion_generated.go deleted file mode 100644 index 6837481c72..0000000000 --- a/pkg/client/listers/policy/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -// PolicyListerExpansion allows custom methods to be added to -// PolicyLister. -type PolicyListerExpansion interface{} diff --git a/pkg/client/listers/policy/v1alpha1/policy.go b/pkg/client/listers/policy/v1alpha1/policy.go deleted file mode 100644 index dd51bbf906..0000000000 --- a/pkg/client/listers/policy/v1alpha1/policy.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// PolicyLister helps list Policies. -type PolicyLister interface { - // List lists all Policies in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) - // Get retrieves the Policy from the index for a given name. - Get(name string) (*v1alpha1.Policy, error) - PolicyListerExpansion -} - -// policyLister implements the PolicyLister interface. -type policyLister struct { - indexer cache.Indexer -} - -// NewPolicyLister returns a new PolicyLister. -func NewPolicyLister(indexer cache.Indexer) PolicyLister { - return &policyLister{indexer: indexer} -} - -// List lists all Policies in the indexer. -func (s *policyLister) List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Policy)) - }) - return ret, err -} - -// Get retrieves the Policy from the index for a given name. -func (s *policyLister) Get(name string) (*v1alpha1.Policy, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("policy"), name) - } - return obj.(*v1alpha1.Policy), nil -} diff --git a/pkg/controller/cleanup.go b/pkg/controller/cleanup.go deleted file mode 100644 index 7890583afb..0000000000 --- a/pkg/controller/cleanup.go +++ /dev/null @@ -1,45 +0,0 @@ -package controller - -import ( - "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/annotations" - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" - "github.com/nirmata/kyverno/pkg/engine" - "github.com/nirmata/kyverno/pkg/utils" - "k8s.io/apimachinery/pkg/runtime" -) - -func cleanAnnotations(client *client.Client, obj interface{}, filterK8Resources []utils.K8Resource) { - // get the policy struct from interface - unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - if err != nil { - glog.Error(err) - return - } - policy := v1alpha1.Policy{} - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr, &policy); err != nil { - glog.Error(err) - return - } - // Get the resources that apply to the policy - resourceMap := engine.ListResourcesThatApplyToPolicy(client, &policy, filterK8Resources) - // remove annotations for the resources - for _, obj := range resourceMap { - // get annotations - - ann := obj.Resource.GetAnnotations() - - _, patch, err := annotations.RemovePolicyJSONPatch(ann, annotations.BuildKey(policy.Name)) - if err != nil { - glog.Error(err) - continue - } - // patch the resource - _, err = client.PatchResource(obj.Resource.GetKind(), obj.Resource.GetNamespace(), obj.Resource.GetName(), patch) - if err != nil { - glog.Error(err) - continue - } - } -} diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go deleted file mode 100644 index e526d715c2..0000000000 --- a/pkg/controller/controller.go +++ /dev/null @@ -1,333 +0,0 @@ -package controller - -import ( - "fmt" - "reflect" - "time" - - jsonpatch "github.com/evanphx/json-patch" - - "github.com/nirmata/kyverno/pkg/annotations" - "github.com/nirmata/kyverno/pkg/info" - "github.com/nirmata/kyverno/pkg/utils" - - "github.com/nirmata/kyverno/pkg/engine" - - "github.com/golang/glog" - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" - "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/sharedinformer" - violation "github.com/nirmata/kyverno/pkg/violation" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -//PolicyController to manage Policy CRD -type PolicyController struct { - client *client.Client - policyLister lister.PolicyLister - policySynced cache.InformerSynced - violationBuilder violation.Generator - eventController event.Generator - annotationsController annotations.Controller - queue workqueue.RateLimitingInterface - filterK8Resources []utils.K8Resource -} - -// NewPolicyController from cmd args -func NewPolicyController(client *client.Client, - policyInformer sharedinformer.PolicyInformer, - violationBuilder violation.Generator, - eventController event.Generator, - annotationsController annotations.Controller, - filterK8Resources string) *PolicyController { - - controller := &PolicyController{ - client: client, - policyLister: policyInformer.GetLister(), - policySynced: policyInformer.GetInfomer().HasSynced, - violationBuilder: violationBuilder, - eventController: eventController, - annotationsController: annotationsController, - filterK8Resources: utils.ParseKinds(filterK8Resources), - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), policyWorkQueueName), - } - - policyInformer.GetInfomer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.createPolicyHandler, - UpdateFunc: controller.updatePolicyHandler, - DeleteFunc: controller.deletePolicyHandler, - }) - return controller -} - -func (pc *PolicyController) createPolicyHandler(resource interface{}) { - pc.enqueuePolicy(resource) -} - -func (pc *PolicyController) updatePolicyHandler(oldResource, newResource interface{}) { - newPolicy := newResource.(*v1alpha1.Policy) - oldPolicy := oldResource.(*v1alpha1.Policy) - newPolicy.Status = v1alpha1.Status{} - oldPolicy.Status = v1alpha1.Status{} - newPolicy.ResourceVersion = "" - oldPolicy.ResourceVersion = "" - if reflect.DeepEqual(newPolicy, oldPolicy) { - return - } - pc.enqueuePolicy(newResource) -} - -func (pc *PolicyController) deletePolicyHandler(resource interface{}) { - var object metav1.Object - var ok bool - if object, ok = resource.(metav1.Object); !ok { - glog.Error("error decoding object, invalid type") - return - } - cleanAnnotations(pc.client, resource, pc.filterK8Resources) - glog.Infof("policy deleted: %s", object.GetName()) -} - -func (pc *PolicyController) enqueuePolicy(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - glog.Error(err) - return - } - pc.queue.Add(key) -} - -// Run is main controller thread -func (pc *PolicyController) Run(stopCh <-chan struct{}) error { - defer utilruntime.HandleCrash() - - if ok := cache.WaitForCacheSync(stopCh, pc.policySynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - for i := 0; i < policyControllerWorkerCount; i++ { - go wait.Until(pc.runWorker, time.Second, stopCh) - } - glog.Info("started policy controller workers") - - return nil -} - -//Stop to perform actions when controller is stopped -func (pc *PolicyController) Stop() { - defer pc.queue.ShutDown() - glog.Info("shutting down policy controller workers") -} -func (pc *PolicyController) runWorker() { - for pc.processNextWorkItem() { - } -} - -func (pc *PolicyController) processNextWorkItem() bool { - obj, shutdown := pc.queue.Get() - if shutdown { - return false - } - - err := func(obj interface{}) error { - defer pc.queue.Done(obj) - err := pc.syncHandler(obj) - pc.handleErr(err, obj) - return nil - }(obj) - if err != nil { - glog.Error(err) - return true - } - return true -} - -func (pc *PolicyController) handleErr(err error, key interface{}) { - if err == nil { - pc.queue.Forget(key) - return - } - // This controller retries if something goes wrong. After that, it stops trying. - if pc.queue.NumRequeues(key) < policyWorkQueueRetryLimit { - glog.Warningf("Error syncing events %v: %v", key, err) - // Re-enqueue the key rate limited. Based on the rate limiter on the - // queue and the re-enqueue history, the key will be processed later again. - pc.queue.AddRateLimited(key) - return - } - pc.queue.Forget(key) - glog.Error(err) - glog.Warningf("Dropping the key out of the queue: %v", err) -} - -func (pc *PolicyController) syncHandler(obj interface{}) error { - var key string - var ok bool - if key, ok = obj.(string); !ok { - return fmt.Errorf("expected string in workqueue but got %#v", obj) - } - _, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - glog.Errorf("invalid policy key: %s", key) - return nil - } - // Get Policy - policy, err := pc.policyLister.Get(name) - if err != nil { - if errors.IsNotFound(err) { - glog.Errorf("policy '%s' in work queue no longer exists", key) - return nil - } - return err - } - - glog.Infof("process policy %s on existing resources", policy.GetName()) - // Process policy on existing resources - policyInfos := engine.ProcessExisting(pc.client, policy, pc.filterK8Resources) - - events, violations := pc.createEventsAndViolations(policyInfos) - // Events, Violations - pc.eventController.Add(events...) - err = pc.violationBuilder.Add(violations...) - if err != nil { - glog.Error(err) - } - - // Annotations - pc.createAnnotations(policyInfos) - - return nil -} - -func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) { - for _, pi := range policyInfos { - var patch []byte - //get resource - obj, err := pc.client.GetResource(pi.RKind, pi.RNamespace, pi.RName) - if err != nil { - glog.Error(err) - continue - } - // add annotation for policy application - ann := obj.GetAnnotations() - // Mutation rules - ann, mpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Mutation) - if err != nil { - glog.Error(err) - continue - } - // Validation rules - ann, vpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Validation) - if err != nil { - glog.Error(err) - } - - // Generation rules - ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Generation) - if err != nil { - glog.Error(err) - } - - if mpatch == nil && vpatch == nil && gpatch == nil { - //nothing to patch - continue - } - // merge the patches - if mpatch != nil && vpatch != nil { - patch, err = jsonpatch.MergePatch(mpatch, vpatch) - if err != nil { - glog.Error(err) - continue - } - } - - if mpatch == nil { - patch = vpatch - } else { - patch = mpatch - } - - if gpatch != nil { - // generation - if patch != nil { - patch, err = jsonpatch.MergePatch(patch, gpatch) - if err != nil { - glog.Error(err) - continue - } - } else { - patch = gpatch - } - } - - if patch == nil { - return - } - - // add the anotation to the resource - _, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch) - if err != nil { - glog.Error(err) - continue - } - } -} - -func (pc *PolicyController) createEventsAndViolations(policyInfos []*info.PolicyInfo) ([]*event.Info, []*violation.Info) { - events := []*event.Info{} - violations := []*violation.Info{} - // Create events from the policyInfo - for _, policyInfo := range policyInfos { - frules := []v1alpha1.FailedRule{} - sruleNames := []string{} - - for _, rule := range policyInfo.Rules { - if !rule.IsSuccessful() { - e := &event.Info{} - frule := v1alpha1.FailedRule{Name: rule.Name} - switch rule.RuleType { - case info.Mutation, info.Validation, info.Generation: - // Events - e = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation, event.FProcessRule, rule.Name, policyInfo.Name) - switch rule.RuleType { - case info.Mutation: - frule.Type = info.Mutation.String() - case info.Validation: - frule.Type = info.Validation.String() - case info.Generation: - frule.Type = info.Generation.String() - } - frule.Error = rule.GetErrorString() - default: - glog.Info("Unsupported Rule type") - } - frule.Error = rule.GetErrorString() - frules = append(frules, frule) - events = append(events, e) - } else { - sruleNames = append(sruleNames, rule.Name) - } - } - - if !policyInfo.IsSuccessful() { - e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyViolation, event.FResourcePolcy, policyInfo.RNamespace+"/"+policyInfo.RName, concatFailedRules(frules)) - events = append(events, e) - // Violation - v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.GetFailedRules()) - violations = append(violations, v) - } else { - // clean up violations - pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Mutation) - pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Validation) - } - } - return events, violations -} diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go deleted file mode 100644 index 4163d08fdf..0000000000 --- a/pkg/controller/controller_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package controller - -import ( - "testing" - - "github.com/golang/glog" - types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" - event "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/sharedinformer" - violation "github.com/nirmata/kyverno/pkg/violation" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/sample-controller/pkg/signals" -) - -func TestCreatePolicy(t *testing.T) { - f := newFixture(t) - // new policy is added to policy lister and explictly passed to sync-handler - // to process the existing - policy := newPolicy("test-policy") - f.policyLister = append(f.policyLister, policy) - f.objects = append(f.objects, policy) - // run controller - f.runControler("test-policy") -} - -func (f *fixture) runControler(policyName string) { - policyInformerFactory, err := sharedinformer.NewFakeSharedInformerFactory() - if err != nil { - f.t.Fatal(err) - } - - eventController := event.NewEventController(f.Client, policyInformerFactory) - violationBuilder := violation.NewPolicyViolationBuilder(f.Client, policyInformerFactory, eventController) - - // new controller - policyController := NewPolicyController( - f.Client, - policyInformerFactory, - violationBuilder, - eventController, - nil, - "") - - stopCh := signals.SetupSignalHandler() - // start informer & controller - policyInformerFactory.Run(stopCh) - if err = policyController.Run(stopCh); err != nil { - glog.Fatalf("Error running PolicyController: %v\n", err) - } - // add policy to the informer - for _, p := range f.policyLister { - policyInformerFactory.GetInfomer().GetIndexer().Add(p) - } - - // sync handler - // reads the policy from the policy lister and processes them - err = policyController.syncHandler(policyName) - if err != nil { - f.t.Fatal(err) - } - policyController.Stop() - -} - -type fixture struct { - t *testing.T - Client *client.Client - policyLister []*types.Policy - objects []runtime.Object -} - -func newFixture(t *testing.T) *fixture { - - // init groupversion - regResource := []schema.GroupVersionResource{ - schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"}, - schema.GroupVersionResource{Group: "group2", Version: "version", Resource: "thekinds"}, - schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}, - schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, - } - - objects := []runtime.Object{newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), - newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"), - newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"), - newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"), - newUnstructured("group2/version", "TheKind", "ns-foo", "name2-baz"), - newUnstructured("apps/v1", "Deployment", "kyverno", "kyverno"), - } - - scheme := runtime.NewScheme() - // Create mock client - fclient, err := client.NewMockClient(scheme, objects...) - if err != nil { - t.Fatal(err) - } - - // set discovery Client - fclient.SetDiscovery(client.NewFakeDiscoveryClient(regResource)) - - f := &fixture{ - t: t, - Client: fclient, - } - - return f -} - -// create mock client with initial resouces -// set registered resources for gvr -func (f *fixture) setupFixture() { - scheme := runtime.NewScheme() - fclient, err := client.NewMockClient(scheme, f.objects...) - if err != nil { - f.t.Fatal(err) - } - - regresource := []schema.GroupVersionResource{ - schema.GroupVersionResource{Group: "kyverno.io", - Version: "v1alpha1", - Resource: "policys"}} - fclient.SetDiscovery(client.NewFakeDiscoveryClient(regresource)) -} - -func newPolicy(name string) *types.Policy { - return &types.Policy{ - TypeMeta: metav1.TypeMeta{APIVersion: types.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - } -} - -func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured { - return &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": apiVersion, - "kind": kind, - "metadata": map[string]interface{}{ - "namespace": namespace, - "name": name, - }, - }, - } -} diff --git a/pkg/controller/utils.go b/pkg/controller/utils.go deleted file mode 100644 index 2501addb51..0000000000 --- a/pkg/controller/utils.go +++ /dev/null @@ -1,21 +0,0 @@ -package controller - -import ( - "bytes" - - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" -) - -const policyWorkQueueName = "policyworkqueue" - -const policyWorkQueueRetryLimit = 3 - -const policyControllerWorkerCount = 2 - -func concatFailedRules(frules []v1alpha1.FailedRule) string { - var buffer bytes.Buffer - for _, frule := range frules { - buffer.WriteString(frule.Name + ";") - } - return buffer.String() -} diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go index fb591b95b2..50a1f1d3e2 100644 --- a/pkg/dclient/client.go +++ b/pkg/dclient/client.go @@ -6,7 +6,7 @@ import ( "time" "github.com/golang/glog" - types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/config" apps "k8s.io/api/apps/v1" certificates "k8s.io/api/certificates/v1beta1" @@ -184,7 +184,7 @@ func convertToUnstructured(obj interface{}) *unstructured.Unstructured { } // GenerateResource creates resource of the specified kind(supports 'clone' & 'data') -func (c *Client) GenerateResource(generator types.Generation, namespace string, processExistingResources bool) error { +func (c *Client) GenerateResource(generator kyverno.Generation, namespace string, processExistingResources bool) error { var err error resource := &unstructured.Unstructured{} @@ -198,7 +198,7 @@ func (c *Client) GenerateResource(generator types.Generation, namespace string, } } // clone -> copy from existing resource - if generator.Clone != nil { + if generator.Clone != (kyverno.CloneFrom{}) { resource, err = c.GetResource(generator.Kind, generator.Clone.Namespace, generator.Clone.Name) if err != nil { return err diff --git a/pkg/engine/overlayPatch.go b/pkg/engine/overlayPatch.go index 73c8ad9f47..d71896b5c7 100644 --- a/pkg/engine/overlayPatch.go +++ b/pkg/engine/overlayPatch.go @@ -5,17 +5,17 @@ import ( "strings" "github.com/golang/glog" - kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" ) -func patchOverlay(rule kubepolicy.Rule, rawResource []byte) ([][]byte, error) { +func patchOverlay(rule kyverno.Rule, rawResource []byte) ([][]byte, error) { var resource interface{} if err := json.Unmarshal(rawResource, &resource); err != nil { return nil, err } resourceInfo := ParseResourceInfoFromObject(rawResource) - patches, err := processOverlayPatches(resource, *rule.Mutation.Overlay) + patches, err := processOverlayPatches(resource, rule.Mutation.Overlay) if err != nil && strings.Contains(err.Error(), "Conditions are not met") { glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name) return nil, nil diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index 6e18124d8f..de13961155 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -517,13 +517,3 @@ type resourceInfo struct { Resource unstructured.Unstructured Gvk *metav1.GroupVersionKind } - -func convertToUnstructured(data []byte) (*unstructured.Unstructured, error) { - resource := &unstructured.Unstructured{} - err := resource.UnmarshalJSON(data) - if err != nil { - glog.V(4).Infof("failed to unmarshall resource: %v", err) - return nil, err - } - return resource, nil -} diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index d850820e3c..665c986feb 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -20,10 +20,7 @@ import ( func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) { //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information //TODO: pass unstructured instead of rawResource ? - // resourceUnstr, err := convertToUnstructured(rawResource) - // if err != nil { - // glog.Errorf("unable to convert raw resource to unstructured: %v", err) - // } + resourceRaw, err := resource.MarshalJSON() if err != nil { glog.V(4).Infof("unable to marshal resource : %v", err) diff --git a/pkg/event/controller.go b/pkg/event/controller.go index 491f48bc85..6fc0d57a0f 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -4,8 +4,9 @@ import ( "time" "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" - policyscheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + + "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" + policyscheme "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" diff --git a/pkg/gencontroller/controller.go b/pkg/gencontroller/controller.go index 157e65b15b..8709e49110 100644 --- a/pkg/gencontroller/controller.go +++ b/pkg/gencontroller/controller.go @@ -7,12 +7,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/annotations" - policyLister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" - policySharedInformer "github.com/nirmata/kyverno/pkg/sharedinformer" - "github.com/nirmata/kyverno/pkg/violation" "k8s.io/apimachinery/pkg/api/errors" v1Informer "k8s.io/client-go/informers/core/v1" diff --git a/pkg/gencontroller/generation.go b/pkg/gencontroller/generation.go index 76c03792e8..dda48b0fe4 100644 --- a/pkg/gencontroller/generation.go +++ b/pkg/gencontroller/generation.go @@ -9,12 +9,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/annotations" - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" "github.com/nirmata/kyverno/pkg/engine" event "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/info" - violation "github.com/nirmata/kyverno/pkg/violation" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -106,7 +103,7 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) { if onViolation { glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name) // Policy Violation - v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.GetFailedRules()) + v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.FailedRules()) c.violationBuilder.Add(v) } else { // Event diff --git a/pkg/gencontroller/utils.go b/pkg/gencontroller/utils.go index 86969313d8..609d53f917 100644 --- a/pkg/gencontroller/utils.go +++ b/pkg/gencontroller/utils.go @@ -2,7 +2,6 @@ package gencontroller import ( "github.com/minio/minio/pkg/wildcard" - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" diff --git a/pkg/info/info.go b/pkg/info/info.go index 1118aa4c4e..d8b742e350 100644 --- a/pkg/info/info.go +++ b/pkg/info/info.go @@ -3,8 +3,6 @@ package info import ( "fmt" "strings" - - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" ) //PolicyInfo defines policy information @@ -72,17 +70,6 @@ func (pi *PolicyInfo) FailedRules() []string { return rules } -//GetFailedRules returns the failed rules with rule type -func (pi *PolicyInfo) GetFailedRules() []v1alpha1.FailedRule { - var rules []v1alpha1.FailedRule - for _, r := range pi.Rules { - if !r.IsSuccessful() { - rules = append(rules, v1alpha1.FailedRule{Name: r.Name, Type: r.RuleType.String(), Error: r.GetErrorString()}) - } - } - return rules -} - //ErrorRules returns error msgs from all rule func (pi *PolicyInfo) ErrorRules() string { errorMsgs := []string{} @@ -165,7 +152,7 @@ func (ri *RuleInfo) Addf(msg string, args ...interface{}) { } //RulesSuccesfuly check if the any rule has failed or not -func RulesSuccesfuly(rules []RuleInfo) bool { +func rulesSuccesfuly(rules []RuleInfo) bool { for _, r := range rules { if !r.success { return false @@ -179,7 +166,7 @@ func (pi *PolicyInfo) AddRuleInfos(rules []RuleInfo) { if rules == nil { return } - if !RulesSuccesfuly(rules) { + if !rulesSuccesfuly(rules) { pi.success = false } @@ -203,13 +190,3 @@ func (pi *PolicyInfo) GetRuleNames(onSuccess bool) string { return strings.Join(ruleNames, ",") } - -//ContainsRuleType checks if a policy info contains a rule type -func (pi *PolicyInfo) ContainsRuleType(ruleType RuleType) bool { - for _, r := range pi.Rules { - if r.RuleType == ruleType { - return true - } - } - return false -} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 26fc4401b9..f756c5a999 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -588,12 +588,14 @@ func (m *PolicyViolationControllerRefManager) releasePolicyViolation(pv *kyverno return err } +//PolicyViolationControllerRefManager manages adoption of policy violation by a policy type PolicyViolationControllerRefManager struct { BaseControllerRefManager controllerKind schema.GroupVersionKind pvControl PVControlInterface } +//NewPolicyViolationControllerRefManager returns new PolicyViolationControllerRefManager func NewPolicyViolationControllerRefManager( pvControl PVControlInterface, controller metav1.Object, @@ -614,6 +616,7 @@ func NewPolicyViolationControllerRefManager( return &m } +//BaseControllerRefManager ... type BaseControllerRefManager struct { Controller metav1.Object Selector labels.Selector @@ -622,6 +625,7 @@ type BaseControllerRefManager struct { CanAdoptFunc func() error } +//CanAdopt ... func (m *BaseControllerRefManager) CanAdopt() error { m.canAdoptOnce.Do(func() { if m.CanAdoptFunc != nil { @@ -631,6 +635,7 @@ func (m *BaseControllerRefManager) CanAdopt() error { return m.canAdoptErr } +//ClaimObject ... func (m *BaseControllerRefManager) ClaimObject(obj metav1.Object, match func(metav1.Object) bool, adopt, release func(metav1.Object) error) (bool, error) { controllerRef := metav1.GetControllerOf(obj) if controllerRef != nil { @@ -687,6 +692,7 @@ func (m *BaseControllerRefManager) ClaimObject(obj metav1.Object, match func(met } +//PVControlInterface provides interface to operate on policy violation resource type PVControlInterface interface { PatchPolicyViolation(name string, data []byte) error } @@ -697,6 +703,7 @@ type RealPVControl struct { Recorder record.EventRecorder } +//PatchPolicyViolation patches the policy violation with the provided JSON Patch func (r RealPVControl) PatchPolicyViolation(name string, data []byte) error { _, err := r.Client.KyvernoV1alpha1().PolicyViolations().Patch(name, types.JSONPatchType, data) return err diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 629ad0fb58..b61ec78fd5 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -186,6 +186,7 @@ func getAllNamespaces(client *client.Client) []string { return namespaces } +//NewResourceManager returns a new ResourceManager func NewResourceManager(rebuildTime int64) *ResourceManager { rm := ResourceManager{ data: make(map[string]interface{}), @@ -196,7 +197,7 @@ func NewResourceManager(rebuildTime int64) *ResourceManager { return &rm } -// ResourceManager +// ResourceManager stores the details on already processed resources for caching type ResourceManager struct { // we drop and re-build the cache // based on the memory consumer of by the map diff --git a/pkg/policy/report.go b/pkg/policy/report.go index f3b80097a9..1005e98b2c 100644 --- a/pkg/policy/report.go +++ b/pkg/policy/report.go @@ -3,6 +3,7 @@ package policy import ( "fmt" + "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/policyviolation" @@ -15,22 +16,22 @@ func (pc *PolicyController) report(policyInfos []info.PolicyInfo) { // events // success - policy applied on resource // failure - policy/rule failed to apply on the resource - reportPolicy(policyInfo, pc.eventGen) + reportEvents(policyInfo, pc.eventGen) // policy violations // failure - policy/rule failed to apply on the resource } // generate policy violation policyviolation.GeneratePolicyViolations(pc.pvListerSynced, pc.pvLister, pc.kyvernoClient, policyInfos) - } -func reportPolicy(policyInfo info.PolicyInfo, eventGen event.Interface) { +//reportEvents generates events for the failed resources +func reportEvents(policyInfo info.PolicyInfo, eventGen event.Interface) { if policyInfo.IsSuccessful() { return } - + glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName) for _, rule := range policyInfo.Rules { if rule.IsSuccessful() { continue @@ -45,8 +46,6 @@ func reportPolicy(policyInfo info.PolicyInfo, eventGen event.Interface) { e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", policyInfo.Name, rule.RuleType.String(), rule.Name, rule.GetErrorString()) eventGen.Add(e) - // generate policy violation for each failed rule - } // generate a event on policy for all failed rules e := &event.Info{} diff --git a/pkg/policyviolation/controller.go b/pkg/policyviolation/controller.go index 3c30229521..0c1ff50741 100644 --- a/pkg/policyviolation/controller.go +++ b/pkg/policyviolation/controller.go @@ -211,7 +211,7 @@ func (pvc *PolicyViolationController) syncPolicyViolation(key string) error { // TODO: Deep-copy only when needed. pv := policyViolation.DeepCopy() // TODO: Update Status to update ObserverdGeneration - // TODO: check if the policy violation refers to a resource thats active ? + // TODO: check if the policy violation refers to a resource thats active ? // done by policy controller // TODO: additional check on deleted webhook for a resource, to delete a policy violation it has a policy violation // list the resource with label selectors, but this can be expensive for each delete request of a resource if err := pvc.syncActiveResource(pv); err != nil { diff --git a/pkg/result/reason.go b/pkg/result/reason.go deleted file mode 100644 index cb786880fb..0000000000 --- a/pkg/result/reason.go +++ /dev/null @@ -1,21 +0,0 @@ -package result - -//Reason types of Result Reasons -type Reason int - -const ( - //Success policy applied - Success Reason = iota - //Violation there is a violation of policy - Violation - //Failed the request to create/update the resource was blocked(generated from admission-controller) - Failed -) - -func (r Reason) String() string { - return [...]string{ - "Success", - "Violation", - "Failed", - }[r] -} diff --git a/pkg/result/result.go b/pkg/result/result.go deleted file mode 100644 index a7ed1a2878..0000000000 --- a/pkg/result/result.go +++ /dev/null @@ -1,182 +0,0 @@ -package result - -import ( - "fmt" -) - -// Indent acts for indenting in result hierarchy -type Indent string - -const ( - // SpaceIndent means 4 spaces - SpaceIndent Indent = " " - // TabIndent is a tab symbol - TabIndent Indent = "\t" -) - -// Result is an interface that is used for result polymorphic behavior -type Result interface { - String() string - StringWithIndent(indent string) string - GetReason() Reason - ToError() error -} - -// CompositeResult is used for result hierarchy -type CompositeResult struct { - Message string - Reason Reason - Children []Result -} - -// RuleApplicationResult represents elementary result that is produced by PolicyEngine -// TODO: It can be used to create Kubernetes Results, so make method for this -type RuleApplicationResult struct { - PolicyRule string - Reason Reason - Messages []string -} - -//NewRuleApplicationResult creates a new rule application result -func NewRuleApplicationResult(ruleName string) RuleApplicationResult { - return RuleApplicationResult{ - PolicyRule: ruleName, - Reason: Success, - Messages: []string{}, - } -} - -// StringWithIndent makes result string where each -// line is prepended with specified indent -func (e *RuleApplicationResult) StringWithIndent(indent string) string { - message := fmt.Sprintf("%s* %s: policy rule - %s:\n", indent, e.Reason.String(), e.PolicyRule) - childrenIndent := indent + string(SpaceIndent) - for i, m := range e.Messages { - message += fmt.Sprintf("%s%d. %s\n", childrenIndent, i+1, m) - } - - // remove last line feed - if 0 != len(message) { - message = message[:len(message)-1] - } - return message -} - -// String makes result string -// for writing it to logs -func (e *RuleApplicationResult) String() string { - return e.StringWithIndent("") -} - -// ToError returns the error if reason is not success -func (e *RuleApplicationResult) ToError() error { - if e.Reason != Success { - return fmt.Errorf(e.String()) - } - return nil -} - -//GetReason returns reason -func (e *RuleApplicationResult) GetReason() Reason { - return e.Reason -} - -//AddMessagef Adds formatted message to this result -func (e *RuleApplicationResult) AddMessagef(message string, a ...interface{}) { - e.Messages = append(e.Messages, fmt.Sprintf(message, a...)) -} - -//FailWithMessagef Sets the Reason Failed and adds formatted message to this result -func (e *RuleApplicationResult) FailWithMessagef(message string, a ...interface{}) { - e.Reason = Failed - e.AddMessagef(message, a...) -} - -//MergeWith Takes messages and higher reason from another RuleApplicationResult -func (e *RuleApplicationResult) MergeWith(other *RuleApplicationResult) { - if other != nil { - e.Messages = append(e.Messages, other.Messages...) - } - if other.Reason > e.Reason { - e.Reason = other.Reason - } -} - -// StringWithIndent makes result string where each -// line is prepended with specified indent -func (e *CompositeResult) StringWithIndent(indent string) string { - childrenIndent := indent + string(SpaceIndent) - message := fmt.Sprintf("%s- %s: %s\n", indent, e.Reason, e.Message) - for _, res := range e.Children { - message += (res.StringWithIndent(childrenIndent) + "\n") - } - - // remove last line feed - if 0 != len(message) { - message = message[:len(message)-1] - } - - return message -} - -// String makes result string -// for writing it to logs -func (e *CompositeResult) String() string { - return e.StringWithIndent("") -} - -//ToError returns error if reason is not success -func (e *CompositeResult) ToError() error { - if e.Reason != Success { - return fmt.Errorf(e.String()) - } - return nil -} - -//GetReason returns reason -func (e *CompositeResult) GetReason() Reason { - return e.Reason -} - -//NewPolicyApplicationResult creates a new policy application result -func NewPolicyApplicationResult(policyName string) Result { - return &CompositeResult{ - Message: fmt.Sprintf("policy - %s:", policyName), - Reason: Success, - } -} - -//NewAdmissionResult creates a new admission result -func NewAdmissionResult(requestUID string) Result { - return &CompositeResult{ - Message: fmt.Sprintf("For resource with UID - %s:", requestUID), - Reason: Success, - } -} - -// Append returns CompositeResult with target and source -// Or appends source to target if it is composite result -// If the source reason is more important than target reason, -// target takes the reason of the source. -func Append(target Result, source Result) Result { - targetReason := target.GetReason() - if targetReason < source.GetReason() { - targetReason = source.GetReason() - } - - if composite, ok := target.(*CompositeResult); ok { - composite.Children = append(composite.Children, source) - composite.Reason = targetReason - return composite - } - - composite := &CompositeResult{ - Children: []Result{ - target, - source, - }, - Reason: targetReason, - } - - return composite -} diff --git a/pkg/result/result_test.go b/pkg/result/result_test.go deleted file mode 100644 index 06ee44921d..0000000000 --- a/pkg/result/result_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package result - -import ( - "testing" - - "gotest.tools/assert" -) - -func TestAppend_TwoResultObjects(t *testing.T) { - firstRuleApplicationResult := RuleApplicationResult{ - Reason: Failed, - Messages: []string{ - "1. Test", - "2. Toast", - }, - } - - secondRuleApplicationResult := RuleApplicationResult{ - Reason: Success, - Messages: []string{ - "1. Kyverno", - "2. KubePolicy", - }, - } - - result := Append(&firstRuleApplicationResult, &secondRuleApplicationResult) - composite, ok := result.(*CompositeResult) - assert.Assert(t, ok) - assert.Equal(t, len(composite.Children), 2) - - RuleApplicationResult, ok := composite.Children[0].(*RuleApplicationResult) - assert.Assert(t, ok) - assert.Equal(t, RuleApplicationResult.Messages[1], "2. Toast") -} - -func TestAppend_FirstObjectIsComposite(t *testing.T) { - composite := &CompositeResult{} - - firstRuleApplicationResult := RuleApplicationResult{ - Reason: Failed, - Messages: []string{ - "1. Test", - "2. Toast", - }, - } - - result := Append(composite, &firstRuleApplicationResult) - composite, ok := result.(*CompositeResult) - assert.Equal(t, len(composite.Children), 1) - - RuleApplicationResult, ok := composite.Children[0].(*RuleApplicationResult) - assert.Assert(t, ok) - assert.Equal(t, RuleApplicationResult.Messages[1], "2. Toast") -} diff --git a/pkg/sharedinformer/sharedinformerfactory.go b/pkg/sharedinformer/sharedinformerfactory.go deleted file mode 100644 index 9676048aa9..0000000000 --- a/pkg/sharedinformer/sharedinformerfactory.go +++ /dev/null @@ -1,57 +0,0 @@ -package sharedinformer - -import ( - "fmt" - - policyclientset "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - informers "github.com/nirmata/kyverno/pkg/client/informers/externalversions" - infomertypes "github.com/nirmata/kyverno/pkg/client/informers/externalversions/policy/v1alpha1" - v1alpha1 "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" -) - -//PolicyInformer access policy informers -type PolicyInformer interface { - GetLister() v1alpha1.PolicyLister - GetInfomer() cache.SharedIndexInformer -} - -// SharedInfomer access shared informers -type SharedInfomer interface { - PolicyInformer - Run(stopCh <-chan struct{}) -} - -type sharedInfomer struct { - policyInformerFactory informers.SharedInformerFactory -} - -//NewSharedInformerFactory returns shared informer -func NewSharedInformerFactory(clientConfig *rest.Config) (SharedInfomer, error) { - // create policy client - policyClientset, err := policyclientset.NewForConfig(clientConfig) - if err != nil { - return nil, fmt.Errorf("Error creating policyClient: %v\n", err) - } - //TODO: replace with NewSharedInformerFactoryWithOptions - policyInformerFactory := informers.NewSharedInformerFactory(policyClientset, 0) - return &sharedInfomer{ - policyInformerFactory: policyInformerFactory, - }, nil -} - -func (si *sharedInfomer) Run(stopCh <-chan struct{}) { - si.policyInformerFactory.Start(stopCh) -} - -func (si *sharedInfomer) getInfomer() infomertypes.PolicyInformer { - return si.policyInformerFactory.Kyverno().V1alpha1().Policies() -} -func (si *sharedInfomer) GetInfomer() cache.SharedIndexInformer { - return si.getInfomer().Informer() -} - -func (si *sharedInfomer) GetLister() v1alpha1.PolicyLister { - return si.getInfomer().Lister() -} diff --git a/pkg/sharedinformer/utils.go b/pkg/sharedinformer/utils.go deleted file mode 100644 index 804be21665..0000000000 --- a/pkg/sharedinformer/utils.go +++ /dev/null @@ -1,15 +0,0 @@ -package sharedinformer - -import ( - "github.com/nirmata/kyverno/pkg/client/clientset/versioned/fake" - informers "github.com/nirmata/kyverno/pkg/client/informers/externalversions" - "k8s.io/apimachinery/pkg/runtime" -) - -func NewFakeSharedInformerFactory(objects ...runtime.Object) (SharedInfomer, error) { - fakePolicyClient := fake.NewSimpleClientset(objects...) - policyInformerFactory := informers.NewSharedInformerFactory(fakePolicyClient, 0) - return &sharedInfomer{ - policyInformerFactory: policyInformerFactory, - }, nil -} diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 19b728babd..2b6d4a90e6 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -4,13 +4,17 @@ import ( "regexp" "strings" - "github.com/golang/glog" - "github.com/minio/minio/pkg/wildcard" "k8s.io/api/admission/v1beta1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +type K8Resource struct { + Kind string //TODO: as we currently only support one GVK version, we use the kind only. But if we support multiple GVK, then GV need to be added + Namespace string + Name string +} + +//Contains Check if strint is contained in a list of string func Contains(list []string, element string) bool { for _, e := range list { if e == element { @@ -20,12 +24,6 @@ func Contains(list []string, element string) bool { return false } -type K8Resource struct { - Kind string //TODO: as we currently only support one GVK version, we use the kind only. But if we support multiple GVK, then GV need to be added - Namespace string - Name string -} - //SkipFilteredResourcesReq checks if request is to be skipped based on filtered kinds func SkipFilteredResourcesReq(request *v1beta1.AdmissionRequest, filterK8Resources []K8Resource) bool { kind := request.Kind.Kind @@ -77,14 +75,3 @@ func ParseKinds(list string) []K8Resource { } return resources } - -//ConvertToUnstructured coverts a raw resource into unstructured struct -func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) { - resource := &unstructured.Unstructured{} - err := resource.UnmarshalJSON(data) - if err != nil { - glog.V(4).Infof("failed to unmarshall resource: %v", err) - return nil, err - } - return resource, nil -} diff --git a/pkg/violation/builder.go b/pkg/violation/builder.go deleted file mode 100644 index 40da777d33..0000000000 --- a/pkg/violation/builder.go +++ /dev/null @@ -1,276 +0,0 @@ -package violation - -import ( - "errors" - - "github.com/golang/glog" - - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" - event "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/info" - "github.com/nirmata/kyverno/pkg/sharedinformer" - "k8s.io/apimachinery/pkg/runtime" -) - -//Generator to generate policy violation -type Generator interface { - Add(infos ...*Info) error - RemoveInactiveViolation(policy, rKind, rNs, rName string, ruleType info.RuleType) error - ResourceRemoval(policy, rKind, rNs, rName string) error -} - -type builder struct { - client *client.Client - policyLister lister.PolicyLister - eventBuilder event.Generator -} - -//Builder is to build policy violations -type Builder interface { - Generator - processViolation(info *Info) error -} - -//NewPolicyViolationBuilder returns new violation builder -func NewPolicyViolationBuilder(client *client.Client, - sharedInfomer sharedinformer.PolicyInformer, - eventController event.Generator) Builder { - - builder := &builder{ - client: client, - policyLister: sharedInfomer.GetLister(), - eventBuilder: eventController, - } - return builder -} - -//BuldNewViolation returns a new violation -func BuldNewViolation(pName string, rKind string, rNs string, rName string, reason string, frules []v1alpha1.FailedRule) *Info { - return &Info{ - Policy: pName, - Violation: v1alpha1.Violation{ - Kind: rKind, - Namespace: rNs, - Name: rName, - Reason: reason, - Rules: frules, - }, - } -} - -func (b *builder) Add(infos ...*Info) error { - if infos == nil { - return nil - } - for _, info := range infos { - err := b.processViolation(info) - if err != nil { - glog.Error(err) - } - } - return nil -} - -func (b *builder) processViolation(info *Info) error { - statusMap := map[string]interface{}{} - violationsMap := map[string]interface{}{} - violationMap := map[string]interface{}{} - var violations interface{} - var violation interface{} - // Get Policy - obj, err := b.client.GetResource("Policy", "", info.Policy, "status") - if err != nil { - return err - } - unstr := obj.UnstructuredContent() - // get "status" subresource - status, ok := unstr["status"] - if ok { - // status exists - // status is already present then we append violations - if statusMap, ok = status.(map[string]interface{}); !ok { - return errors.New("Unable to parse status subresource") - } - // get policy violations - violations, ok = statusMap["violations"] - if !ok { - return nil - } - violationsMap, ok = violations.(map[string]interface{}) - if !ok { - return errors.New("Unable to get status.violations subresource") - } - // check if the resource has a violation - violation, ok = violationsMap[info.getKey()] - if !ok { - // add resource violation - violationsMap[info.getKey()] = info.Violation - statusMap["violations"] = violationsMap - unstr["status"] = statusMap - } else { - violationMap, ok = violation.(map[string]interface{}) - if !ok { - return errors.New("Unable to get status.violations.violation subresource") - } - // we check if the new violation updates are different from stored violation info - v := v1alpha1.Violation{} - err := runtime.DefaultUnstructuredConverter.FromUnstructured(violationMap, &v) - if err != nil { - return err - } - // compare v & info.Violation - if v.IsEqual(info.Violation) { - // no updates to violation - // do nothing - return nil - } - // update the violation - violationsMap[info.getKey()] = info.Violation - statusMap["violations"] = violationsMap - unstr["status"] = statusMap - } - } else { - violationsMap[info.getKey()] = info.Violation - statusMap["violations"] = violationsMap - unstr["status"] = statusMap - } - - obj.SetUnstructuredContent(unstr) - // update the status sub-resource for policy - _, err = b.client.UpdateStatusResource("Policy", "", obj, false) - if err != nil { - return err - } - return nil -} - -//RemoveInactiveViolation -func (b *builder) RemoveInactiveViolation(policy, rKind, rNs, rName string, ruleType info.RuleType) error { - statusMap := map[string]interface{}{} - violationsMap := map[string]interface{}{} - violationMap := map[string]interface{}{} - var violations interface{} - var violation interface{} - // Get Policy - obj, err := b.client.GetResource("Policy", "", policy, "status") - if err != nil { - return err - } - unstr := obj.UnstructuredContent() - // get "status" subresource - status, ok := unstr["status"] - if !ok { - return nil - } - // status exists - // status is already present then we append violations - if statusMap, ok = status.(map[string]interface{}); !ok { - return errors.New("Unable to parse status subresource") - } - // get policy violations - violations, ok = statusMap["violations"] - if !ok { - return nil - } - violationsMap, ok = violations.(map[string]interface{}) - if !ok { - return errors.New("Unable to get status.violations subresource") - } - // check if the resource has a violation - violation, ok = violationsMap[BuildKey(rKind, rNs, rName)] - if !ok { - // no violation for this resource - return nil - } - violationMap, ok = violation.(map[string]interface{}) - if !ok { - return errors.New("Unable to get status.violations.violation subresource") - } - // check remove the rules of the given type - // this is called when the policy is applied succesfully, so we can remove the previous failed rules - // if all rules are to be removed, the deleted the violation - v := v1alpha1.Violation{} - err = runtime.DefaultUnstructuredConverter.FromUnstructured(violationMap, &v) - if err != nil { - return err - } - if !v.RemoveRulesOfType(ruleType.String()) { - // no rule of given type found, - // no need to remove rule - return nil - } - // if there are no faile rules remove the violation - if len(v.Rules) == 0 { - delete(violationsMap, BuildKey(rKind, rNs, rName)) - } else { - // update the rules - violationsMap[BuildKey(rKind, rNs, rName)] = v - } - statusMap["violations"] = violationsMap - unstr["status"] = statusMap - - obj.SetUnstructuredContent(unstr) - // update the status sub-resource for policy - _, err = b.client.UpdateStatusResource("Policy", "", obj, false) - if err != nil { - return err - } - return nil -} - -// ResourceRemoval on resources reoval we remove the policy violation in the policy -func (b *builder) ResourceRemoval(policy, rKind, rNs, rName string) error { - statusMap := map[string]interface{}{} - violationsMap := map[string]interface{}{} - var violations interface{} - // Get Policy - obj, err := b.client.GetResource("Policy", "", policy, "status") - if err != nil { - return err - } - unstr := obj.UnstructuredContent() - // get "status" subresource - status, ok := unstr["status"] - if !ok { - return nil - } - // status exists - // status is already present then we append violations - if statusMap, ok = status.(map[string]interface{}); !ok { - return errors.New("Unable to parse status subresource") - } - // get policy violations - violations, ok = statusMap["violations"] - if !ok { - return nil - } - violationsMap, ok = violations.(map[string]interface{}) - if !ok { - return errors.New("Unable to get status.violations subresource") - } - - // check if the resource has a violation - _, ok = violationsMap[BuildKey(rKind, rNs, rName)] - if !ok { - // no violation for this resource - return nil - } - // remove the pair from the map - delete(violationsMap, BuildKey(rKind, rNs, rName)) - if len(violationsMap) == 0 { - delete(statusMap, "violations") - } else { - statusMap["violations"] = violationsMap - } - unstr["status"] = statusMap - - obj.SetUnstructuredContent(unstr) - // update the status sub-resource for policy - _, err = b.client.UpdateStatusResource("Policy", "", obj, false) - if err != nil { - return err - } - return nil -} diff --git a/pkg/violation/util.go b/pkg/violation/util.go deleted file mode 100644 index 3f525b53d2..0000000000 --- a/pkg/violation/util.go +++ /dev/null @@ -1,27 +0,0 @@ -package violation - -import policytype "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - -// Source for the events recorder -const violationEventSource = "policy-controller" - -// Name for the workqueue to store the events -const workqueueViolationName = "Policy-Violations" - -// Event Reason -const violationEventResrouce = "Violation" - -//Info describes the policyviolation details -type Info struct { - Policy string - policytype.Violation -} - -func (i Info) getKey() string { - return i.Kind + "/" + i.Namespace + "/" + i.Name -} - -//BuildKey returns the key format -func BuildKey(rKind, rNs, rName string) string { - return rKind + "/" + rNs + "/" + rName -} diff --git a/pkg/webhooks/deleteresource.go b/pkg/webhooks/deleteresource.go deleted file mode 100644 index 61c62c0e1d..0000000000 --- a/pkg/webhooks/deleteresource.go +++ /dev/null @@ -1,38 +0,0 @@ -package webhooks - -import ( - v1beta1 "k8s.io/api/admission/v1beta1" -) - -func (ws *WebhookServer) removePolicyViolation(request *v1beta1.AdmissionRequest) error { - //TODO: ClenUp will be handled by the policycontroller - - // // Get the list of policies that apply on the resource - // policies, err := ws.policyLister.List(labels.NewSelector()) - // if err != nil { - // // Unable to connect to policy Lister to access policies - // return errors.New("Unable to connect to policy controller to access policies. Clean Up of Policy Violations is not being done") - // } - // for _, policy := range policies { - // // check if policy has a rule for the admission request kind - // if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { - // continue - // } - // // get the details from the request - // rname := request.Name - // rns := request.Namespace - // rkind := request.Kind.Kind - // // check if the resource meets the policy Resource description - // for _, rule := range policy.Spec.Rules { - // ok := engine.ResourceMeetsDescription(request.Object.Raw, rule.MatchResources.ResourceDescription, rule.ExcludeResources.ResourceDescription, request.Kind) - // if ok { - // // Check if the policy has a violation for this resource - // err := ws.violationBuilder.ResourceRemoval(policy.Name, rkind, rns, rname) - // if err != nil { - // return err - // } - // } - // } - // } - return nil -} diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 9fc6f83540..5e8333ce7e 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -81,7 +81,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be // TODO: merge the annotation patch with the patch response // ADD EVENTS if len(patches) > 0 { - eventsInfo, _ := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation) + eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation) ws.eventGen.Add(eventsInfo...) } // ADD POLICY VIOLATIONS diff --git a/pkg/webhooks/policyvalidation.go b/pkg/webhooks/policyvalidation.go index 873e7e5bfa..4f72e49c8b 100644 --- a/pkg/webhooks/policyvalidation.go +++ b/pkg/webhooks/policyvalidation.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/golang/glog" - policyv1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/utils" v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,7 +18,7 @@ func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionReques // Verify if the Rule names are unique within a policy func (ws *WebhookServer) validateUniqueRuleName(rawPolicy []byte) *v1beta1.AdmissionResponse { - var policy *policyv1.Policy + var policy *kyverno.Policy var ruleNames []string json.Unmarshal(rawPolicy, &policy) diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go index d9b8042834..f67e30b451 100644 --- a/pkg/webhooks/report.go +++ b/pkg/webhooks/report.go @@ -3,18 +3,14 @@ package webhooks import ( "strings" - "github.com/nirmata/kyverno/pkg/annotations" - "github.com/nirmata/kyverno/pkg/violation" - "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/info" ) //TODO: change validation from bool -> enum(validation, mutation) -func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, ruleType info.RuleType) ([]*event.Info, []*violation.Info) { +func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, ruleType info.RuleType) []*event.Info { var eventsInfo []*event.Info - var violations []*violation.Info ok, msg := isAdmSuccesful(policyInfoList) // Some policies failed to apply succesfully if !ok { @@ -38,12 +34,6 @@ func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyBlockResourceUpdate, pi.RName, ruleNames)) glog.V(3).Infof("Request blocked events info has prepared for %s/%s and %s/%s\n", policyKind, pi.Name, pi.RKind, pi.RName) } - // if report flag is set - if pi.ValidationFailureAction == ReportViolation && ruleType == info.Validation { - // Create Violations - v := violation.BuldNewViolation(pi.Name, pi.RKind, pi.RNamespace, pi.RName, event.PolicyViolation.String(), pi.GetFailedRules()) - violations = append(violations, v) - } } } else { if !onUpdate { @@ -59,149 +49,5 @@ func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, } } } - return eventsInfo, violations + return eventsInfo } - -func addAnnotationsToResource(rawResource []byte, pi *info.PolicyInfo, ruleType info.RuleType) []byte { - if len(pi.Rules) == 0 { - return nil - } - // get annotations - ann := annotations.ParseAnnotationsFromObject(rawResource) - ann, patch, err := annotations.AddPolicyJSONPatch(ann, pi, ruleType) - if err != nil { - glog.Error(err) - return nil - } - return patch -} - -//buildAnnotation we add annotations for the successful application of JSON patches -//TODO -func buildAnnotation(mAnn map[string]string, pi *info.PolicyInfo) { - if len(pi.Rules) == 0 { - return - } - var mchanges []string - - for _, r := range pi.Rules { - if r.Changes != "" { - // the rule generate a patch - // key policy name will be updated to right format during creation of annotations - mchanges = append(mchanges) - } - } -} - -// // buildPolicyViolationsForAPolicy returns a policy violation object if there are any rules that fail -// func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation { -// var fRules []kyverno.ViolatedRule -// var pv kyverno.PolicyViolation -// for _, r := range pi.Rules { -// if !r.IsSuccessful() { -// fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()}) -// } -// } -// if len(fRules) > 0 { -// glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName) -// // there is an error -// pv = policyviolation.BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{ -// Kind: pi.RKind, -// Namespace: pi.RNamespace, -// Name: pi.RName, -// }, -// fRules, -// ) - -// } -// return pv -// } - -// //generatePolicyViolations generate policyViolation resources for the rules that failed -// //TODO: check if pvListerSynced is needed -// func generatePolicyViolations(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { -// var pvs []kyverno.PolicyViolation -// for _, policyInfo := range policyInfos { -// if !policyInfo.IsSuccessful() { -// if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) { -// pvs = append(pvs, pv) -// } -// } -// } - -// if len(pvs) > 0 { -// for _, newPv := range pvs { -// // generate PolicyViolation objects -// glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name) - -// // check if there was a previous violation for policy & resource combination -// curPv, err := getExistingPolicyViolationIfAny(pvListerSynced, pvLister, newPv) -// if err != nil { -// continue -// } -// if curPv == nil { -// // no existing policy violation, create a new one -// _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv) -// if err != nil { -// glog.Error(err) -// } -// continue -// } -// // compare the policyviolation spec for existing resource if present else -// if reflect.DeepEqual(curPv.Spec, newPv.Spec) { -// // if they are equal there has been no change so dont update the polivy violation -// glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec) -// continue -// } -// // spec changed so update the policyviolation -// //TODO: wont work, as name is not defined yet -// _, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv) -// if err != nil { -// glog.Error(err) -// continue -// } -// } -// } -// } - -// //TODO: change the name -// func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { -// // TODO: check for existing ov using label selectors on resource and policy -// labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} -// ls := &metav1.LabelSelector{} -// err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil) -// if err != nil { -// glog.Errorf("failed to generate label sector of Policy name %s: %v", newPv.Spec.Policy, err) -// return nil, err -// } -// policyViolationSelector, err := metav1.LabelSelectorAsSelector(ls) -// if err != nil { -// glog.Errorf("invalid label selector: %v", err) -// return nil, err -// } - -// //TODO: sync the cache before reading from it ? -// // check is this is needed ? -// // stopCh := make(chan struct{}, 0) -// // if !cache.WaitForCacheSync(stopCh, pvListerSynced) { -// // //TODO: can this be handled or avoided ? -// // glog.Info("unable to sync policy violation shared informer cache, might be out of sync") -// // } - -// pvs, err := pvLister.List(policyViolationSelector) -// if err != nil { -// glog.Errorf("unable to list policy violations with label selector %v: %v", policyViolationSelector, err) -// return nil, err -// } -// //TODO: ideally there should be only one policy violation returned -// if len(pvs) > 1 { -// glog.Errorf("more than one policy violation exists with labels %v", labelMap) -// return nil, fmt.Errorf("more than one policy violation exists with labels %v", labelMap) -// } - -// if len(pvs) == 0 { -// glog.Infof("policy violation does not exist with labels %v", labelMap) -// return nil, nil -// } -// return pvs[0], nil -// } diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 265090e977..77d3858e62 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -98,30 +98,15 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { // Do not process the admission requests for kinds that are in filterKinds for filtering if !utils.SkipFilteredResourcesReq(admissionReview.Request, ws.filterK8Resources) { - // if the resource is being deleted we need to clear any existing Policy Violations - // TODO: can report to the user that we clear the violation corresponding to this resource - if admissionReview.Request.Operation == v1beta1.Delete { - // Resource DELETE - err := ws.removePolicyViolation(admissionReview.Request) - if err != nil { - glog.Info(err) - } - admissionReview.Response = &v1beta1.AdmissionResponse{ - Allowed: true, - } - admissionReview.Response.UID = admissionReview.Request.UID - } else { - // Resource CREATE - // Resource UPDATE - switch r.URL.Path { - case config.MutatingWebhookServicePath: - admissionReview.Response = ws.HandleMutation(admissionReview.Request) - case config.ValidatingWebhookServicePath: - admissionReview.Response = ws.HandleValidation(admissionReview.Request) - case config.PolicyValidatingWebhookServicePath: - admissionReview.Response = ws.HandlePolicyValidation(admissionReview.Request) - } - + // Resource CREATE + // Resource UPDATE + switch r.URL.Path { + case config.MutatingWebhookServicePath: + admissionReview.Response = ws.HandleMutation(admissionReview.Request) + case config.ValidatingWebhookServicePath: + admissionReview.Response = ws.HandleValidation(admissionReview.Request) + case config.PolicyValidatingWebhookServicePath: + admissionReview.Response = ws.HandlePolicyValidation(admissionReview.Request) } } diff --git a/pkg/webhooks/utils.go b/pkg/webhooks/utils.go index 78fd64835e..7d6d9b5df2 100644 --- a/pkg/webhooks/utils.go +++ b/pkg/webhooks/utils.go @@ -1,15 +1,12 @@ package webhooks import ( - "encoding/json" "fmt" - "reflect" "strings" "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" - v1beta1 "k8s.io/api/admission/v1beta1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -40,21 +37,6 @@ func StringInSlice(kind string, list []string) bool { return false } -//parseKinds parses the kinds if a single string contains comma seperated kinds -// {"1,2,3","4","5"} => {"1","2","3","4","5"} -func parseKinds(list []string) []string { - kinds := []string{} - for _, k := range list { - args := strings.Split(k, ",") - for _, arg := range args { - if arg != "" { - kinds = append(kinds, strings.TrimSpace(arg)) - } - } - } - return kinds -} - //ArrayFlags to store filterkinds type ArrayFlags []string @@ -115,102 +97,6 @@ func toBlock(pis []info.PolicyInfo) bool { return false } -func checkIfOnlyAnnotationsUpdate(request *v1beta1.AdmissionRequest) bool { - var err error - // process only if its for existing resources - if request.Operation != v1beta1.Update { - return false - } - - // approach : we only compare if the addition contains annotations the are added with prefix "policies.kyverno.io" - // get annotations for the old resource - oldObj := request.OldObject - oldObjUnstr := unstructured.Unstructured{} - // need to set kind as some request dont contain kind meta-data raw resource but in the api request - oldObj.Raw = setKindForObject(oldObj.Raw, request.Kind.Kind) - err = oldObjUnstr.UnmarshalJSON(oldObj.Raw) - if err != nil { - glog.Error(err) - return false - } - oldAnn := oldObjUnstr.GetAnnotations() - - // get annotations for the new resource - newObj := request.Object - newObjUnstr := unstructured.Unstructured{} - // need to set kind as some request dont contain kind meta-data raw resource but in the api request - newObj.Raw = setKindForObject(newObj.Raw, request.Kind.Kind) - err = newObjUnstr.UnmarshalJSON(newObj.Raw) - if err != nil { - glog.Error(err) - return false - } - newAnn := newObjUnstr.GetAnnotations() - policiesAppliedNew := 0 - newAnnPolicy := map[string]string{} - // check if annotations changed - // assuming that we only add an annotation with the given prefix - for k, v := range newAnn { - // check prefix - policyName := strings.Split(k, "/") - if len(policyName) == 1 { - continue - } - if policyName[0] == "policies.kyverno.io" { - newAnnPolicy[policyName[1]] = v - policiesAppliedNew++ - } - } - - oldAnnPolicy := map[string]string{} - policiesAppliedOld := 0 - // check if annotations changed - // assuming that we only add an annotation with the given prefix - for k, v := range oldAnn { - // check prefix - policyName := strings.Split(k, "/") - if len(policyName) == 1 { - continue - } - if policyName[0] == "policies.kyverno.io" { - oldAnnPolicy[policyName[1]] = v - policiesAppliedOld++ - } - } - diffCount := policiesAppliedNew - policiesAppliedOld - switch diffCount { - case 1: // policy applied - return true - case -1: // policy removed - return true - case 0: // no new policy added or remove - // need to check if the policy was updated - if !reflect.DeepEqual(newAnnPolicy, oldAnnPolicy) { - return true - } - } - //TODO: Hack if there an update on self link then we ignore - if oldObjUnstr.GetSelfLink() != newObjUnstr.GetSelfLink() { - return true - } - - // then there is some other change and we should process it - return false -} - -func setKindForObject(bytes []byte, kind string) []byte { - var objectJSON map[string]interface{} - json.Unmarshal(bytes, &objectJSON) - objectJSON["kind"] = kind - data, err := json.Marshal(objectJSON) - if err != nil { - glog.Error(err) - glog.Error("unable to marshall, not setting the kind") - return bytes - } - return data -} - func convertToUnstructured(data []byte) (*unstructured.Unstructured, error) { resource := &unstructured.Unstructured{} err := resource.UnmarshalJSON(data) diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index e5915e4eaf..2e67548949 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -79,7 +79,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 // ADD EVENTS if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 { - eventsInfo, _ := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Validation) + eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Validation) // If the validationFailureAction flag is set "audit", // then we dont block the request and report the violations ws.eventGen.Add(eventsInfo...) From e87c72291f10801b11b3b8947f6759e591006707 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Wed, 14 Aug 2019 11:51:01 -0700 Subject: [PATCH 26/66] - Patch resource between every rule application - move mutation & validation to mutate webhook --- pkg/engine/engine.go | 2 +- pkg/engine/mutation.go | 23 +++++++---- pkg/webhooks/mutation.go | 61 ++++++++++++++--------------- pkg/webhooks/registration.go | 13 ++++--- pkg/webhooks/server.go | 28 ++++++++++++-- pkg/webhooks/utils.go | 2 + pkg/webhooks/validation.go | 30 +++++++------- pkg/webhooks/webhookManager.go | 71 +++------------------------------- 8 files changed, 98 insertions(+), 132 deletions(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 5698c51048..49332c7646 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -61,7 +61,7 @@ func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) } func mutation(p *types.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { - patches, ruleInfos := Mutate(*p, rawResource, *gvk) + patches, _, ruleInfos := Mutate(*p, rawResource, *gvk) if len(ruleInfos) == 0 { // no rules were processed return nil, nil diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index f1d551e1e6..a518e9fb4f 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -8,8 +8,10 @@ import ( ) // Mutate performs mutation. Overlay first and then mutation patches -func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []*info.RuleInfo) { - var allPatches [][]byte +func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []byte, []*info.RuleInfo) { + var allPatches, rulePatches [][]byte + var err error + var errs []error patchedDocument := rawResource ris := []*info.RuleInfo{} @@ -26,9 +28,9 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio } // Process Overlay if rule.Mutation.Overlay != nil { - overlayPatches, err := ProcessOverlay(rule, rawResource, gvk) + rulePatches, err = ProcessOverlay(rule, patchedDocument, gvk) if err == nil { - if len(overlayPatches) == 0 { + if len(rulePatches) == 0 { // if array elements dont match then we skip(nil patch, no error) // or if acnohor is defined and doenst match // policy is not applicable @@ -36,10 +38,10 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio } ri.Addf("Rule %s: Overlay succesfully applied.", rule.Name) // merge the json patches - patch := JoinPatches(overlayPatches) + patch := JoinPatches(rulePatches) // strip slashes from string ri.Changes = string(patch) - allPatches = append(allPatches, overlayPatches...) + allPatches = append(allPatches, rulePatches...) } else { ri.Fail() ri.Addf("overlay application has failed, err %v.", err) @@ -48,7 +50,7 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio // Process Patches if len(rule.Mutation.Patches) != 0 { - rulePatches, errs := ProcessPatches(rule, patchedDocument) + rulePatches, errs = ProcessPatches(rule, patchedDocument) if len(errs) > 0 { ri.Fail() for _, err := range errs { @@ -59,8 +61,13 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio allPatches = append(allPatches, rulePatches...) } } + + patchedDocument, err = ApplyPatches(rawResource, rulePatches) + if err != nil { + glog.Errorf("Failed to apply patches on ruleName=%s, err%v\n:", rule.Name, err) + } ris = append(ris, ri) } - return allPatches, ris + return allPatches, patchedDocument, ris } diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 82c5f01aac..41f78e177b 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -5,12 +5,19 @@ import ( engine "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" v1beta1 "k8s.io/api/admission/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" ) // HandleMutation handles mutating webhook admission request -func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { +func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, [][]byte, []byte) { + var allPatches, policyPatches [][]byte + policyInfos := []*info.PolicyInfo{} + var ruleInfos []*info.RuleInfo + patchedDocument := request.Object.Raw + + if request.Operation == v1beta1.Delete { + return true, nil, patchedDocument + } glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) @@ -18,12 +25,11 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be policies, err := ws.policyLister.List(labels.NewSelector()) if err != nil { // Unable to connect to policy Lister to access policies - glog.Error("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied") + glog.Errorln("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied") glog.Warning(err) - return &v1beta1.AdmissionResponse{ - Allowed: true, - } + return true, nil, patchedDocument } + rname := engine.ParseNameFromObject(request.Object.Raw) rns := engine.ParseNamespaceFromObject(request.Object.Raw) rkind := request.Kind.Kind @@ -31,19 +37,17 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be glog.Errorf("failed to parse KIND from request: Namespace=%s Name=%s UID=%s patchOperation=%s\n", request.Namespace, request.Name, request.UID, request.Operation) } - var allPatches [][]byte - policyInfos := []*info.PolicyInfo{} for _, policy := range policies { + // check if policy has a rule for the admission request kind if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { continue } //TODO: HACK Check if an update of annotations - if checkIfOnlyAnnotationsUpdate(request) { - return &v1beta1.AdmissionResponse{ - Allowed: true, - } - } + // if checkIfOnlyAnnotationsUpdate(request) { + // return true + // } + policyInfo := info.NewPolicyInfo(policy.Name, rkind, rname, @@ -55,7 +59,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - policyPatches, ruleInfos := engine.Mutate(*policy, request.Object.Raw, request.Kind) + policyPatches, patchedDocument, ruleInfos = engine.Mutate(*policy, patchedDocument, request.Kind) policyInfo.AddRuleInfos(ruleInfos) @@ -71,34 +75,27 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be glog.Info(err) } allPatches = append(allPatches, policyPatches...) - glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns) + glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rns, rname) } policyInfos = append(policyInfos, policyInfo) - annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Mutation) - if annPatch != nil { - // add annotations - ws.annotationsController.Add(rkind, rns, rname, annPatch) - } + // annPatch := addAnnotationsToResource(patchedDocument, policyInfo, info.Mutation) + // if annPatch != nil { + // // add annotations + // ws.annotationsController.Add(rkind, rns, rname, annPatch) + // } } if len(allPatches) > 0 { eventsInfo, _ := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation) ws.eventController.Add(eventsInfo...) } + ok, msg := isAdmSuccesful(policyInfos) if ok { - patchType := v1beta1.PatchTypeJSONPatch - return &v1beta1.AdmissionResponse{ - Allowed: true, - Patch: engine.JoinPatches(allPatches), - PatchType: &patchType, - } - } - return &v1beta1.AdmissionResponse{ - Allowed: false, - Result: &metav1.Status{ - Message: msg, - }, + return true, allPatches, patchedDocument } + + glog.Errorf("Failed to mutate the resource: %s\n", msg) + return false, nil, patchedDocument } diff --git a/pkg/webhooks/registration.go b/pkg/webhooks/registration.go index e0248fa86f..739c64392e 100644 --- a/pkg/webhooks/registration.go +++ b/pkg/webhooks/registration.go @@ -11,6 +11,7 @@ import ( client "github.com/nirmata/kyverno/pkg/dclient" "github.com/tevino/abool" admregapi "k8s.io/api/admissionregistration/v1beta1" + errorsapi "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" admregclient "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" rest "k8s.io/client-go/rest" @@ -112,12 +113,12 @@ func (wrc *WebhookRegistrationClient) DeregisterAll() { if wrc.serverIP != "" { err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationDebug, &v1.DeleteOptions{}) - if err != nil { + if err != nil && !errorsapi.IsNotFound(err) { glog.Error(err) } } err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationName, &v1.DeleteOptions{}) - if err != nil { + if err != nil && !errorsapi.IsNotFound(err) { glog.Error(err) } } @@ -130,7 +131,7 @@ func (wrc *WebhookRegistrationClient) deregister() { func (wrc *WebhookRegistrationClient) deregisterMutatingWebhook() { if wrc.serverIP != "" { err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationDebug, &v1.DeleteOptions{}) - if err != nil { + if err != nil && !errorsapi.IsNotFound(err) { glog.Error(err) } else { wrc.MutationRegistered.UnSet() @@ -139,7 +140,7 @@ func (wrc *WebhookRegistrationClient) deregisterMutatingWebhook() { } err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationName, &v1.DeleteOptions{}) - if err != nil { + if err != nil && !errorsapi.IsNotFound(err) { glog.Error(err) } else { wrc.MutationRegistered.UnSet() @@ -149,7 +150,7 @@ func (wrc *WebhookRegistrationClient) deregisterMutatingWebhook() { func (wrc *WebhookRegistrationClient) deregisterValidatingWebhook() { if wrc.serverIP != "" { err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationDebug, &v1.DeleteOptions{}) - if err != nil { + if err != nil && !errorsapi.IsNotFound(err) { glog.Error(err) } wrc.ValidationRegistered.UnSet() @@ -157,7 +158,7 @@ func (wrc *WebhookRegistrationClient) deregisterValidatingWebhook() { } err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationName, &v1.DeleteOptions{}) - if err != nil { + if err != nil && !errorsapi.IsNotFound(err) { glog.Error(err) } wrc.ValidationRegistered.UnSet() diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index a0e309234b..b166054355 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -10,6 +10,8 @@ import ( "net/http" "time" + "github.com/nirmata/kyverno/pkg/engine" + "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/annotations" "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" @@ -114,13 +116,10 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { // Resource UPDATE switch r.URL.Path { case config.MutatingWebhookServicePath: - admissionReview.Response = ws.HandleMutation(admissionReview.Request) - case config.ValidatingWebhookServicePath: - admissionReview.Response = ws.HandleValidation(admissionReview.Request) + admissionReview.Response = ws.HandleAdmissionRequest(admissionReview.Request) case config.PolicyValidatingWebhookServicePath: admissionReview.Response = ws.HandlePolicyValidation(admissionReview.Request) } - } } @@ -138,6 +137,27 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { } } +func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { + var response *v1beta1.AdmissionResponse + + allowed, allPatches, patchedDocument := ws.HandleMutation(request) + if !allowed { + // TODO: add failure message to response + return &v1beta1.AdmissionResponse{ + Allowed: false, + } + } + + response = ws.HandleValidation(request, patchedDocument) + if response.Allowed && len(allPatches) > 0 { + patchType := v1beta1.PatchTypeJSONPatch + response.Patch = engine.JoinPatches(allPatches) + response.PatchType = &patchType + } + + return response +} + // RunAsync TLS server in separate thread and returns control immediately func (ws *WebhookServer) RunAsync() { go func(ws *WebhookServer) { diff --git a/pkg/webhooks/utils.go b/pkg/webhooks/utils.go index 83ca8944d7..1b055ab8f9 100644 --- a/pkg/webhooks/utils.go +++ b/pkg/webhooks/utils.go @@ -109,9 +109,11 @@ const ( func toBlock(pis []*info.PolicyInfo) bool { for _, pi := range pis { if pi.ValidationFailureAction != ReportViolation { + glog.V(3).Infoln("ValidationFailureAction set to enforce, blocking resource ceation") return true } } + glog.V(3).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation") return false } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index b444c4c11c..abeca7f792 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -11,7 +11,7 @@ import ( // HandleValidation handles validating webhook admission request // If there are no errors in validating rule we apply generation rules -func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { +func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, rawResource []byte) *v1beta1.AdmissionResponse { glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) @@ -27,8 +27,8 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } } - rname := engine.ParseNameFromObject(request.Object.Raw) - rns := engine.ParseNamespaceFromObject(request.Object.Raw) + rname := engine.ParseNameFromObject(rawResource) + rns := engine.ParseNamespaceFromObject(rawResource) rkind := request.Kind.Kind if rkind == "" { glog.Errorf("failed to parse KIND from request: Namespace=%s Name=%s UID=%s patchOperation=%s\n", request.Namespace, request.Name, request.UID, request.Operation) @@ -40,12 +40,12 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 continue } //TODO: HACK Check if an update of annotations - if checkIfOnlyAnnotationsUpdate(request) { - // allow the update of resource to add annotations - return &v1beta1.AdmissionResponse{ - Allowed: true, - } - } + // if checkIfOnlyAnnotationsUpdate(request) { + // // allow the update of resource to add annotations + // return &v1beta1.AdmissionResponse{ + // Allowed: true, + // } + // } policyInfo := info.NewPolicyInfo(policy.Name, rkind, @@ -57,7 +57,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 request.Kind.Kind, rns, rname, request.UID, request.Operation) glog.Infof("Validating resource %s/%s/%s with policy %s with %d rules", rkind, rns, rname, policy.ObjectMeta.Name, len(policy.Spec.Rules)) - ruleInfos, err := engine.Validate(*policy, request.Object.Raw, request.Kind) + ruleInfos, err := engine.Validate(*policy, rawResource, request.Kind) if err != nil { // This is not policy error // but if unable to parse request raw resource @@ -84,11 +84,11 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 } } policyInfos = append(policyInfos, policyInfo) - // annotations - annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Validation) - if annPatch != nil { - ws.annotationsController.Add(rkind, rns, rname, annPatch) - } + // // annotations + // annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Validation) + // if annPatch != nil { + // ws.annotationsController.Add(rkind, rns, rname, annPatch) + // } } if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 { diff --git a/pkg/webhooks/webhookManager.go b/pkg/webhooks/webhookManager.go index 118d31a4f8..5f8ec0a760 100644 --- a/pkg/webhooks/webhookManager.go +++ b/pkg/webhooks/webhookManager.go @@ -33,79 +33,18 @@ func (ws *WebhookServer) registerWebhookConfigurations(policy v1alpha1.Policy) e } glog.Infof("Mutating webhook registered") } - - if rule.Validation != nil && !ws.webhookRegistrationClient.ValidationRegistered.IsSet() { - if err := ws.webhookRegistrationClient.RegisterValidatingWebhook(); err != nil { - return err - } - glog.Infof("Validating webhook registered") - } } return nil } func (ws *WebhookServer) deregisterWebhookConfigurations(policy v1alpha1.Policy) error { - glog.V(3).Infof("Retreiving policy type for %s\n", policy.Name) + policies, _ := ws.policyLister.List(labels.NewSelector()) - pt := GetPolicyType([]*v1alpha1.Policy{&policy}, "") - - glog.V(3).Infof("Policy to be deleted type==%v\n", pt) - - existPolicyType := ws.getExistingPolicyType(policy.Name) - glog.V(3).Infof("Found existing policy type==%v\n", existPolicyType) - - switch existPolicyType { - case none: - ws.webhookRegistrationClient.deregister() - glog.Infoln("All webhook deregistered") - case mutate: - if pt != mutate { - ws.webhookRegistrationClient.deregisterValidatingWebhook() - glog.Infoln("Validating webhook deregistered") - } - case validate: - if pt != validate { - ws.webhookRegistrationClient.deregisterMutatingWebhook() - glog.Infoln("Mutating webhook deregistered") - } - case all: - return nil + // deregister webhook if no policy found in cluster + if len(policies) == 1 { + ws.webhookRegistrationClient.deregisterMutatingWebhook() + glog.Infoln("Mutating webhook deregistered") } return nil } - -func (ws *WebhookServer) getExistingPolicyType(policyName string) policyType { - - policies, err := ws.policyLister.List(labels.NewSelector()) - if err != nil { - glog.Errorf("Failed to get policy list") - } - - return GetPolicyType(policies, policyName) -} - -// GetPolicyType get the type of policies -// excludes is the policy name to be skipped -func GetPolicyType(policyList []*v1alpha1.Policy, excludes string) policyType { - ptype := none - - for _, p := range policyList { - if p.Name == excludes { - glog.Infof("Skipping policy type check on %s\n", excludes) - continue - } - - for _, rule := range p.Spec.Rules { - if rule.Mutation != nil { - ptype = ptype | mutate - } - - if rule.Validation != nil { - ptype = ptype | validate - } - } - } - - return ptype -} From 05e1f128c7ac840d942dfef367939930862515e4 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 14 Aug 2019 14:56:53 -0700 Subject: [PATCH 27/66] namespace controller redesign --- main.go | 18 ++ pkg/engine/generation.go | 24 ++- pkg/gencontroller/controller.go | 160 ----------------- pkg/gencontroller/generation.go | 155 ----------------- pkg/gencontroller/utils.go | 62 ------- pkg/namespace/controller.go | 199 +++++++++++++++++++++ pkg/namespace/generation.go | 298 ++++++++++++++++++++++++++++++++ pkg/namespace/report.go | 59 +++++++ pkg/namespace/utils.go | 55 ++++++ pkg/policy/controller.go | 1 - pkg/utils/util.go | 11 ++ 11 files changed, 659 insertions(+), 383 deletions(-) delete mode 100644 pkg/gencontroller/controller.go delete mode 100644 pkg/gencontroller/generation.go delete mode 100644 pkg/gencontroller/utils.go create mode 100644 pkg/namespace/controller.go create mode 100644 pkg/namespace/generation.go create mode 100644 pkg/namespace/report.go create mode 100644 pkg/namespace/utils.go diff --git a/main.go b/main.go index ef44747238..cef58b4080 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,12 @@ import ( "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" event "github.com/nirmata/kyverno/pkg/event" + "github.com/nirmata/kyverno/pkg/namespace" "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/policyviolation" + "github.com/nirmata/kyverno/pkg/utils" "github.com/nirmata/kyverno/pkg/webhooks" + kubeinformers "k8s.io/client-go/informers" "k8s.io/sample-controller/pkg/signals" ) @@ -73,6 +76,19 @@ func main() { glog.Fatalf("error creating policy violation controller: %v\n", err) } + // NAMESPACE INFORMER + // watches namespace resource + // - cache resync time: 30 seconds + kubeClient, err := utils.NewKubeClient(clientConfig) + if err != nil { + glog.Fatalf("Error creating kubernetes client: %v\n", err) + } + kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 30) + + // GENERATE CONTROLLER + // - watches for Namespace resource and generates resource based on the policy generate rule + nsc := namespace.NewNamespaceController(pclient, client, kubeInformer.Core().V1().Namespaces(), pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) + tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) @@ -94,9 +110,11 @@ func main() { } pInformer.Start(stopCh) + kubeInformer.Start(stopCh) go pc.Run(1, stopCh) go pvc.Run(1, stopCh) go egen.Run(1, stopCh) + go nsc.Run(1, stopCh) //TODO add WG for the go routines? server.RunAsync() diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 52076832b7..7dbaeb107b 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -14,22 +14,24 @@ import ( ) //Generate apply generation rules on a resource -func Generate(client *client.Client, policy *kyverno.Policy, ns unstructured.Unstructured) []info.RuleInfo { +func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) []info.RuleInfo { ris := []info.RuleInfo{} for _, rule := range policy.Spec.Rules { if rule.Generation == (kyverno.Generation{}) { continue } + glog.V(4).Infof("applying policy %s generate rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName()) ri := info.NewRuleInfo(rule.Name, info.Generation) err := applyRuleGenerator(client, ns, rule.Generation) if err != nil { ri.Fail() - ri.Addf("Rule %s: Failed to apply rule generator, err %v.", rule.Name, err) + ri.Addf("Failed to apply rule generator, err %v.", rule.Name, err) + glog.Infof("failed to apply policy %s rule %s on resource %s/%s/%s: %v", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName(), err) } else { - ri.Addf("Rule %s: Generation succesfully.", rule.Name) + ri.Addf("Generation succesfully.", rule.Name) + glog.Infof("succesfully applied policy %s rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName()) } ris = append(ris, ri) - } return ris } @@ -40,19 +42,24 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen var rdata map[string]interface{} if gen.Data != nil { + glog.V(4).Info("generate rule: creates new resource") // 1> Check if resource exists obj, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name) if err == nil { + glog.V(4).Infof("generate rule: resource %s/%s/%s already present. checking if it contains the required configuration", gen.Kind, ns.GetName(), gen.Name) // 2> If already exsists, then verify the content is contained // found the resource // check if the rule is create, if yes, then verify if the specified configuration is present in the resource ok, err := checkResource(gen.Data, obj) if err != nil { + glog.V(4).Infof("generate rule:: unable to check if configuration %v, is present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name) return err } if !ok { + glog.V(4).Infof("generate rule:: configuration %v not present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name) return errors.New("rule configuration not present in resource") } + glog.V(4).Infof("generate rule: required configuration %v is present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name) return nil } rdata, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&gen.Data) @@ -62,16 +69,20 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen } } if gen.Clone != (kyverno.CloneFrom{}) { + glog.V(4).Info("generate rule: clone resource") // 1> Check if resource exists _, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name) if err == nil { + glog.V(4).Infof("generate rule: resource %s/%s/%s already present", gen.Kind, ns.GetName(), gen.Name) return nil } - // 2> If already exists return + // 2> If clone already exists return resource, err = client.GetResource(gen.Kind, gen.Clone.Namespace, gen.Clone.Name) if err != nil { + glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s not present: %v", gen.Kind, gen.Kind, gen.Clone.Namespace, gen.Clone.Name, err) return err } + glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", gen.Kind, gen.Kind, gen.Clone.Namespace, gen.Clone.Name) rdata = resource.UnstructuredContent() } resource.SetUnstructuredContent(rdata) @@ -82,11 +93,14 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen _, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false) if err != nil { + glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, ns.GetKind(), ns.GetNamespace(), ns.GetName(), err) return err } + glog.V(4).Infof("generate rule: created resource %s/%s/%s", gen.Kind, ns.GetKind(), ns.GetNamespace(), ns.GetName()) return nil } +//checkResource checks if the config is present in th eresource func checkResource(config interface{}, resource *unstructured.Unstructured) (bool, error) { var err error diff --git a/pkg/gencontroller/controller.go b/pkg/gencontroller/controller.go deleted file mode 100644 index 8709e49110..0000000000 --- a/pkg/gencontroller/controller.go +++ /dev/null @@ -1,160 +0,0 @@ -package gencontroller - -import ( - "fmt" - "time" - - "k8s.io/apimachinery/pkg/util/wait" - - "github.com/golang/glog" - client "github.com/nirmata/kyverno/pkg/dclient" - "github.com/nirmata/kyverno/pkg/event" - "k8s.io/apimachinery/pkg/api/errors" - - v1Informer "k8s.io/client-go/informers/core/v1" - v1CoreLister "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -//Controller watches the 'Namespace' resource creation/update and applied the generation rules on them -type Controller struct { - client *client.Client - namespaceLister v1CoreLister.NamespaceLister - namespaceSynced cache.InformerSynced - policyLister policyLister.PolicyLister - eventController event.Generator - violationBuilder violation.Generator - annotationsController annotations.Controller - workqueue workqueue.RateLimitingInterface -} - -//NewGenController returns a new Controller to manage generation rules -func NewGenController(client *client.Client, - eventController event.Generator, - policyInformer policySharedInformer.PolicyInformer, - violationBuilder violation.Generator, - namespaceInformer v1Informer.NamespaceInformer, - annotationsController annotations.Controller) *Controller { - - // create the controller - controller := &Controller{ - client: client, - namespaceLister: namespaceInformer.Lister(), - namespaceSynced: namespaceInformer.Informer().HasSynced, - policyLister: policyInformer.GetLister(), - eventController: eventController, - violationBuilder: violationBuilder, - annotationsController: annotationsController, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), wqNamespace), - } - namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.createNamespaceHandler, - UpdateFunc: controller.updateNamespaceHandler, - }) - - return controller -} -func (c *Controller) createNamespaceHandler(resource interface{}) { - c.enqueueNamespace(resource) -} - -func (c *Controller) updateNamespaceHandler(oldResoruce, newResource interface{}) { - // DO we need to anything if the namespace is modified ? -} - -func (c *Controller) enqueueNamespace(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - glog.Error(err) - return - } - c.workqueue.Add(key) -} - -//Run to run the controller -func (c *Controller) Run(stopCh <-chan struct{}) error { - - if ok := cache.WaitForCacheSync(stopCh, c.namespaceSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - for i := 0; i < workerCount; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - glog.Info("started namespace controller workers") - return nil -} - -//Stop to stop the controller -func (c *Controller) Stop() { - defer c.workqueue.ShutDown() - glog.Info("shutting down namespace controller workers") -} - -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - if shutdown { - return false - } - err := func(obj interface{}) error { - defer c.workqueue.Done(obj) - err := c.syncHandler(obj) - c.handleErr(err, obj) - return nil - }(obj) - if err != nil { - glog.Error(err) - return true - } - return true -} - -func (c *Controller) handleErr(err error, key interface{}) { - if err == nil { - c.workqueue.Forget(key) - return - } - if c.workqueue.NumRequeues(key) < wqRetryLimit { - glog.Warningf("Error syncing events %v: %v", key, err) - c.workqueue.AddRateLimited(key) - return - } - c.workqueue.Forget(key) - glog.Error(err) - glog.Warningf("Dropping the key %q out of the queue: %v", key, err) -} - -func (c *Controller) syncHandler(obj interface{}) error { - var key string - var ok bool - if key, ok = obj.(string); !ok { - return fmt.Errorf("expected string in workqueue but got %v", obj) - } - // Namespace is cluster wide resource - _, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - glog.Errorf("invalid namespace key: %s", key) - return err - } - // Get Namespace - ns, err := c.namespaceLister.Get(name) - if err != nil { - if errors.IsNotFound(err) { - glog.Errorf("namespace '%s' in work queue no longer exists", key) - return nil - } - } - - //TODO: need to find a way to store the policy such that we can directly queury the - // policies with generation policies - // PolicyListerExpansion - c.processNamespace(ns) - return nil -} diff --git a/pkg/gencontroller/generation.go b/pkg/gencontroller/generation.go deleted file mode 100644 index dda48b0fe4..0000000000 --- a/pkg/gencontroller/generation.go +++ /dev/null @@ -1,155 +0,0 @@ -package gencontroller - -import ( - "encoding/json" - "fmt" - "strings" - - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - - "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/engine" - event "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/info" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" -) - -func (c *Controller) processNamespace(ns *corev1.Namespace) error { - //Get all policies and then verify if the namespace matches any of the defined selectors - policies, err := c.listPolicies(ns) - if err != nil { - return err - } - // process policy on namespace - for _, p := range policies { - c.processPolicy(ns, p) - } - - return nil -} - -func (c *Controller) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) { - var fpolicies []*v1alpha1.Policy - policies, err := c.policyLister.List(labels.NewSelector()) - if err != nil { - glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules") - return nil, err - } - for _, p := range policies { - // Check if the policy contains a generatoin rule - for _, r := range p.Spec.Rules { - if r.Generation != nil { - // Check if the resource meets the description - data, err := json.Marshal(ns) - if err != nil { - glog.Error(err) - continue - } - // convert types of GVK - nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace") - // Hardcode as we have a informer on specified gvk - gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version} - if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) { - fpolicies = append(fpolicies, p) - break - } - } - } - } - - return fpolicies, nil -} - -func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) { - var eventInfo *event.Info - var onViolation bool - var msg string - - policyInfo := info.NewPolicyInfo(p.Name, - "Namespace", - ns.Name, - "", - p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW - - // convert to unstructured - unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns) - if err != nil { - glog.Error(err) - return - } - unstObj := unstructured.Unstructured{Object: unstrMap} - ruleInfos := engine.Generate(c.client, p, unstObj) - policyInfo.AddRuleInfos(ruleInfos) - - // generate annotations on namespace - c.createAnnotations(policyInfo) - //TODO generate namespace on created resources - - if !policyInfo.IsSuccessful() { - glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name) - for _, r := range ruleInfos { - glog.Warning(r.Msgs) - - if msg = strings.Join(r.Msgs, " "); strings.Contains(msg, "rule configuration not present in resource") { - onViolation = true - msg = fmt.Sprintf(`Resource creation violates generate rule '%s' of policy '%s'`, r.Name, policyInfo.Name) - } - } - - if onViolation { - glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name) - // Policy Violation - v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.FailedRules()) - c.violationBuilder.Add(v) - } else { - // Event - eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked, - event.FPolicyApplyBlockCreate, policyInfo.RName, policyInfo.GetRuleNames(false)) - - glog.V(2).Infof("Request blocked event info has prepared for %s/%s\n", policyKind, policyInfo.Name) - - c.eventController.Add(eventInfo) - } - return - } - - glog.Infof("Generation from policy %s has succesfully applied to %s/%s", p.Name, policyInfo.RKind, policyInfo.RName) - - eventInfo = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, - event.PolicyApplied, event.SRulesApply, policyInfo.GetRuleNames(true), policyInfo.Name) - - glog.V(2).Infof("Success event info has prepared for %s/%s\n", policyInfo.RKind, policyInfo.RName) - - c.eventController.Add(eventInfo) -} - -func (c *Controller) createAnnotations(pi *info.PolicyInfo) { - //get resource - obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName) - if err != nil { - glog.Error(err) - return - } - // add annotation for policy application - ann := obj.GetAnnotations() - // Generation rules - ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Generation) - if err != nil { - glog.Error(err) - return - } - if gpatch == nil { - // nothing to patch - return - } - // add the anotation to the resource - _, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch) - if err != nil { - glog.Error(err) - return - } -} diff --git a/pkg/gencontroller/utils.go b/pkg/gencontroller/utils.go deleted file mode 100644 index 609d53f917..0000000000 --- a/pkg/gencontroller/utils.go +++ /dev/null @@ -1,62 +0,0 @@ -package gencontroller - -import ( - "github.com/minio/minio/pkg/wildcard" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" -) - -const ( - wqNamespace string = "namespace" - workerCount int = 1 - wqRetryLimit int = 5 - policyKind string = "Policy" -) - -func namespaceMeetsRuleDescription(ns *corev1.Namespace, resourceDescription v1alpha1.ResourceDescription) bool { - //REWORK Not needed but verify the 'Namespace' is defined in the list of supported kinds - if !findKind(resourceDescription.Kinds, "Namespace") { - return false - } - if resourceDescription.Name != nil { - if !wildcard.Match(*resourceDescription.Name, ns.Name) { - return false - } - } - - if resourceDescription.Selector != nil { - selector, err := metav1.LabelSelectorAsSelector(resourceDescription.Selector) - if err != nil { - return false - } - - labelSet := convertLabelsToLabelSet(ns.Labels) - // labels - if !selector.Matches(labelSet) { - return false - } - } - return true -} - -func convertLabelsToLabelSet(labelMap map[string]string) labels.Set { - labelSet := make(labels.Set, len(labelMap)) - // REWORK: check if the below works - // if x, ok := labelMap.(labels.Set); !ok { - - // } - for k, v := range labelMap { - labelSet[k] = v - } - return labelSet -} - -func findKind(kinds []string, kindGVK string) bool { - for _, kind := range kinds { - if kind == kindGVK { - return true - } - } - return false -} diff --git a/pkg/namespace/controller.go b/pkg/namespace/controller.go new file mode 100644 index 0000000000..9966ce01ba --- /dev/null +++ b/pkg/namespace/controller.go @@ -0,0 +1,199 @@ +package namespace + +import ( + "time" + + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/golang/glog" + client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/event" + "k8s.io/apimachinery/pkg/api/errors" + + kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + v1 "k8s.io/api/core/v1" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + v1Informer "k8s.io/client-go/informers/core/v1" + v1CoreLister "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" +) + +const ( + // maxRetries is the number of times a Namespace will be processed for a policy before its dropped from the queue + maxRetries = 15 +) + +//NamespaceController watches the 'Namespace' resource creation/update and applied the generation rules on them +type NamespaceController struct { + client *client.Client + kyvernoClient *kyvernoclient.Clientset + syncHandler func(nsKey string) error + enqueueNs func(ns *v1.Namespace) + + // nLsister can list/get namespaces from the shared informer's store + nsLister v1CoreLister.NamespaceLister + // nsListerSynced returns true if the Namespace store has been synced at least once + nsListerSynced cache.InformerSynced + // pvLister can list/get policy violation from the shared informer's store + pLister lister.PolicyLister + // pvListerSynced retrns true if the Policy store has been synced at least once + pvListerSynced cache.InformerSynced + // pvLister can list/get policy violation from the shared informer's store + pvLister lister.PolicyViolationLister + + // eventGen provides interface to generate evenets + eventGen event.Interface + // Namespaces that need to be synced + queue workqueue.RateLimitingInterface + // Resource manager, manages the mapping for already processed resource + rm resourceManager +} + +//NewNamespaceController returns a new Controller to manage generation rules +func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset, + client *client.Client, + nsInformer v1Informer.NamespaceInformer, + pInformer informer.PolicyInformer, + pvInformer informer.PolicyViolationInformer, + eventGen event.Interface) *NamespaceController { + //TODO: do we need to event recorder for this controller? + // create the controller + nsc := &NamespaceController{ + client: client, + kyvernoClient: kyvernoClient, + eventGen: eventGen, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"), + } + + nsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: nsc.addNamespace, + UpdateFunc: nsc.updateNamespace, + DeleteFunc: nsc.deleteNamespace, + }) + + nsc.enqueueNs = nsc.enqueue + nsc.syncHandler = nsc.syncNamespace + + nsc.nsLister = nsInformer.Lister() + nsc.nsListerSynced = nsInformer.Informer().HasSynced + nsc.pLister = pInformer.Lister() + nsc.pvListerSynced = pInformer.Informer().HasSynced + nsc.pvLister = pvInformer.Lister() + + return nsc +} +func (nsc *NamespaceController) addNamespace(obj interface{}) { + ns := obj.(*v1.Namespace) + glog.V(4).Infof("Adding Namespace %s", ns.Name) + nsc.enqueueNs(ns) +} + +func (nsc *NamespaceController) updateNamespace(old, cur interface{}) { + oldNs := old.(*v1.Namespace) + curNs := cur.(*v1.Namespace) + if curNs.ResourceVersion == oldNs.ResourceVersion { + // Periodic resync will send update events for all known Namespace. + // Two different versions of the same replica set will always have different RVs. + return + } + glog.V(4).Infof("Updating Namesapce %s", curNs.Name) + //TODO: anything to be done here? +} + +func (nsc *NamespaceController) deleteNamespace(obj interface{}) { + ns, _ := obj.(*v1.Namespace) + glog.V(4).Infof("Deleting Namespace %s", ns.Name) + //TODO: anything to be done here? +} + +func (nsc *NamespaceController) enqueue(ns *v1.Namespace) { + key, err := cache.MetaNamespaceKeyFunc(ns) + if err != nil { + glog.Error(err) + return + } + nsc.queue.Add(key) +} + +//Run to run the controller +func (nsc *NamespaceController) Run(workers int, stopCh <-chan struct{}) { + defer utilruntime.HandleCrash() + defer nsc.queue.ShutDown() + + glog.Info("Starting namespace controller") + defer glog.Info("Shutting down namespace controller") + + if ok := cache.WaitForCacheSync(stopCh, nsc.nsListerSynced); !ok { + return + } + + for i := 0; i < workerCount; i++ { + go wait.Until(nsc.worker, time.Second, stopCh) + } + <-stopCh +} + +// worker runs a worker thread that just dequeues items, processes them, and marks them done. +// It enforces that the syncHandler is never invoked concurrently with the same key. +func (nsc *NamespaceController) worker() { + for nsc.processNextWorkItem() { + } +} + +func (nsc *NamespaceController) processNextWorkItem() bool { + key, quit := nsc.queue.Get() + if quit { + return false + } + defer nsc.queue.Done(key) + + err := nsc.syncHandler(key.(string)) + nsc.handleErr(err, key) + + return true +} + +func (nsc *NamespaceController) handleErr(err error, key interface{}) { + if err == nil { + nsc.queue.Forget(key) + return + } + + if nsc.queue.NumRequeues(key) < maxRetries { + glog.V(2).Infof("Error syncing Namespace %v: %v", key, err) + nsc.queue.AddRateLimited(key) + return + } + + utilruntime.HandleError(err) + glog.V(2).Infof("Dropping namespace %q out of the queue: %v", key, err) + nsc.queue.Forget(key) +} + +func (nsc *NamespaceController) syncNamespace(key string) error { + startTime := time.Now() + glog.V(4).Infof("Started syncing namespace %q (%v)", key, startTime) + defer func() { + glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Since(startTime)) + }() + namespace, err := nsc.nsLister.Get(key) + if errors.IsNotFound(err) { + glog.V(2).Infof("Namespace %v has been deleted", key) + return nil + } + if err != nil { + return err + } + // Deep-copy otherwise we are mutating our cache. + // TODO: Deep-copy only when needed. + n := namespace.DeepCopy() + + // process generate rules + policyInfos := nsc.processNamespace(*n) + // report errors + nsc.report(policyInfos) + return nil +} diff --git a/pkg/namespace/generation.go b/pkg/namespace/generation.go new file mode 100644 index 0000000000..9851101cb8 --- /dev/null +++ b/pkg/namespace/generation.go @@ -0,0 +1,298 @@ +package namespace + +import ( + "sync" + "time" + + client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/engine" + + "github.com/golang/glog" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + + lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/info" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" +) + +type resourceManager interface { + ProcessResource(policy, pv, kind, ns, name, rv string) bool + //TODO removeResource(kind, ns, name string) error + RegisterResource(policy, pv, kind, ns, name, rv string) + // reload + Drop() +} + +// ResourceManager stores the details on already processed resources for caching +type ResourceManager struct { + // we drop and re-build the cache + // based on the memory consumer of by the map + data map[string]interface{} + mux sync.RWMutex + time time.Time + rebuildTime int64 // after how many seconds should we rebuild the cache +} + +//NewResourceManager returns a new ResourceManager +func NewResourceManager(rebuildTime int64) *ResourceManager { + rm := ResourceManager{ + data: make(map[string]interface{}), + time: time.Now(), + rebuildTime: rebuildTime, + } + // set time it was built + return &rm +} + +var empty struct{} + +//RegisterResource stores if the policy is processed on this resource version +func (rm *ResourceManager) RegisterResource(policy, pv, kind, ns, name, rv string) { + rm.mux.Lock() + defer rm.mux.Unlock() + // add the resource + key := buildKey(policy, pv, kind, ns, name, rv) + rm.data[key] = empty +} + +//ProcessResource returns true if the policy was not applied on the resource +func (rm *ResourceManager) ProcessResource(policy, pv, kind, ns, name, rv string) bool { + rm.mux.RLock() + defer rm.mux.RUnlock() + + key := buildKey(policy, pv, kind, ns, name, rv) + _, ok := rm.data[key] + return ok == false +} + +func buildKey(policy, pv, kind, ns, name, rv string) string { + return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv +} + +// func (nsc *NamespaceController) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) { +// var fpolicies []*v1alpha1.Policy +// policies, err := c.policyLister.List(labels.NewSelector()) +// if err != nil { +// glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules") +// return nil, err +// } +// for _, p := range policies { +// // Check if the policy contains a generatoin rule +// for _, r := range p.Spec.Rules { +// if r.Generation != nil { +// // Check if the resource meets the description +// data, err := json.Marshal(ns) +// if err != nil { +// glog.Error(err) +// continue +// } +// // convert types of GVK +// nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace") +// // Hardcode as we have a informer on specified gvk +// gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version} +// if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) { +// fpolicies = append(fpolicies, p) +// break +// } +// } +// } +// } + +// func (nsc *NamespaceController) processNamespace(ns *corev1.Namespace) error { +// //Get all policies and then verify if the namespace matches any of the defined selectors +// policies, err := c.listPolicies(ns) +// if err != nil { +// return err +// } +// // process policy on namespace +// for _, p := range policies { +// c.processPolicy(ns, p) +// } + +// return nil +// } + +func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []info.PolicyInfo { + var policyInfos []info.PolicyInfo + // convert to unstructured + unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(namespace) + if err != nil { + glog.Infof("unable to convert to unstructured, not processing any policies: %v", err) + return policyInfos + } + ns := unstructured.Unstructured{Object: unstr} + + // get all the policies that have a generate rule and apply on the namespace + // apply policy on resource + + policies := listpolicies(ns, nsc.pLister) + for _, policy := range policies { + policyInfo := applyPolicy(nsc.client, ns, *policy) + policyInfos = append(policyInfos, policyInfo) + } + return policyInfos +} + +func listpolicies(ns unstructured.Unstructured, pLister lister.PolicyLister) []*kyverno.Policy { + var filteredpolicies []*kyverno.Policy + glog.V(4).Infof("listing policies that namespace %s", ns.GetName()) + policies, err := pLister.List(labels.NewSelector()) + if err != nil { + glog.Errorf("failed to get list policies: %v", err) + return nil + } + for _, policy := range policies { + for _, rule := range policy.Spec.Rules { + if rule.Generation == (kyverno.Generation{}) { + continue + } + ok := engine.MatchesResourceDescription(ns, rule) + if !ok { + glog.V(4).Infof("namespace %s does not satisfy the resource description for the rule ", ns.GetName()) + continue + } + filteredpolicies = append(filteredpolicies, policy) + } + } + + return filteredpolicies +} + +func applyPolicy(client *client.Client, resource unstructured.Unstructured, policy kyverno.Policy) info.PolicyInfo { + startTime := time.Now() + glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime) + defer func() { + glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime)) + }() + policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) + ruleInfos := engine.Generate(client, policy, resource) + policyInfo.AddRuleInfos(ruleInfos) + + return policyInfo +} + +// // func (nsc *NamespaceController) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) { +// // var fpolicies []*v1alpha1.Policy +// // policies, err := c.policyLister.List(labels.NewSelector()) +// // if err != nil { +// // glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules") +// // return nil, err +// // } +// // for _, p := range policies { +// // // Check if the policy contains a generatoin rule +// // for _, r := range p.Spec.Rules { +// // if r.Generation != nil { +// // // Check if the resource meets the description +// // data, err := json.Marshal(ns) +// // if err != nil { +// // glog.Error(err) +// // continue +// // } +// // // convert types of GVK +// // nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace") +// // // Hardcode as we have a informer on specified gvk +// // gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version} +// // if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) { +// // fpolicies = append(fpolicies, p) +// // break +// // } +// // } +// // } +// // } + +// // return fpolicies, nil +// // } + +// func (nsc *NamespaceController) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) { +// var eventInfo *event.Info +// var onViolation bool +// var msg string + +// policyInfo := info.NewPolicyInfo(p.Name, +// "Namespace", +// ns.Name, +// "", +// p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW + +// // convert to unstructured +// unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns) +// if err != nil { +// glog.Error(err) +// return +// } +// unstObj := unstructured.Unstructured{Object: unstrMap} +// ruleInfos := engine.Generate(c.client, p, unstObj) +// policyInfo.AddRuleInfos(ruleInfos) + +// // generate annotations on namespace +// c.createAnnotations(policyInfo) +// //TODO generate namespace on created resources + +// if !policyInfo.IsSuccessful() { +// glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name) +// for _, r := range ruleInfos { +// glog.Warning(r.Msgs) + +// if msg = strings.Join(r.Msgs, " "); strings.Contains(msg, "rule configuration not present in resource") { +// onViolation = true +// msg = fmt.Sprintf(`Resource creation violates generate rule '%s' of policy '%s'`, r.Name, policyInfo.Name) +// } +// } + +// if onViolation { +// glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name) +// // Policy Violation +// v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.FailedRules()) +// c.violationBuilder.Add(v) +// } else { +// // Event +// eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked, +// event.FPolicyApplyBlockCreate, policyInfo.RName, policyInfo.GetRuleNames(false)) + +// glog.V(2).Infof("Request blocked event info has prepared for %s/%s\n", policyKind, policyInfo.Name) + +// c.eventController.Add(eventInfo) +// } +// return +// } + +// glog.Infof("Generation from policy %s has succesfully applied to %s/%s", p.Name, policyInfo.RKind, policyInfo.RName) + +// eventInfo = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, +// event.PolicyApplied, event.SRulesApply, policyInfo.GetRuleNames(true), policyInfo.Name) + +// glog.V(2).Infof("Success event info has prepared for %s/%s\n", policyInfo.RKind, policyInfo.RName) + +// c.eventController.Add(eventInfo) +// } + +// func (nsc *NamespaceController) createAnnotations(pi *info.PolicyInfo) { +// //get resource +// obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName) +// if err != nil { +// glog.Error(err) +// return +// } +// // add annotation for policy application +// ann := obj.GetAnnotations() +// // Generation rules +// ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Generation) +// if err != nil { +// glog.Error(err) +// return +// } +// if gpatch == nil { +// // nothing to patch +// return +// } +// // add the anotation to the resource +// _, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch) +// if err != nil { +// glog.Error(err) +// return +// } +// } diff --git a/pkg/namespace/report.go b/pkg/namespace/report.go new file mode 100644 index 0000000000..57306c289c --- /dev/null +++ b/pkg/namespace/report.go @@ -0,0 +1,59 @@ +package namespace + +import ( + "fmt" + + "github.com/golang/glog" + "github.com/nirmata/kyverno/pkg/event" + "github.com/nirmata/kyverno/pkg/info" + "github.com/nirmata/kyverno/pkg/policyviolation" +) + +func (nsc *NamespaceController) report(policyInfos []info.PolicyInfo) { + // generate events + // generate policy violations + for _, policyInfo := range policyInfos { + // events + // success - policy applied on resource + // failure - policy/rule failed to apply on the resource + reportEvents(policyInfo, nsc.eventGen) + // policy violations + // failure - policy/rule failed to apply on the resource + } + + // generate policy violation + policyviolation.GeneratePolicyViolations(nsc.pvListerSynced, nsc.pvLister, nsc.kyvernoClient, policyInfos) + +} + +//reportEvents generates events for the failed resources +func reportEvents(policyInfo info.PolicyInfo, eventGen event.Interface) { + + if policyInfo.IsSuccessful() { + return + } + glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName) + for _, rule := range policyInfo.Rules { + if rule.IsSuccessful() { + continue + } + + // generate event on resource for each failed rule + e := &event.Info{} + e.Kind = policyInfo.RKind + e.Namespace = policyInfo.RNamespace + e.Name = policyInfo.RName + e.Reason = "Failure" + e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", policyInfo.Name, rule.RuleType.String(), rule.Name, rule.GetErrorString()) + eventGen.Add(e) + + } + // generate a event on policy for all failed rules + e := &event.Info{} + e.Kind = "Policy" + e.Namespace = "" + e.Name = policyInfo.Name + e.Reason = "Failure" + e.Message = fmt.Sprintf("failed to apply rules %s on resource %s/%s/%s", policyInfo.FailedRules(), policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName) + eventGen.Add(e) +} diff --git a/pkg/namespace/utils.go b/pkg/namespace/utils.go new file mode 100644 index 0000000000..ee0922fc44 --- /dev/null +++ b/pkg/namespace/utils.go @@ -0,0 +1,55 @@ +package namespace + +const ( + wqNamespace string = "namespace" + workerCount int = 1 + wqRetryLimit int = 5 + policyKind string = "Policy" +) + +// func namespaceMeetsRuleDescription(ns *corev1.Namespace, resourceDescription v1alpha1.ResourceDescription) bool { +// //REWORK Not needed but verify the 'Namespace' is defined in the list of supported kinds +// if !findKind(resourceDescription.Kinds, "Namespace") { +// return false +// } +// if resourceDescription.Name != nil { +// if !wildcard.Match(*resourceDescription.Name, ns.Name) { +// return false +// } +// } + +// if resourceDescription.Selector != nil { +// selector, err := metav1.LabelSelectorAsSelector(resourceDescription.Selector) +// if err != nil { +// return false +// } + +// labelSet := convertLabelsToLabelSet(ns.Labels) +// // labels +// if !selector.Matches(labelSet) { +// return false +// } +// } +// return true +// } + +// func convertLabelsToLabelSet(labelMap map[string]string) labels.Set { +// labelSet := make(labels.Set, len(labelMap)) +// // REWORK: check if the below works +// // if x, ok := labelMap.(labels.Set); !ok { + +// // } +// for k, v := range labelMap { +// labelSet[k] = v +// } +// return labelSet +// } + +// func findKind(kinds []string, kindGVK string) bool { +// for _, kind := range kinds { +// if kind == kindGVK { +// return true +// } +// } +// return false +// } diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index f756c5a999..2a3ce7af96 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -393,7 +393,6 @@ func (pc *PolicyController) syncPolicy(key string) error { // Deep-copy otherwise we are mutating our cache. // TODO: Deep-copy only when needed. p := policy.DeepCopy() - // TODO: Update Status to update ObserverdGeneration pvList, err := pc.getPolicyViolationsForPolicy(p) if err != nil { diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 2b6d4a90e6..e77e1a55db 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -6,6 +6,8 @@ import ( "github.com/minio/minio/pkg/wildcard" "k8s.io/api/admission/v1beta1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" ) type K8Resource struct { @@ -75,3 +77,12 @@ func ParseKinds(list string) []K8Resource { } return resources } + +//NewKubeClient returns a new kubernetes client +func NewKubeClient(config *rest.Config) (kubernetes.Interface, error) { + kclient, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return kclient, nil +} From aca4b13d5416e0ffd8b6ddc070862f740ae7c6a6 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 14 Aug 2019 15:06:38 -0700 Subject: [PATCH 28/66] add resource manager to namespace controller --- pkg/namespace/controller.go | 4 ++++ pkg/namespace/generation.go | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pkg/namespace/controller.go b/pkg/namespace/controller.go index 9966ce01ba..71518d3834 100644 --- a/pkg/namespace/controller.go +++ b/pkg/namespace/controller.go @@ -83,6 +83,10 @@ func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset, nsc.pvListerSynced = pInformer.Informer().HasSynced nsc.pvLister = pvInformer.Lister() + // resource manager + // rebuild after 300 seconds/ 5 mins + nsc.rm = NewResourceManager(300) + return nsc } func (nsc *NamespaceController) addNamespace(obj interface{}) { diff --git a/pkg/namespace/generation.go b/pkg/namespace/generation.go index 9851101cb8..7323c11424 100644 --- a/pkg/namespace/generation.go +++ b/pkg/namespace/generation.go @@ -69,6 +69,20 @@ func (rm *ResourceManager) ProcessResource(policy, pv, kind, ns, name, rv string return ok == false } +//Drop drop the cache after every rebuild interval mins +//TODO: or drop based on the size +func (rm *ResourceManager) Drop() { + timeSince := time.Since(rm.time) + glog.V(4).Infof("time since last cache reset time %v is %v", rm.time, timeSince) + glog.V(4).Infof("cache rebuild time %v", time.Duration(rm.rebuildTime)*time.Second) + if timeSince > time.Duration(rm.rebuildTime)*time.Second { + rm.mux.Lock() + defer rm.mux.Unlock() + rm.data = map[string]interface{}{} + rm.time = time.Now() + glog.V(4).Infof("dropping cache at time %v", rm.time) + } +} func buildKey(policy, pv, kind, ns, name, rv string) string { return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv } @@ -124,6 +138,8 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i glog.Infof("unable to convert to unstructured, not processing any policies: %v", err) return policyInfos } + nsc.rm.Drop() + ns := unstructured.Unstructured{Object: unstr} // get all the policies that have a generate rule and apply on the namespace @@ -131,8 +147,15 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i policies := listpolicies(ns, nsc.pLister) for _, policy := range policies { + // pre-processing, check if the policy and resource version has been processed before + if !nsc.rm.ProcessResource(policy.Name, policy.ResourceVersion, ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion()) { + glog.V(4).Infof("policy %s with resource version %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion()) + continue + } policyInfo := applyPolicy(nsc.client, ns, *policy) policyInfos = append(policyInfos, policyInfo) + // post-processing, register the resource as processed + nsc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion()) } return policyInfos } From 6b1b6dddfa23e6564e8f170b13652f75d51d36d3 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Wed, 14 Aug 2019 15:18:46 -0700 Subject: [PATCH 29/66] combine policy engine returns into single struct --- pkg/engine/engine.go | 17 ++++++++++++----- pkg/engine/mutation.go | 8 ++++++-- pkg/engine/utils.go | 7 +++++++ pkg/engine/validation.go | 12 +++++++----- pkg/engine/validation_test.go | 8 ++++---- pkg/webhooks/mutation.go | 25 ++++++++++++------------- pkg/webhooks/server.go | 8 ++++---- pkg/webhooks/validation.go | 15 ++++++--------- pkg/webhooks/webhookManager.go | 11 +++++------ 9 files changed, 63 insertions(+), 48 deletions(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 49332c7646..908a95c1a9 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -1,6 +1,8 @@ package engine import ( + "errors" + jsonpatch "github.com/evanphx/json-patch" "github.com/golang/glog" types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" @@ -45,11 +47,13 @@ func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) return nil, err } // Validation - vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) - policyInfo.AddRuleInfos(vruleInfos) - if err != nil { - return nil, err + response := Validate(*policy, rawResource, *res.Gvk) + if response != nil { + policyInfo.AddRuleInfos(response.RuleInfos) + } else { + return nil, errors.New("Failed to process validate rule, error parsing rawResource") } + if res.Gvk.Kind == "Namespace" { // Generation @@ -61,7 +65,10 @@ func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) } func mutation(p *types.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { - patches, _, ruleInfos := Mutate(*p, rawResource, *gvk) + response := Mutate(*p, rawResource, *gvk) + patches := response.Patches + ruleInfos := response.RuleInfos + if len(ruleInfos) == 0 { // no rules were processed return nil, nil diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index a518e9fb4f..cc5d097a21 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -8,7 +8,7 @@ import ( ) // Mutate performs mutation. Overlay first and then mutation patches -func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []byte, []*info.RuleInfo) { +func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) *EngineResponse { var allPatches, rulePatches [][]byte var err error var errs []error @@ -69,5 +69,9 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio ris = append(ris, ri) } - return allPatches, patchedDocument, ris + return &EngineResponse{ + Patches: allPatches, + PatchedDocument: patchedDocument, + RuleInfos: ris, + } } diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index ecb03f2eac..d2700de56b 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -12,6 +12,7 @@ import ( types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" v1helper "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -20,6 +21,12 @@ import ( "k8s.io/apimachinery/pkg/labels" ) +type EngineResponse struct { + Patches [][]byte + PatchedDocument []byte + RuleInfos []*info.RuleInfo +} + //ListResourcesThatApplyToPolicy returns list of resources that are filtered by policy rules func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy, filterK8Resources []utils.K8Resource) map[string]resourceInfo { // key uid diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 92a98e2451..0637dca710 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -17,13 +17,13 @@ import ( // Validate handles validating admission request // Checks the target resources for rules defined in the policy -func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) *EngineResponse { var resource interface{} ris := []*info.RuleInfo{} - err := json.Unmarshal(rawResource, &resource) - if err != nil { - return nil, err + if err := json.Unmarshal(rawResource, &resource); err != nil { + glog.Errorf("Failed to parse rawResource err: %v\n", err) + return nil } for _, rule := range policy.Spec.Rules { @@ -49,7 +49,9 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers ris = append(ris, ri) } - return ris, nil + return &EngineResponse{ + RuleInfos: ris, + } } // validateResourceWithPattern is a start of element-by-element validation process diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index de1117ebf2..5a73e87dcb 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -1573,8 +1573,8 @@ func TestValidate_ServiceTest(t *testing.T) { gvk := metav1.GroupVersionKind{ Kind: "Service", } - _, err := Validate(policy, rawResource, gvk) - assert.Assert(t, err == nil) + res := Validate(policy, rawResource, gvk) + assert.Assert(t, res != nil) } func TestValidate_MapHasFloats(t *testing.T) { @@ -1672,6 +1672,6 @@ func TestValidate_MapHasFloats(t *testing.T) { Kind: "Deployment", } - _, err := Validate(policy, rawResource, gvk) - assert.NilError(t, err) + res := Validate(policy, rawResource, gvk) + assert.Assert(t, res != nil) } diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 41f78e177b..d0633793ec 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -9,14 +9,13 @@ import ( ) // HandleMutation handles mutating webhook admission request -func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, [][]byte, []byte) { - var allPatches, policyPatches [][]byte +func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, *engine.EngineResponse) { + var allPatches [][]byte policyInfos := []*info.PolicyInfo{} - var ruleInfos []*info.RuleInfo - patchedDocument := request.Object.Raw + engineResponse := &engine.EngineResponse{PatchedDocument: request.Object.Raw} if request.Operation == v1beta1.Delete { - return true, nil, patchedDocument + return true, engineResponse } glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", @@ -27,7 +26,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool // Unable to connect to policy Lister to access policies glog.Errorln("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied") glog.Warning(err) - return true, nil, patchedDocument + return true, engineResponse } rname := engine.ParseNameFromObject(request.Object.Raw) @@ -59,13 +58,12 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - policyPatches, patchedDocument, ruleInfos = engine.Mutate(*policy, patchedDocument, request.Kind) - - policyInfo.AddRuleInfos(ruleInfos) + engineResponse = engine.Mutate(*policy, engineResponse.PatchedDocument, request.Kind) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) if !policyInfo.IsSuccessful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range ruleInfos { + for _, r := range engineResponse.RuleInfos { glog.Warningf("%s: %s\n", r.Name, r.Msgs) } } else { @@ -74,7 +72,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool if err != nil { glog.Info(err) } - allPatches = append(allPatches, policyPatches...) + allPatches = append(allPatches, engineResponse.Patches...) glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rns, rname) } policyInfos = append(policyInfos, policyInfo) @@ -93,9 +91,10 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool ok, msg := isAdmSuccesful(policyInfos) if ok { - return true, allPatches, patchedDocument + engineResponse.Patches = allPatches + return true, engineResponse } glog.Errorf("Failed to mutate the resource: %s\n", msg) - return false, nil, patchedDocument + return false, engineResponse } diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index b166054355..393a6bf1f1 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -140,7 +140,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { var response *v1beta1.AdmissionResponse - allowed, allPatches, patchedDocument := ws.HandleMutation(request) + allowed, engineResponse := ws.HandleMutation(request) if !allowed { // TODO: add failure message to response return &v1beta1.AdmissionResponse{ @@ -148,10 +148,10 @@ func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionReques } } - response = ws.HandleValidation(request, patchedDocument) - if response.Allowed && len(allPatches) > 0 { + response = ws.HandleValidation(request, engineResponse.PatchedDocument) + if response.Allowed && len(engineResponse.Patches) > 0 { patchType := v1beta1.PatchTypeJSONPatch - response.Patch = engine.JoinPatches(allPatches) + response.Patch = engine.JoinPatches(engineResponse.Patches) response.PatchType = &patchType } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index abeca7f792..f0cecab966 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -57,19 +57,16 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, raw request.Kind.Kind, rns, rname, request.UID, request.Operation) glog.Infof("Validating resource %s/%s/%s with policy %s with %d rules", rkind, rns, rname, policy.ObjectMeta.Name, len(policy.Spec.Rules)) - ruleInfos, err := engine.Validate(*policy, rawResource, request.Kind) - if err != nil { - // This is not policy error - // but if unable to parse request raw resource - // TODO : create event ? dont think so - glog.Error(err) + engineResponse := engine.Validate(*policy, rawResource, request.Kind) + if engineResponse == nil { + glog.Errorln("Failed to process validate rule, error parsing rawResource") continue } - policyInfo.AddRuleInfos(ruleInfos) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) if !policyInfo.IsSuccessful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range ruleInfos { + for _, r := range engineResponse.RuleInfos { glog.Warningf("%s: %s\n", r.Name, r.Msgs) } } else { @@ -79,7 +76,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, raw glog.Info(err) } - if len(ruleInfos) > 0 { + if len(engineResponse.RuleInfos) > 0 { glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns) } } diff --git a/pkg/webhooks/webhookManager.go b/pkg/webhooks/webhookManager.go index 5f8ec0a760..37b0c50ce3 100644 --- a/pkg/webhooks/webhookManager.go +++ b/pkg/webhooks/webhookManager.go @@ -26,14 +26,13 @@ func (ws *WebhookServer) manageWebhookConfigurations(policy v1alpha1.Policy, op } func (ws *WebhookServer) registerWebhookConfigurations(policy v1alpha1.Policy) error { - for _, rule := range policy.Spec.Rules { - if rule.Mutation != nil && !ws.webhookRegistrationClient.MutationRegistered.IsSet() { - if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil { - return err - } - glog.Infof("Mutating webhook registered") + if !ws.webhookRegistrationClient.MutationRegistered.IsSet() { + if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil { + return err } + glog.Infof("Mutating webhook registered") } + return nil } From 63a5337c9b2ad3f7812a560ac5fc446f524132a5 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 14 Aug 2019 18:40:33 -0700 Subject: [PATCH 30/66] generation test --- main.go | 9 +- .../kyverno/v1alpha1/expansion_generated.go | 28 ++- pkg/engine/generation.go | 5 +- pkg/namespace/controller.go | 14 +- pkg/namespace/expansion.go | 46 +++++ pkg/namespace/generation.go | 176 +----------------- pkg/policy/controller.go | 3 +- pkg/webhooks/mutation.go | 1 - pkg/webhooks/registration.go | 16 +- 9 files changed, 103 insertions(+), 195 deletions(-) create mode 100644 pkg/namespace/expansion.go diff --git a/main.go b/main.go index cef58b4080..b829fee827 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "time" "github.com/golang/glog" clientNew "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" @@ -53,8 +54,8 @@ func main() { // watches CRD resources: // - Policy // - PolicyVolation - // - cache resync time: 30 seconds - pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 30) + // - cache resync time: 10 seconds + pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10*time.Second) // EVENT GENERATOR // - generate event with retry egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies()) @@ -78,12 +79,12 @@ func main() { // NAMESPACE INFORMER // watches namespace resource - // - cache resync time: 30 seconds + // - cache resync time: 10 seconds kubeClient, err := utils.NewKubeClient(clientConfig) if err != nil { glog.Fatalf("Error creating kubernetes client: %v\n", err) } - kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 30) + kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Second) // GENERATE CONTROLLER // - watches for Namespace resource and generates resource based on the policy generate rule diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go b/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go index bfe398f1be..f37f2e4f76 100644 --- a/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go +++ b/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go @@ -22,6 +22,7 @@ import ( "fmt" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" ) @@ -30,11 +31,36 @@ import ( // PolicyLister. type PolicyListerExpansion interface { GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) + ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error) } // PolicyViolationListerExpansion allows custom methods to be added to // PolicyViolationLister. -type PolicyViolationListerExpansion interface{} +type PolicyViolationListerExpansion interface { + // List lists all PolicyViolations in the indexer with GVK. + // List lists all PolicyViolations in the indexer with GVK. + ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) +} + +//ListResources is a wrapper to List and adds the resource kind information +// as the lister is specific to a gvk we can harcode the values here +func (pvl *policyViolationLister) ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) { + policyviolations, err := pvl.List(selector) + for index := range policyviolations { + policyviolations[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("PolicyViolation")) + } + return policyviolations, nil +} + +//ListResources is a wrapper to List and adds the resource kind information +// as the lister is specific to a gvk we can harcode the values here +func (pl *policyLister) ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error) { + policies, err := pl.List(selector) + for index := range policies { + policies[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("Policy")) + } + return policies, err +} func (pl *policyLister) GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) { if len(pv.Labels) == 0 { diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 7dbaeb107b..5577cdc768 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -93,10 +93,10 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen _, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false) if err != nil { - glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, ns.GetKind(), ns.GetNamespace(), ns.GetName(), err) + glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, resource.GetNamespace(), resource.GetName(), err) return err } - glog.V(4).Infof("generate rule: created resource %s/%s/%s", gen.Kind, ns.GetKind(), ns.GetNamespace(), ns.GetName()) + glog.V(4).Infof("generate rule: created resource %s/%s/%s", gen.Kind, resource.GetNamespace(), resource.GetName()) return nil } @@ -125,7 +125,6 @@ func checkResource(config interface{}, resource *unstructured.Unstructured) (boo if err != nil { // unable to unmarshall return false, err - } var objData interface{} diff --git a/pkg/namespace/controller.go b/pkg/namespace/controller.go index 71518d3834..fae6b7539f 100644 --- a/pkg/namespace/controller.go +++ b/pkg/namespace/controller.go @@ -16,7 +16,6 @@ import ( v1 "k8s.io/api/core/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" v1Informer "k8s.io/client-go/informers/core/v1" - v1CoreLister "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" ) @@ -33,8 +32,10 @@ type NamespaceController struct { syncHandler func(nsKey string) error enqueueNs func(ns *v1.Namespace) + //nsLister provides expansion to the namespace lister to inject GVK for the resource + nsLister NamespaceListerExpansion // nLsister can list/get namespaces from the shared informer's store - nsLister v1CoreLister.NamespaceLister + // nsLister v1CoreLister.NamespaceLister // nsListerSynced returns true if the Namespace store has been synced at least once nsListerSynced cache.InformerSynced // pvLister can list/get policy violation from the shared informer's store @@ -77,7 +78,7 @@ func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset, nsc.enqueueNs = nsc.enqueue nsc.syncHandler = nsc.syncNamespace - nsc.nsLister = nsInformer.Lister() + nsc.nsLister = NewNamespaceLister(nsInformer.Lister()) nsc.nsListerSynced = nsInformer.Informer().HasSynced nsc.pLister = pInformer.Lister() nsc.pvListerSynced = pInformer.Informer().HasSynced @@ -89,6 +90,7 @@ func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset, return nsc } + func (nsc *NamespaceController) addNamespace(obj interface{}) { ns := obj.(*v1.Namespace) glog.V(4).Infof("Adding Namespace %s", ns.Name) @@ -167,7 +169,7 @@ func (nsc *NamespaceController) handleErr(err error, key interface{}) { } if nsc.queue.NumRequeues(key) < maxRetries { - glog.V(2).Infof("Error syncing Namespace %v: %v", key, err) + glog.V(2).Infof("Error syncing namespace %v: %v", key, err) nsc.queue.AddRateLimited(key) return } @@ -183,9 +185,9 @@ func (nsc *NamespaceController) syncNamespace(key string) error { defer func() { glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Since(startTime)) }() - namespace, err := nsc.nsLister.Get(key) + namespace, err := nsc.nsLister.GetResource(key) if errors.IsNotFound(err) { - glog.V(2).Infof("Namespace %v has been deleted", key) + glog.V(2).Infof("namespace %v has been deleted", key) return nil } if err != nil { diff --git a/pkg/namespace/expansion.go b/pkg/namespace/expansion.go new file mode 100644 index 0000000000..1e256e556e --- /dev/null +++ b/pkg/namespace/expansion.go @@ -0,0 +1,46 @@ +package namespace + +import ( + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + v1CoreLister "k8s.io/client-go/listers/core/v1" +) + +//NamespaceListerExpansion ... +type NamespaceListerExpansion interface { + v1CoreLister.NamespaceLister + // List lists all Namespaces in the indexer. + ListResources(selector labels.Selector) (ret []*v1.Namespace, err error) + // GetsResource and injects gvk + GetResource(name string) (*v1.Namespace, error) +} + +//NamespaceLister ... +type NamespaceLister struct { + v1CoreLister.NamespaceLister +} + +//NewNamespaceLister returns a new NamespaceLister +func NewNamespaceLister(nsLister v1CoreLister.NamespaceLister) NamespaceListerExpansion { + nsl := NamespaceLister{ + nsLister, + } + return &nsl +} + +//ListResources is a wrapper to List and adds the resource kind information +// as the lister is specific to a gvk we can harcode the values here +func (nsl *NamespaceLister) ListResources(selector labels.Selector) (ret []*v1.Namespace, err error) { + namespaces, err := nsl.List(selector) + for index := range namespaces { + namespaces[index].SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Namespace")) + } + return namespaces, err +} + +//GetResource is a wrapper to get the resource and inject the GVK +func (nsl *NamespaceLister) GetResource(name string) (*v1.Namespace, error) { + namespace, err := nsl.Get(name) + namespace.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Namespace")) + return namespace, err +} diff --git a/pkg/namespace/generation.go b/pkg/namespace/generation.go index 7323c11424..c21dc50638 100644 --- a/pkg/namespace/generation.go +++ b/pkg/namespace/generation.go @@ -87,53 +87,10 @@ func buildKey(policy, pv, kind, ns, name, rv string) string { return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv } -// func (nsc *NamespaceController) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) { -// var fpolicies []*v1alpha1.Policy -// policies, err := c.policyLister.List(labels.NewSelector()) -// if err != nil { -// glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules") -// return nil, err -// } -// for _, p := range policies { -// // Check if the policy contains a generatoin rule -// for _, r := range p.Spec.Rules { -// if r.Generation != nil { -// // Check if the resource meets the description -// data, err := json.Marshal(ns) -// if err != nil { -// glog.Error(err) -// continue -// } -// // convert types of GVK -// nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace") -// // Hardcode as we have a informer on specified gvk -// gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version} -// if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) { -// fpolicies = append(fpolicies, p) -// break -// } -// } -// } -// } - -// func (nsc *NamespaceController) processNamespace(ns *corev1.Namespace) error { -// //Get all policies and then verify if the namespace matches any of the defined selectors -// policies, err := c.listPolicies(ns) -// if err != nil { -// return err -// } -// // process policy on namespace -// for _, p := range policies { -// c.processPolicy(ns, p) -// } - -// return nil -// } - func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []info.PolicyInfo { var policyInfos []info.PolicyInfo // convert to unstructured - unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(namespace) + unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&namespace) if err != nil { glog.Infof("unable to convert to unstructured, not processing any policies: %v", err) return policyInfos @@ -142,9 +99,8 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i ns := unstructured.Unstructured{Object: unstr} - // get all the policies that have a generate rule and apply on the namespace + // get all the policies that have a generate rule and resource description satifies the namespace // apply policy on resource - policies := listpolicies(ns, nsc.pLister) for _, policy := range policies { // pre-processing, check if the policy and resource version has been processed before @@ -162,7 +118,7 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i func listpolicies(ns unstructured.Unstructured, pLister lister.PolicyLister) []*kyverno.Policy { var filteredpolicies []*kyverno.Policy - glog.V(4).Infof("listing policies that namespace %s", ns.GetName()) + glog.V(4).Infof("listing policies for namespace %s", ns.GetName()) policies, err := pLister.List(labels.NewSelector()) if err != nil { glog.Errorf("failed to get list policies: %v", err) @@ -175,13 +131,13 @@ func listpolicies(ns unstructured.Unstructured, pLister lister.PolicyLister) []* } ok := engine.MatchesResourceDescription(ns, rule) if !ok { - glog.V(4).Infof("namespace %s does not satisfy the resource description for the rule ", ns.GetName()) + glog.V(4).Infof("namespace %s does not satisfy the resource description for the policy %s rule %s", ns.GetName(), policy.Name, rule.Name) continue } + glog.V(4).Infof("namespace %s satisfies resource description for policy %s rule %s", ns.GetName(), policy.Name, rule.Name) filteredpolicies = append(filteredpolicies, policy) } } - return filteredpolicies } @@ -197,125 +153,3 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, poli return policyInfo } - -// // func (nsc *NamespaceController) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) { -// // var fpolicies []*v1alpha1.Policy -// // policies, err := c.policyLister.List(labels.NewSelector()) -// // if err != nil { -// // glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules") -// // return nil, err -// // } -// // for _, p := range policies { -// // // Check if the policy contains a generatoin rule -// // for _, r := range p.Spec.Rules { -// // if r.Generation != nil { -// // // Check if the resource meets the description -// // data, err := json.Marshal(ns) -// // if err != nil { -// // glog.Error(err) -// // continue -// // } -// // // convert types of GVK -// // nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace") -// // // Hardcode as we have a informer on specified gvk -// // gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version} -// // if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) { -// // fpolicies = append(fpolicies, p) -// // break -// // } -// // } -// // } -// // } - -// // return fpolicies, nil -// // } - -// func (nsc *NamespaceController) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) { -// var eventInfo *event.Info -// var onViolation bool -// var msg string - -// policyInfo := info.NewPolicyInfo(p.Name, -// "Namespace", -// ns.Name, -// "", -// p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW - -// // convert to unstructured -// unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns) -// if err != nil { -// glog.Error(err) -// return -// } -// unstObj := unstructured.Unstructured{Object: unstrMap} -// ruleInfos := engine.Generate(c.client, p, unstObj) -// policyInfo.AddRuleInfos(ruleInfos) - -// // generate annotations on namespace -// c.createAnnotations(policyInfo) -// //TODO generate namespace on created resources - -// if !policyInfo.IsSuccessful() { -// glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name) -// for _, r := range ruleInfos { -// glog.Warning(r.Msgs) - -// if msg = strings.Join(r.Msgs, " "); strings.Contains(msg, "rule configuration not present in resource") { -// onViolation = true -// msg = fmt.Sprintf(`Resource creation violates generate rule '%s' of policy '%s'`, r.Name, policyInfo.Name) -// } -// } - -// if onViolation { -// glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name) -// // Policy Violation -// v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.FailedRules()) -// c.violationBuilder.Add(v) -// } else { -// // Event -// eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked, -// event.FPolicyApplyBlockCreate, policyInfo.RName, policyInfo.GetRuleNames(false)) - -// glog.V(2).Infof("Request blocked event info has prepared for %s/%s\n", policyKind, policyInfo.Name) - -// c.eventController.Add(eventInfo) -// } -// return -// } - -// glog.Infof("Generation from policy %s has succesfully applied to %s/%s", p.Name, policyInfo.RKind, policyInfo.RName) - -// eventInfo = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, -// event.PolicyApplied, event.SRulesApply, policyInfo.GetRuleNames(true), policyInfo.Name) - -// glog.V(2).Infof("Success event info has prepared for %s/%s\n", policyInfo.RKind, policyInfo.RName) - -// c.eventController.Add(eventInfo) -// } - -// func (nsc *NamespaceController) createAnnotations(pi *info.PolicyInfo) { -// //get resource -// obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName) -// if err != nil { -// glog.Error(err) -// return -// } -// // add annotation for policy application -// ann := obj.GetAnnotations() -// // Generation rules -// ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Generation) -// if err != nil { -// glog.Error(err) -// return -// } -// if gpatch == nil { -// // nothing to patch -// return -// } -// // add the anotation to the resource -// _, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch) -// if err != nil { -// glog.Error(err) -// return -// } -// } diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 2a3ce7af96..9f71dc1542 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -113,7 +113,8 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. // resource manager // rebuild after 300 seconds/ 5 mins - pc.rm = NewResourceManager(300) + //TODO: pass the time in seconds instead of converting it internally + pc.rm = NewResourceManager(30) return &pc, nil } diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 5e8333ce7e..64e7e94128 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -57,7 +57,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be // continue // } //TODO: check if the GVK information is present in the request of we set it explicity here ? - glog.V(4).Infof("GVK is %v", resource.GroupVersionKind()) // resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind}) //TODO: passing policy value as we dont wont to modify the policy diff --git a/pkg/webhooks/registration.go b/pkg/webhooks/registration.go index adb35abbe5..e59d35dea5 100644 --- a/pkg/webhooks/registration.go +++ b/pkg/webhooks/registration.go @@ -285,11 +285,11 @@ func constructWebhook(name, servicePath string, caData []byte, validation bool) admregapi.Create, admregapi.Update, } - // Add operation DELETE for validation - if validation { - operationtypes = append(operationtypes, admregapi.Delete) + // // Add operation DELETE for validation + // if validation { + // operationtypes = append(operationtypes, admregapi.Delete) - } + // } return admregapi.Webhook{ Name: name, @@ -334,10 +334,10 @@ func constructDebugWebhook(name, url string, caData []byte, validation bool) adm admregapi.Create, admregapi.Update, } - // Add operation DELETE for validation - if validation { - operationtypes = append(operationtypes, admregapi.Delete) - } + // // Add operation DELETE for validation + // if validation { + // operationtypes = append(operationtypes, admregapi.Delete) + // } return admregapi.Webhook{ Name: name, From 998f6126797acb6f861aecb192deb35d27f06bcd Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Thu, 15 Aug 2019 15:23:54 -0700 Subject: [PATCH 31/66] fix engine unit test --- pkg/engine/overlay.go | 1 + pkg/engine/patches.go | 1 + pkg/engine/utils_test.go | 18 +++++++++--------- pkg/engine/validation_test.go | 20 ++++++++------------ 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index dd3121534b..4757ad3ca1 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -16,6 +16,7 @@ import ( // rawResource handles validating admission request // Checks the target resources for rules defined in the policy +// TODO: pass in the unstructured object in stead of raw byte? func processOverlay(rule kyverno.Rule, rawResource []byte) ([][]byte, error) { var resource interface{} if err := json.Unmarshal(rawResource, &resource); err != nil { diff --git a/pkg/engine/patches.go b/pkg/engine/patches.go index b13c0971a3..557611e6e0 100644 --- a/pkg/engine/patches.go +++ b/pkg/engine/patches.go @@ -13,6 +13,7 @@ import ( // ProcessPatches Returns array from separate patches that can be applied to the document // Returns error ONLY in case when creation of resource should be denied. +// TODO: pass in the unstructured object in stead of raw byte? func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, errs []error) { if len(resource) == 0 { errs = append(errs, errors.New("Source document for patching is empty")) diff --git a/pkg/engine/utils_test.go b/pkg/engine/utils_test.go index 0390fbb8b9..ad996c10f8 100644 --- a/pkg/engine/utils_test.go +++ b/pkg/engine/utils_test.go @@ -45,7 +45,7 @@ func TestResourceMeetsDescription_Name(t *testing.T) { resourceName := "test-config-map" resourceDescription := types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: nil, MatchExpressions: nil, @@ -68,7 +68,7 @@ func TestResourceMeetsDescription_Name(t *testing.T) { }`) assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceName = "test-config-map-new" + resourceDescription.Name = "test-config-map-new" assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) rawResource = []byte(`{ @@ -102,7 +102,7 @@ func TestResourceMeetsDescription_MatchExpressions(t *testing.T) { resourceName := "test-config-map" resourceDescription := types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: nil, MatchExpressions: []metav1.LabelSelectorRequirement{ @@ -173,7 +173,7 @@ func TestResourceMeetsDescription_MatchLabels(t *testing.T) { resourceName := "test-config-map" resourceDescription := types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "label1": "test1", @@ -213,7 +213,7 @@ func TestResourceMeetsDescription_MatchLabels(t *testing.T) { resourceDescription = types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "label3": "test1", @@ -230,7 +230,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { resourceName := "test-config-map" resourceDescription := types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "label1": "test1", @@ -265,7 +265,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { resourceDescription = types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "label1": "test1", @@ -297,7 +297,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { resourceDescription = types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "label1": "test1", @@ -318,7 +318,7 @@ func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { resourceDescription = types.ResourceDescription{ Kinds: []string{"ConfigMap"}, - Name: &resourceName, + Name: resourceName, Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ "label1": "test1", diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index fc945ffbb8..69e4dac447 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -6,7 +6,6 @@ import ( kubepolicy "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "gotest.tools/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestValidateString_AsteriskTest(t *testing.T) { @@ -1570,11 +1569,10 @@ func TestValidate_ServiceTest(t *testing.T) { var policy kubepolicy.Policy json.Unmarshal(rawPolicy, &policy) - gvk := metav1.GroupVersionKind{ - Kind: "Service", - } - res := Validate(policy, rawResource, gvk) - assert.Assert(t, res != nil) + resourceUnstructured, err := ConvertToUnstructured(rawResource) + assert.NilError(t, err) + res := Validate(policy, *resourceUnstructured) + assert.Assert(t, len(res.RuleInfos) == 0) } func TestValidate_MapHasFloats(t *testing.T) { @@ -1668,10 +1666,8 @@ func TestValidate_MapHasFloats(t *testing.T) { var policy kubepolicy.Policy json.Unmarshal(rawPolicy, &policy) - gvk := metav1.GroupVersionKind{ - Kind: "Deployment", - } - - res := Validate(policy, rawResource, gvk) - assert.Assert(t, res != nil) + resourceUnstructured, err := ConvertToUnstructured(rawResource) + assert.NilError(t, err) + res := Validate(policy, *resourceUnstructured) + assert.Assert(t, len(res.RuleInfos) == 0) } From 44db8b064ed77befdc06b1236c330aedb7445c3c Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Sat, 17 Aug 2019 09:45:57 -0700 Subject: [PATCH 32/66] resource description: support list of namespaces --- pkg/api/kyverno/v1alpha1/types.go | 8 +- .../kyverno/v1alpha1/zz_generated.deepcopy.go | 5 + pkg/engine/engine.go | 105 --- pkg/engine/overlay.go | 2 - pkg/engine/overlayPatch.go | 2 +- pkg/engine/utils.go | 304 ++------- pkg/engine/utils_test.go | 608 +++++++++--------- pkg/policy/existing.go | 13 +- pkg/webhooks/mutation.go | 3 +- pkg/webhooks/utils.go | 10 - pkg/webhooks/validation.go | 3 +- 11 files changed, 394 insertions(+), 669 deletions(-) delete mode 100644 pkg/engine/engine.go diff --git a/pkg/api/kyverno/v1alpha1/types.go b/pkg/api/kyverno/v1alpha1/types.go index 54a1f7f093..4d33f62941 100644 --- a/pkg/api/kyverno/v1alpha1/types.go +++ b/pkg/api/kyverno/v1alpha1/types.go @@ -45,10 +45,10 @@ type ExcludeResources struct { // ResourceDescription describes the resource to which the PolicyRule will be applied. type ResourceDescription struct { - Kinds []string `json:"kinds"` - Name string `json:"name"` - Namespace string `json:"namespace,omitempty"` - Selector *metav1.LabelSelector `json:"selector"` + Kinds []string `json:"kinds"` + Name string `json:"name"` + Namespaces []string `json:"namespace,omitempty"` + Selector *metav1.LabelSelector `json:"selector"` } // Mutation describes the way how Mutating Webhook will react on resource creation diff --git a/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go b/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go index 16fa7b7f56..cfc34d4a24 100644 --- a/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/kyverno/v1alpha1/zz_generated.deepcopy.go @@ -280,6 +280,11 @@ func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Selector != nil { in, out := &in.Selector, &out.Selector *out = new(v1.LabelSelector) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go deleted file mode 100644 index 93ca34e1b5..0000000000 --- a/pkg/engine/engine.go +++ /dev/null @@ -1,105 +0,0 @@ -package engine - -import ( - kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" - "github.com/nirmata/kyverno/pkg/info" - "github.com/nirmata/kyverno/pkg/utils" -) - -// ProcessExisting checks for mutation and validation violations of existing resources -func ProcessExisting(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) []info.PolicyInfo { - return nil - // glog.Infof("Applying policy %s on existing resources", policy.Name) - // // key uid - // resourceMap := ListResourcesThatApplyToPolicy(client, policy, filterK8Resources) - // policyInfos := []info.PolicyInfo{} - // // for the filtered resource apply policy - // for _, v := range resourceMap { - - // policyInfo, err := applyPolicy(client, policy, v) - // if err != nil { - // glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.Resource.GetName(), v.Resource.GetNamespace()) - // glog.Error(err) - // continue - // } - // policyInfos = append(policyInfos, policyInfo) - // } - - // return policyInfos -} - -// func applyPolicy(client *client.Client, policy *kyverno.Policy, res resourceInfo) (info.PolicyInfo, error) { -// var policyInfo info.PolicyInfo -// glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) -// rawResource, err := res.Resource.MarshalJSON() -// if err != nil { -// return policyInfo, err -// } -// // Mutate -// mruleInfos, err := mutation(policy, rawResource, res.Gvk) -// policyInfo.AddRuleInfos(mruleInfos) -// if err != nil { -// return policyInfo, err -// } -// // Validation -// //TODO check by value or pointer -// vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) -// if err != nil { -// return policyInfo, err -// } -// policyInfo.AddRuleInfos(vruleInfos) -// policyInfo = info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction) - -// if res.Gvk.Kind == "Namespace" { - -// // Generation -// gruleInfos := Generate(client, policy, res.Resource) -// policyInfo.AddRuleInfos(gruleInfos) -// } - -// return policyInfo, nil -// } - -// func mutation(p *kyverno.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { -// patches, ruleInfos := Mutate(*p, rawResource, *gvk) -// if len(ruleInfos) == 0 { -// // no rules were processed -// return nil, nil -// } -// // if there are any errors return -// for _, r := range ruleInfos { -// if !r.IsSuccessful() { -// return ruleInfos, nil -// } -// } -// // if there are no patches // for overlay -// if len(patches) == 0 { -// return ruleInfos, nil -// } -// // option 2: (original Resource + patch) compare with (original resource) -// mergePatches := JoinPatches(patches) -// // merge the patches -// patch, err := jsonpatch.DecodePatch(mergePatches) -// if err != nil { -// return nil, err -// } -// // apply the patches returned by mutate to the original resource -// patchedResource, err := patch.Apply(rawResource) -// if err != nil { -// return nil, err -// } -// // compare (original Resource + patch) vs (original resource) -// // to verify if they are equal -// ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation) -// if !jsonpatch.Equal(patchedResource, rawResource) { -// //resource does not match so there was a mutation rule violated -// // TODO : check the rule name "mutation rules" -// ruleInfo.Fail() -// ruleInfo.Add("resource does not satisfy mutation rules") -// } else { -// ruleInfo.Add("resource satisfys the mutation rule") -// } -// ruleInfos = append(ruleInfos, ruleInfo) -// return ruleInfos, nil -// } diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index 1ae274bb8c..a48a2c5b5f 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -31,11 +31,9 @@ func processOverlay(resourceUnstr unstructured.Unstructured, rule kyverno.Rule) return nil, err } - // resourceInfo := ParseResourceInfoFromObject(rawResource) patches, err := processOverlayPatches(resource, rule.Mutation.Overlay) if err != nil && strings.Contains(err.Error(), "Conditions are not met") { glog.V(4).Infof("overlay pattern %s does not match resource %s/%s", rule.Mutation.Overlay, resourceUnstr.GetNamespace(), resourceUnstr.GetName()) - // glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name) return nil, nil } diff --git a/pkg/engine/overlayPatch.go b/pkg/engine/overlayPatch.go index d71896b5c7..9f4df0ce90 100644 --- a/pkg/engine/overlayPatch.go +++ b/pkg/engine/overlayPatch.go @@ -13,7 +13,7 @@ func patchOverlay(rule kyverno.Rule, rawResource []byte) ([][]byte, error) { if err := json.Unmarshal(rawResource, &resource); err != nil { return nil, err } - + //TODO: evaluate, Unmarshall called thrice resourceInfo := ParseResourceInfoFromObject(rawResource) patches, err := processOverlayPatches(resource, rule.Mutation.Overlay) if err != nil && strings.Contains(err.Error(), "Conditions are not met") { diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index de13961155..167d0bf208 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -10,145 +10,13 @@ import ( "github.com/minio/minio/pkg/wildcard" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/utils" - v1helper "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" ) -//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 @@ -158,8 +26,8 @@ func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno return false } - // meta := parseMetadataFromObject(resourceRaw) name := resource.GetName() + namespace := resource.GetNamespace() if matches.Name != "" { @@ -175,14 +43,18 @@ func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno return false } } + // Matches - if matches.Namespace != "" && matches.Namespace != namespace { + // check if the resource namespace is defined in the list of namespaces for inclusion + if len(matches.Namespaces) > 0 && !utils.Contains(matches.Namespaces, namespace) { return false } // Exclude - if exclude.Namespace != "" && exclude.Namespace == namespace { + // check if the resource namespace is defined in the list of namespace for exclusion + if len(exclude.Namespaces) > 0 && utils.Contains(exclude.Namespaces, namespace) { return false } + // Matches if matches.Selector != nil { selector, err := metav1.LabelSelectorAsSelector(matches.Selector) @@ -209,82 +81,74 @@ func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno return true } -// ResourceMeetsDescription checks requests kind, name and labels to fit the policy rule -func ResourceMeetsDescription(resourceRaw []byte, matches kyverno.ResourceDescription, exclude kyverno.ResourceDescription, gvk metav1.GroupVersionKind) bool { - if !findKind(matches.Kinds, gvk.Kind) { - return false - } +// // ResourceMeetsDescription checks requests kind, name and labels to fit the policy rule +// func ResourceMeetsDescription(resourceRaw []byte, matches kyverno.ResourceDescription, exclude kyverno.ResourceDescription, gvk metav1.GroupVersionKind) bool { +// if !findKind(matches.Kinds, gvk.Kind) { +// return false +// } - if resourceRaw != nil { - meta := parseMetadataFromObject(resourceRaw) - name := ParseNameFromObject(resourceRaw) - namespace := ParseNamespaceFromObject(resourceRaw) +// if resourceRaw != nil { +// meta := parseMetadataFromObject(resourceRaw) +// name := ParseNameFromObject(resourceRaw) +// namespace := ParseNamespaceFromObject(resourceRaw) - if matches.Name != "" { - // Matches - if !wildcard.Match(matches.Name, name) { - return false - } - } - // Exclude - // the resource name matches the exclude resource name then reject - if exclude.Name != "" { - if wildcard.Match(exclude.Name, name) { - return false - } - } - // Matches - if matches.Namespace != "" && matches.Namespace != namespace { - return false - } - // Exclude - if exclude.Namespace != "" && exclude.Namespace == namespace { - return false - } - // Matches - if matches.Selector != nil { - selector, err := metav1.LabelSelectorAsSelector(matches.Selector) - if err != nil { - glog.Error(err) - return false - } - if meta != nil { - labelMap := parseLabelsFromMetadata(meta) - if !selector.Matches(labelMap) { - return false - } - } - } - // Exclude - if exclude.Selector != nil { - selector, err := metav1.LabelSelectorAsSelector(exclude.Selector) - // if the label selector is incorrect, should be fail or - if err != nil { - glog.Error(err) - return false - } +// if matches.Name != "" { +// // Matches +// if !wildcard.Match(matches.Name, name) { +// return false +// } +// } +// // Exclude +// // the resource name matches the exclude resource name then reject +// if exclude.Name != "" { +// if wildcard.Match(exclude.Name, name) { +// return false +// } +// } +// // 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) { +// return false +// } +// // Exclude +// // check if the resource namespace is defined in the list of namespace for exclusion +// if len(exclude.Namespaces) > 0 && utils.Contains(exclude.Namespaces, namespace) { +// return false +// } +// // Matches +// if matches.Selector != nil { +// selector, err := metav1.LabelSelectorAsSelector(matches.Selector) +// if err != nil { +// glog.Error(err) +// return false +// } +// if meta != nil { +// labelMap := parseLabelsFromMetadata(meta) +// if !selector.Matches(labelMap) { +// return false +// } +// } +// } +// // Exclude +// if exclude.Selector != nil { +// selector, err := metav1.LabelSelectorAsSelector(exclude.Selector) +// // if the label selector is incorrect, should be fail or +// if err != nil { +// glog.Error(err) +// return false +// } - if meta != nil { - labelMap := parseLabelsFromMetadata(meta) - if selector.Matches(labelMap) { - return false - } - } - } +// if meta != nil { +// labelMap := parseLabelsFromMetadata(meta) +// if selector.Matches(labelMap) { +// return false +// } +// } +// } - } - return true -} - -func parseMetadataFromObject(bytes []byte) map[string]interface{} { - var objectJSON map[string]interface{} - json.Unmarshal(bytes, &objectJSON) - meta, ok := objectJSON["metadata"].(map[string]interface{}) - if !ok { - return nil - } - return meta -} +// } +// return true +// } // ParseResourceInfoFromObject get kind/namepace/name from resource func ParseResourceInfoFromObject(rawResource []byte) string { @@ -303,18 +167,6 @@ func ParseKindFromObject(bytes []byte) string { return objectJSON["kind"].(string) } -func parseLabelsFromMetadata(meta map[string]interface{}) labels.Set { - if interfaceMap, ok := meta["labels"].(map[string]interface{}); ok { - labelMap := make(labels.Set, len(interfaceMap)) - - for key, value := range interfaceMap { - labelMap[key] = value.(string) - } - return labelMap - } - return nil -} - //ParseNameFromObject extracts resource name from JSON obj func ParseNameFromObject(bytes []byte) string { var objectJSON map[string]interface{} @@ -354,15 +206,6 @@ func ParseNamespaceFromObject(bytes []byte) string { return "" } -// ParseRegexPolicyResourceName returns true if policyResourceName is a regexp -func ParseRegexPolicyResourceName(policyResourceName string) (string, bool) { - regex := strings.Split(policyResourceName, "regex:") - if len(regex) == 1 { - return regex[0], false - } - return strings.Trim(regex[1], " "), true -} - func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} { result := make(map[string]interface{}) @@ -512,8 +355,3 @@ func convertToFloat(value interface{}) (float64, error) { return 0, fmt.Errorf("Could not convert %T to float64", value) } } - -type resourceInfo struct { - Resource unstructured.Unstructured - Gvk *metav1.GroupVersionKind -} diff --git a/pkg/engine/utils_test.go b/pkg/engine/utils_test.go index 0373209a48..fa3959f480 100644 --- a/pkg/engine/utils_test.go +++ b/pkg/engine/utils_test.go @@ -3,341 +3,339 @@ package engine import ( "testing" - types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" "gotest.tools/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestResourceMeetsDescription_Kind(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: nil, - MatchExpressions: nil, - }, - } - excludeResourcesResourceDesc := types.ResourceDescription{} - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} +// func TestResourceMeetsDescription_Kind(t *testing.T) { +// resourceName := "test-config-map" +// resourceDescription := types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: nil, +// MatchExpressions: nil, +// }, +// } +// excludeResourcesResourceDesc := types.ResourceDescription{} +// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) +// rawResource := []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription.Kinds[0] = "Deployment" - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription.Kinds[0] = "ConfigMap" - groupVersionKind.Kind = "Deployment" - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -} +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// resourceDescription.Kinds[0] = "Deployment" +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// resourceDescription.Kinds[0] = "ConfigMap" +// groupVersionKind.Kind = "Deployment" +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// } -func TestResourceMeetsDescription_Name(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: nil, - MatchExpressions: nil, - }, - } - excludeResourcesResourceDesc := types.ResourceDescription{} +// func TestResourceMeetsDescription_Name(t *testing.T) { +// resourceName := "test-config-map" +// resourceDescription := types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: nil, +// MatchExpressions: nil, +// }, +// } +// excludeResourcesResourceDesc := types.ResourceDescription{} - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} +// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) +// rawResource := []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceName = "test-config-map-new" - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// resourceName = "test-config-map-new" +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map-new", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// rawResource = []byte(`{ +// "metadata":{ +// "name":"test-config-map-new", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - rawResource = []byte(`{ - "metadata":{ - "name":"", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -} +// rawResource = []byte(`{ +// "metadata":{ +// "name":"", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// } -func TestResourceMeetsDescription_MatchExpressions(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: nil, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "NotIn", - Values: []string{ - "sometest1", - }, - }, - metav1.LabelSelectorRequirement{ - Key: "label1", - Operator: "In", - Values: []string{ - "test1", - "test8", - "test201", - }, - }, - metav1.LabelSelectorRequirement{ - Key: "label3", - Operator: "DoesNotExist", - Values: nil, - }, - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "test2", - }, - }, - }, - }, - } - excludeResourcesResourceDesc := types.ResourceDescription{} +// func TestResourceMeetsDescription_MatchExpressions(t *testing.T) { +// resourceName := "test-config-map" +// resourceDescription := types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: nil, +// MatchExpressions: []metav1.LabelSelectorRequirement{ +// metav1.LabelSelectorRequirement{ +// Key: "label2", +// Operator: "NotIn", +// Values: []string{ +// "sometest1", +// }, +// }, +// metav1.LabelSelectorRequirement{ +// Key: "label1", +// Operator: "In", +// Values: []string{ +// "test1", +// "test8", +// "test201", +// }, +// }, +// metav1.LabelSelectorRequirement{ +// Key: "label3", +// Operator: "DoesNotExist", +// Values: nil, +// }, +// metav1.LabelSelectorRequirement{ +// Key: "label2", +// Operator: "In", +// Values: []string{ +// "test2", +// }, +// }, +// }, +// }, +// } +// excludeResourcesResourceDesc := types.ResourceDescription{} - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) +// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} +// rawResource := []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1234567890", - "label2":"test2" - } - } - }`) +// rawResource = []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1234567890", +// "label2":"test2" +// } +// } +// }`) - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -} +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// } -func TestResourceMeetsDescription_MatchLabels(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - "label2": "test2", - }, - MatchExpressions: nil, - }, - } - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - excludeResourcesResourceDesc := types.ResourceDescription{} +// func TestResourceMeetsDescription_MatchLabels(t *testing.T) { +// resourceName := "test-config-map" +// resourceDescription := types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: map[string]string{ +// "label1": "test1", +// "label2": "test2", +// }, +// MatchExpressions: nil, +// }, +// } +// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} +// excludeResourcesResourceDesc := types.ResourceDescription{} - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// rawResource := []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label3":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// rawResource = []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label3":"test1", +// "label2":"test2" +// } +// } +// }`) +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label3": "test1", - "label2": "test2", - }, - MatchExpressions: nil, - }, - } +// resourceDescription = types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: map[string]string{ +// "label3": "test1", +// "label2": "test2", +// }, +// MatchExpressions: nil, +// }, +// } - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -} +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// } -func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "test2", - }, - }, - }, - }, - } - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - excludeResourcesResourceDesc := types.ResourceDescription{} +// func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { +// resourceName := "test-config-map" +// resourceDescription := types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: map[string]string{ +// "label1": "test1", +// }, +// MatchExpressions: []metav1.LabelSelectorRequirement{ +// metav1.LabelSelectorRequirement{ +// Key: "label2", +// Operator: "In", +// Values: []string{ +// "test2", +// }, +// }, +// }, +// }, +// } +// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} +// excludeResourcesResourceDesc := types.ResourceDescription{} - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) +// rawResource := []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "NotIn", - Values: []string{ - "sometest1", - }, - }, - }, - }, - } +// resourceDescription = types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: map[string]string{ +// "label1": "test1", +// }, +// MatchExpressions: []metav1.LabelSelectorRequirement{ +// metav1.LabelSelectorRequirement{ +// Key: "label2", +// Operator: "NotIn", +// Values: []string{ +// "sometest1", +// }, +// }, +// }, +// }, +// } - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// rawResource = []byte(`{ +// "metadata":{ +// "name":"test-config-map", +// "namespace":"default", +// "creationTimestamp":null, +// "labels":{ +// "label1":"test1", +// "label2":"test2" +// } +// } +// }`) +// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "sometest1", - }, - }, - }, - }, - } +// resourceDescription = types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: map[string]string{ +// "label1": "test1", +// }, +// MatchExpressions: []metav1.LabelSelectorRequirement{ +// metav1.LabelSelectorRequirement{ +// Key: "label2", +// Operator: "In", +// Values: []string{ +// "sometest1", +// }, +// }, +// }, +// }, +// } - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: &resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - "label3": "test3", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "test2", - }, - }, - }, - }, - } +// resourceDescription = types.ResourceDescription{ +// Kinds: []string{"ConfigMap"}, +// Name: &resourceName, +// Selector: &metav1.LabelSelector{ +// MatchLabels: map[string]string{ +// "label1": "test1", +// "label3": "test3", +// }, +// MatchExpressions: []metav1.LabelSelectorRequirement{ +// metav1.LabelSelectorRequirement{ +// Key: "label2", +// Operator: "In", +// Values: []string{ +// "test2", +// }, +// }, +// }, +// }, +// } - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -} +// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) +// } func TestWrappedWithParentheses_StringIsWrappedWithParentheses(t *testing.T) { str := "(something)" diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index b61ec78fd5..cf376dfc92 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -64,16 +64,15 @@ func listResources(client *client.Client, policy kyverno.Policy, filterK8Resourc glog.V(4).Infof("skipping processing policy %s rule %s for kind Namespace", policy.Name, rule.Name) continue } - //TODO: if namespace is not define can we default to * - if rule.MatchResources.Namespace != "" { - namespaces = append(namespaces, rule.MatchResources.Namespace) + if len(rule.MatchResources.Namespaces) > 0 { + namespaces = append(namespaces, rule.MatchResources.Namespaces...) } else { glog.V(4).Infof("processing policy %s rule %s, namespace not defined, getting all namespaces ", policy.Name, rule.Name) // get all namespaces namespaces = getAllNamespaces(client) } // check if exclude namespace is not clashing - namespaces = excludeNamespaces(namespaces, rule.ExcludeResources.Namespace) + namespaces = excludeNamespaces(namespaces, rule.ExcludeResources.Namespaces) // get resources in the namespaces for _, ns := range namespaces { @@ -158,13 +157,13 @@ func kindIsExcluded(kind string, list []string) bool { return false } -func excludeNamespaces(namespaces []string, excludeNs string) []string { - if excludeNs == "" { +func excludeNamespaces(namespaces, excludeNs []string) []string { + if len(excludeNs) == 0 { return namespaces } filteredNamespaces := []string{} for _, n := range namespaces { - if n == excludeNs { + if utils.Contains(excludeNs, n) { continue } filteredNamespaces = append(filteredNamespaces, n) diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 64e7e94128..a5e5482762 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -4,6 +4,7 @@ import ( "github.com/golang/glog" engine "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" + "github.com/nirmata/kyverno/pkg/utils" v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -42,7 +43,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be for _, policy := range policies { // check if policy has a rule for the admission request kind - if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { + if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) { continue } policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) diff --git a/pkg/webhooks/utils.go b/pkg/webhooks/utils.go index 7d6d9b5df2..e2406ac229 100644 --- a/pkg/webhooks/utils.go +++ b/pkg/webhooks/utils.go @@ -27,16 +27,6 @@ func isAdmSuccesful(policyInfos []info.PolicyInfo) (bool, string) { return admSuccess, strings.Join(errMsgs, ";") } -//StringInSlice checks if string is present in slice of strings -func StringInSlice(kind string, list []string) bool { - for _, b := range list { - if b == kind { - return true - } - } - return false -} - //ArrayFlags to store filterkinds type ArrayFlags []string diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 2e67548949..cf5c04d64f 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -5,6 +5,7 @@ import ( engine "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/policyviolation" + "github.com/nirmata/kyverno/pkg/utils" v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -42,7 +43,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 for _, policy := range policies { - if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) { + if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) { continue } From 6580e0e73a117e90bd6968ab409157e9bd471bf4 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Sat, 17 Aug 2019 09:58:14 -0700 Subject: [PATCH 33/66] remove temp clientNew --- .../clientset/versioned/clientset.go | 2 +- .../clientset/versioned/doc.go | 0 .../versioned/fake/clientset_generated.go | 6 +- .../clientset/versioned/fake/doc.go | 0 .../clientset/versioned/fake/register.go | 0 .../clientset/versioned/scheme/doc.go | 0 .../clientset/versioned/scheme/register.go | 0 .../informers/externalversions/factory.go | 6 +- .../informers/externalversions/generic.go | 0 .../internalinterfaces/factory_interfaces.go | 2 +- .../versioned/typed/kyverno/v1alpha1/doc.go | 20 -- .../typed/kyverno/v1alpha1/fake/doc.go | 20 -- .../v1alpha1/fake/fake_kyverno_client.go | 44 ----- .../kyverno/v1alpha1/fake/fake_policy.go | 131 ------------- .../v1alpha1/fake/fake_policyviolation.go | 131 ------------- .../kyverno/v1alpha1/generated_expansion.go | 23 --- .../typed/kyverno/v1alpha1/kyverno_client.go | 95 --------- .../typed/kyverno/v1alpha1/policy.go | 180 ------------------ .../typed/kyverno/v1alpha1/policyviolation.go | 180 ------------------ .../externalversions/kyverno/interface.go | 46 ----- .../kyverno/v1alpha1/interface.go | 52 ----- .../kyverno/v1alpha1/policy.go | 88 --------- .../kyverno/v1alpha1/policyviolation.go | 88 --------- .../kyverno/v1alpha1/expansion_generated.go | 101 ---------- .../listers/kyverno/v1alpha1/policy.go | 65 ------- .../kyverno/v1alpha1/policyviolation.go | 65 ------- pkg/event/controller.go | 13 +- pkg/namespace/controller.go | 14 +- pkg/namespace/generation.go | 4 +- pkg/policy/controller.go | 14 +- pkg/policyviolation/controller.go | 14 +- pkg/policyviolation/helpers.go | 8 +- pkg/webhooks/server.go | 14 +- scripts/new_update-codegen.sh | 27 --- scripts/update-codegen.sh | 4 +- 35 files changed, 50 insertions(+), 1407 deletions(-) rename pkg/{clientNew => client}/clientset/versioned/clientset.go (96%) rename pkg/{clientNew => client}/clientset/versioned/doc.go (100%) rename pkg/{clientNew => client}/clientset/versioned/fake/clientset_generated.go (89%) rename pkg/{clientNew => client}/clientset/versioned/fake/doc.go (100%) rename pkg/{clientNew => client}/clientset/versioned/fake/register.go (100%) rename pkg/{clientNew => client}/clientset/versioned/scheme/doc.go (100%) rename pkg/{clientNew => client}/clientset/versioned/scheme/register.go (100%) rename pkg/{clientNew => client}/informers/externalversions/factory.go (95%) rename pkg/{clientNew => client}/informers/externalversions/generic.go (100%) rename pkg/{clientNew => client}/informers/externalversions/internalinterfaces/factory_interfaces.go (94%) delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go delete mode 100644 pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go delete mode 100644 pkg/clientNew/informers/externalversions/kyverno/interface.go delete mode 100644 pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go delete mode 100644 pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go delete mode 100644 pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go delete mode 100644 pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go delete mode 100644 pkg/clientNew/listers/kyverno/v1alpha1/policy.go delete mode 100644 pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go delete mode 100755 scripts/new_update-codegen.sh diff --git a/pkg/clientNew/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go similarity index 96% rename from pkg/clientNew/clientset/versioned/clientset.go rename to pkg/client/clientset/versioned/clientset.go index 8c98dcd7db..9725f2aae2 100644 --- a/pkg/clientNew/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -19,7 +19,7 @@ limitations under the License. package versioned import ( - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1" + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" diff --git a/pkg/clientNew/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go similarity index 100% rename from pkg/clientNew/clientset/versioned/doc.go rename to pkg/client/clientset/versioned/doc.go diff --git a/pkg/clientNew/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go similarity index 89% rename from pkg/clientNew/clientset/versioned/fake/clientset_generated.go rename to pkg/client/clientset/versioned/fake/clientset_generated.go index 18e3e0d57b..ff0c5bc16f 100644 --- a/pkg/clientNew/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -19,9 +19,9 @@ limitations under the License. package fake import ( - clientset "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1" - fakekyvernov1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake" + clientset "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1alpha1" + fakekyvernov1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" diff --git a/pkg/clientNew/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go similarity index 100% rename from pkg/clientNew/clientset/versioned/fake/doc.go rename to pkg/client/clientset/versioned/fake/doc.go diff --git a/pkg/clientNew/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go similarity index 100% rename from pkg/clientNew/clientset/versioned/fake/register.go rename to pkg/client/clientset/versioned/fake/register.go diff --git a/pkg/clientNew/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go similarity index 100% rename from pkg/clientNew/clientset/versioned/scheme/doc.go rename to pkg/client/clientset/versioned/scheme/doc.go diff --git a/pkg/clientNew/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go similarity index 100% rename from pkg/clientNew/clientset/versioned/scheme/register.go rename to pkg/client/clientset/versioned/scheme/register.go diff --git a/pkg/clientNew/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go similarity index 95% rename from pkg/clientNew/informers/externalversions/factory.go rename to pkg/client/informers/externalversions/factory.go index 07bddb9a53..e2da4e38ee 100644 --- a/pkg/clientNew/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -23,9 +23,9 @@ import ( sync "sync" time "time" - versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" - kyverno "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno" + versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" + kyverno "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/clientNew/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go similarity index 100% rename from pkg/clientNew/informers/externalversions/generic.go rename to pkg/client/informers/externalversions/generic.go diff --git a/pkg/clientNew/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go similarity index 94% rename from pkg/clientNew/informers/externalversions/internalinterfaces/factory_interfaces.go rename to pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go index 2081ff4851..65a1d1de8f 100644 --- a/pkg/clientNew/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -21,7 +21,7 @@ package internalinterfaces import ( time "time" - versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" + versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" cache "k8s.io/client-go/tools/cache" diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go deleted file mode 100644 index df51baa4d4..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go deleted file mode 100644 index 16f4439906..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go deleted file mode 100644 index 244260245f..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeKyvernoV1alpha1 struct { - *testing.Fake -} - -func (c *FakeKyvernoV1alpha1) Policies() v1alpha1.PolicyInterface { - return &FakePolicies{c} -} - -func (c *FakeKyvernoV1alpha1) PolicyViolations() v1alpha1.PolicyViolationInterface { - return &FakePolicyViolations{c} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeKyvernoV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go deleted file mode 100644 index 76bd94b95f..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakePolicies implements PolicyInterface -type FakePolicies struct { - Fake *FakeKyvernoV1alpha1 -} - -var policiesResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policies"} - -var policiesKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "Policy"} - -// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. -func (c *FakePolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(policiesResource, name), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// List takes label and field selectors, and returns the list of Policies that match those selectors. -func (c *FakePolicies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(policiesResource, policiesKind, opts), &v1alpha1.PolicyList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.PolicyList{ListMeta: obj.(*v1alpha1.PolicyList).ListMeta} - for _, item := range obj.(*v1alpha1.PolicyList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested policies. -func (c *FakePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(policiesResource, opts)) -} - -// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *FakePolicies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(policiesResource, policy), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *FakePolicies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(policiesResource, policy), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePolicies) UpdateStatus(policy *v1alpha1.Policy) (*v1alpha1.Policy, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(policiesResource, "status", policy), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} - -// Delete takes name of the policy and deletes it. Returns an error if one occurs. -func (c *FakePolicies) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(policiesResource, name), &v1alpha1.Policy{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(policiesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.PolicyList{}) - return err -} - -// Patch applies the patch and returns the patched policy. -func (c *FakePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(policiesResource, name, pt, data, subresources...), &v1alpha1.Policy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Policy), err -} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go deleted file mode 100644 index e5306f11fe..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakePolicyViolations implements PolicyViolationInterface -type FakePolicyViolations struct { - Fake *FakeKyvernoV1alpha1 -} - -var policyviolationsResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policyviolations"} - -var policyviolationsKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "PolicyViolation"} - -// Get takes name of the policyViolation, and returns the corresponding policyViolation object, and an error if there is any. -func (c *FakePolicyViolations) Get(name string, options v1.GetOptions) (result *v1alpha1.PolicyViolation, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(policyviolationsResource, name), &v1alpha1.PolicyViolation{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PolicyViolation), err -} - -// List takes label and field selectors, and returns the list of PolicyViolations that match those selectors. -func (c *FakePolicyViolations) List(opts v1.ListOptions) (result *v1alpha1.PolicyViolationList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(policyviolationsResource, policyviolationsKind, opts), &v1alpha1.PolicyViolationList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.PolicyViolationList{ListMeta: obj.(*v1alpha1.PolicyViolationList).ListMeta} - for _, item := range obj.(*v1alpha1.PolicyViolationList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested policyViolations. -func (c *FakePolicyViolations) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(policyviolationsResource, opts)) -} - -// Create takes the representation of a policyViolation and creates it. Returns the server's representation of the policyViolation, and an error, if there is any. -func (c *FakePolicyViolations) Create(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(policyviolationsResource, policyViolation), &v1alpha1.PolicyViolation{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PolicyViolation), err -} - -// Update takes the representation of a policyViolation and updates it. Returns the server's representation of the policyViolation, and an error, if there is any. -func (c *FakePolicyViolations) Update(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(policyviolationsResource, policyViolation), &v1alpha1.PolicyViolation{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PolicyViolation), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePolicyViolations) UpdateStatus(policyViolation *v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(policyviolationsResource, "status", policyViolation), &v1alpha1.PolicyViolation{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PolicyViolation), err -} - -// Delete takes name of the policyViolation and deletes it. Returns an error if one occurs. -func (c *FakePolicyViolations) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(policyviolationsResource, name), &v1alpha1.PolicyViolation{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePolicyViolations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(policyviolationsResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.PolicyViolationList{}) - return err -} - -// Patch applies the patch and returns the patched policyViolation. -func (c *FakePolicyViolations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(policyviolationsResource, name, pt, data, subresources...), &v1alpha1.PolicyViolation{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.PolicyViolation), err -} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go deleted file mode 100644 index 442fa55942..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type PolicyExpansion interface{} - -type PolicyViolationExpansion interface{} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go deleted file mode 100644 index 93b837d4df..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" -) - -type KyvernoV1alpha1Interface interface { - RESTClient() rest.Interface - PoliciesGetter - PolicyViolationsGetter -} - -// KyvernoV1alpha1Client is used to interact with features provided by the kyverno.io group. -type KyvernoV1alpha1Client struct { - restClient rest.Interface -} - -func (c *KyvernoV1alpha1Client) Policies() PolicyInterface { - return newPolicies(c) -} - -func (c *KyvernoV1alpha1Client) PolicyViolations() PolicyViolationInterface { - return newPolicyViolations(c) -} - -// NewForConfig creates a new KyvernoV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*KyvernoV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &KyvernoV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new KyvernoV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *KyvernoV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new KyvernoV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *KyvernoV1alpha1Client { - return &KyvernoV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *KyvernoV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go deleted file mode 100644 index b7fb02600e..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policy.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - scheme "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// PoliciesGetter has a method to return a PolicyInterface. -// A group's client should implement this interface. -type PoliciesGetter interface { - Policies() PolicyInterface -} - -// PolicyInterface has methods to work with Policy resources. -type PolicyInterface interface { - Create(*v1alpha1.Policy) (*v1alpha1.Policy, error) - Update(*v1alpha1.Policy) (*v1alpha1.Policy, error) - UpdateStatus(*v1alpha1.Policy) (*v1alpha1.Policy, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Policy, error) - List(opts v1.ListOptions) (*v1alpha1.PolicyList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) - PolicyExpansion -} - -// policies implements PolicyInterface -type policies struct { - client rest.Interface -} - -// newPolicies returns a Policies -func newPolicies(c *KyvernoV1alpha1Client) *policies { - return &policies{ - client: c.RESTClient(), - } -} - -// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. -func (c *policies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Get(). - Resource("policies"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Policies that match those selectors. -func (c *policies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.PolicyList{} - err = c.client.Get(). - Resource("policies"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested policies. -func (c *policies) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("policies"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *policies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Post(). - Resource("policies"). - Body(policy). - Do(). - Into(result) - return -} - -// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. -func (c *policies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Put(). - Resource("policies"). - Name(policy.Name). - Body(policy). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *policies) UpdateStatus(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Put(). - Resource("policies"). - Name(policy.Name). - SubResource("status"). - Body(policy). - Do(). - Into(result) - return -} - -// Delete takes name of the policy and deletes it. Returns an error if one occurs. -func (c *policies) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("policies"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *policies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("policies"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched policy. -func (c *policies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { - result = &v1alpha1.Policy{} - err = c.client.Patch(pt). - Resource("policies"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go b/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go deleted file mode 100644 index bc93b28281..0000000000 --- a/pkg/clientNew/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - scheme "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// PolicyViolationsGetter has a method to return a PolicyViolationInterface. -// A group's client should implement this interface. -type PolicyViolationsGetter interface { - PolicyViolations() PolicyViolationInterface -} - -// PolicyViolationInterface has methods to work with PolicyViolation resources. -type PolicyViolationInterface interface { - Create(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) - Update(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) - UpdateStatus(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.PolicyViolation, error) - List(opts v1.ListOptions) (*v1alpha1.PolicyViolationList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) - PolicyViolationExpansion -} - -// policyViolations implements PolicyViolationInterface -type policyViolations struct { - client rest.Interface -} - -// newPolicyViolations returns a PolicyViolations -func newPolicyViolations(c *KyvernoV1alpha1Client) *policyViolations { - return &policyViolations{ - client: c.RESTClient(), - } -} - -// Get takes name of the policyViolation, and returns the corresponding policyViolation object, and an error if there is any. -func (c *policyViolations) Get(name string, options v1.GetOptions) (result *v1alpha1.PolicyViolation, err error) { - result = &v1alpha1.PolicyViolation{} - err = c.client.Get(). - Resource("policyviolations"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of PolicyViolations that match those selectors. -func (c *policyViolations) List(opts v1.ListOptions) (result *v1alpha1.PolicyViolationList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.PolicyViolationList{} - err = c.client.Get(). - Resource("policyviolations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested policyViolations. -func (c *policyViolations) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("policyviolations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a policyViolation and creates it. Returns the server's representation of the policyViolation, and an error, if there is any. -func (c *policyViolations) Create(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { - result = &v1alpha1.PolicyViolation{} - err = c.client.Post(). - Resource("policyviolations"). - Body(policyViolation). - Do(). - Into(result) - return -} - -// Update takes the representation of a policyViolation and updates it. Returns the server's representation of the policyViolation, and an error, if there is any. -func (c *policyViolations) Update(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { - result = &v1alpha1.PolicyViolation{} - err = c.client.Put(). - Resource("policyviolations"). - Name(policyViolation.Name). - Body(policyViolation). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *policyViolations) UpdateStatus(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { - result = &v1alpha1.PolicyViolation{} - err = c.client.Put(). - Resource("policyviolations"). - Name(policyViolation.Name). - SubResource("status"). - Body(policyViolation). - Do(). - Into(result) - return -} - -// Delete takes name of the policyViolation and deletes it. Returns an error if one occurs. -func (c *policyViolations) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("policyviolations"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *policyViolations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("policyviolations"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched policyViolation. -func (c *policyViolations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) { - result = &v1alpha1.PolicyViolation{} - err = c.client.Patch(pt). - Resource("policyviolations"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/clientNew/informers/externalversions/kyverno/interface.go b/pkg/clientNew/informers/externalversions/kyverno/interface.go deleted file mode 100644 index ee3f322230..0000000000 --- a/pkg/clientNew/informers/externalversions/kyverno/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package kyverno - -import ( - internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha1 provides access to shared informers for resources in V1alpha1. - V1alpha1() v1alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha1 returns a new v1alpha1.Interface. -func (g *group) V1alpha1() v1alpha1.Interface { - return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go deleted file mode 100644 index ceead2b318..0000000000 --- a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/interface.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // Policies returns a PolicyInformer. - Policies() PolicyInformer - // PolicyViolations returns a PolicyViolationInformer. - PolicyViolations() PolicyViolationInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// Policies returns a PolicyInformer. -func (v *version) Policies() PolicyInformer { - return &policyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// PolicyViolations returns a PolicyViolationInformer. -func (v *version) PolicyViolations() PolicyViolationInformer { - return &policyViolationInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go deleted file mode 100644 index b59ee59a86..0000000000 --- a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policy.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// PolicyInformer provides access to a shared informer and lister for -// Policies. -type PolicyInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.PolicyLister -} - -type policyInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewPolicyInformer constructs a new informer for Policy type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPolicyInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredPolicyInformer constructs a new informer for Policy type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KyvernoV1alpha1().Policies().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KyvernoV1alpha1().Policies().Watch(options) - }, - }, - &kyvernov1alpha1.Policy{}, - resyncPeriod, - indexers, - ) -} - -func (f *policyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *policyInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&kyvernov1alpha1.Policy{}, f.defaultInformer) -} - -func (f *policyInformer) Lister() v1alpha1.PolicyLister { - return v1alpha1.NewPolicyLister(f.Informer().GetIndexer()) -} diff --git a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go b/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go deleted file mode 100644 index 286b015fd9..0000000000 --- a/pkg/clientNew/informers/externalversions/kyverno/v1alpha1/policyviolation.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - versioned "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - internalinterfaces "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// PolicyViolationInformer provides access to a shared informer and lister for -// PolicyViolations. -type PolicyViolationInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.PolicyViolationLister -} - -type policyViolationInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewPolicyViolationInformer constructs a new informer for PolicyViolation type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewPolicyViolationInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPolicyViolationInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredPolicyViolationInformer constructs a new informer for PolicyViolation type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredPolicyViolationInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KyvernoV1alpha1().PolicyViolations().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.KyvernoV1alpha1().PolicyViolations().Watch(options) - }, - }, - &kyvernov1alpha1.PolicyViolation{}, - resyncPeriod, - indexers, - ) -} - -func (f *policyViolationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPolicyViolationInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *policyViolationInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&kyvernov1alpha1.PolicyViolation{}, f.defaultInformer) -} - -func (f *policyViolationInformer) Lister() v1alpha1.PolicyViolationLister { - return v1alpha1.NewPolicyViolationLister(f.Informer().GetIndexer()) -} diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go b/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go deleted file mode 100644 index f37f2e4f76..0000000000 --- a/pkg/clientNew/listers/kyverno/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "fmt" - - kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" -) - -// PolicyListerExpansion allows custom methods to be added to -// PolicyLister. -type PolicyListerExpansion interface { - GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) - ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error) -} - -// PolicyViolationListerExpansion allows custom methods to be added to -// PolicyViolationLister. -type PolicyViolationListerExpansion interface { - // List lists all PolicyViolations in the indexer with GVK. - // List lists all PolicyViolations in the indexer with GVK. - ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) -} - -//ListResources is a wrapper to List and adds the resource kind information -// as the lister is specific to a gvk we can harcode the values here -func (pvl *policyViolationLister) ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) { - policyviolations, err := pvl.List(selector) - for index := range policyviolations { - policyviolations[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("PolicyViolation")) - } - return policyviolations, nil -} - -//ListResources is a wrapper to List and adds the resource kind information -// as the lister is specific to a gvk we can harcode the values here -func (pl *policyLister) ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error) { - policies, err := pl.List(selector) - for index := range policies { - policies[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("Policy")) - } - return policies, err -} - -func (pl *policyLister) GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) { - if len(pv.Labels) == 0 { - return nil, fmt.Errorf("no Policy found for PolicyViolation %v because it has no labels", pv.Name) - } - - pList, err := pl.List(labels.Everything()) - if err != nil { - return nil, err - } - - var policies []*kyverno.Policy - for _, p := range pList { - policyLabelmap := map[string]string{"policy": p.Name} - - ls := &metav1.LabelSelector{} - err = metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&policyLabelmap, ls, nil) - if err != nil { - return nil, fmt.Errorf("failed to generate label sector of Policy name %s: %v", p.Name, err) - } - selector, err := metav1.LabelSelectorAsSelector(ls) - if err != nil { - return nil, fmt.Errorf("invalid label selector: %v", err) - } - // If a policy with a nil or empty selector creeps in, it should match nothing, not everything. - if selector.Empty() || !selector.Matches(labels.Set(pv.Labels)) { - continue - } - policies = append(policies, p) - } - - if len(policies) == 0 { - return nil, fmt.Errorf("could not find Policy set for PolicyViolation %s with labels: %v", pv.Name, pv.Labels) - } - - return policies, nil - -} diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/policy.go b/pkg/clientNew/listers/kyverno/v1alpha1/policy.go deleted file mode 100644 index c981855c33..0000000000 --- a/pkg/clientNew/listers/kyverno/v1alpha1/policy.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// PolicyLister helps list Policies. -type PolicyLister interface { - // List lists all Policies in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) - // Get retrieves the Policy from the index for a given name. - Get(name string) (*v1alpha1.Policy, error) - PolicyListerExpansion -} - -// policyLister implements the PolicyLister interface. -type policyLister struct { - indexer cache.Indexer -} - -// NewPolicyLister returns a new PolicyLister. -func NewPolicyLister(indexer cache.Indexer) PolicyLister { - return &policyLister{indexer: indexer} -} - -// List lists all Policies in the indexer. -func (s *policyLister) List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Policy)) - }) - return ret, err -} - -// Get retrieves the Policy from the index for a given name. -func (s *policyLister) Get(name string) (*v1alpha1.Policy, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("policy"), name) - } - return obj.(*v1alpha1.Policy), nil -} diff --git a/pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go b/pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go deleted file mode 100644 index e93ec95228..0000000000 --- a/pkg/clientNew/listers/kyverno/v1alpha1/policyviolation.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// PolicyViolationLister helps list PolicyViolations. -type PolicyViolationLister interface { - // List lists all PolicyViolations in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) - // Get retrieves the PolicyViolation from the index for a given name. - Get(name string) (*v1alpha1.PolicyViolation, error) - PolicyViolationListerExpansion -} - -// policyViolationLister implements the PolicyViolationLister interface. -type policyViolationLister struct { - indexer cache.Indexer -} - -// NewPolicyViolationLister returns a new PolicyViolationLister. -func NewPolicyViolationLister(indexer cache.Indexer) PolicyViolationLister { - return &policyViolationLister{indexer: indexer} -} - -// List lists all PolicyViolations in the indexer. -func (s *policyViolationLister) List(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.PolicyViolation)) - }) - return ret, err -} - -// Get retrieves the PolicyViolation from the index for a given name. -func (s *policyViolationLister) Get(name string) (*v1alpha1.PolicyViolation, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("policyviolation"), name) - } - return obj.(*v1alpha1.PolicyViolation), nil -} diff --git a/pkg/event/controller.go b/pkg/event/controller.go index 6fc0d57a0f..8cf6a234e3 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -5,10 +5,9 @@ import ( "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" - policyscheme "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" - informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" @@ -22,7 +21,7 @@ import ( //Generator generate events type Generator struct { client *client.Client - pLister lister.PolicyLister + pLister kyvernolister.PolicyLister queue workqueue.RateLimitingInterface recorder record.EventRecorder } @@ -34,7 +33,7 @@ type Interface interface { //NewEventGenerator to generate a new event controller func NewEventGenerator(client *client.Client, - pInformer informer.PolicyInformer) *Generator { + pInformer kyvernoinformer.PolicyInformer) *Generator { gen := Generator{ client: client, @@ -48,7 +47,7 @@ func NewEventGenerator(client *client.Client, func initRecorder(client *client.Client) record.EventRecorder { // Initliaze Event Broadcaster - err := policyscheme.AddToScheme(scheme.Scheme) + err := scheme.AddToScheme(scheme.Scheme) if err != nil { glog.Error(err) return nil diff --git a/pkg/namespace/controller.go b/pkg/namespace/controller.go index fae6b7539f..78e6340a3f 100644 --- a/pkg/namespace/controller.go +++ b/pkg/namespace/controller.go @@ -10,9 +10,9 @@ import ( "github.com/nirmata/kyverno/pkg/event" "k8s.io/apimachinery/pkg/api/errors" - kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" v1 "k8s.io/api/core/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" v1Informer "k8s.io/client-go/informers/core/v1" @@ -39,11 +39,11 @@ type NamespaceController struct { // nsListerSynced returns true if the Namespace store has been synced at least once nsListerSynced cache.InformerSynced // pvLister can list/get policy violation from the shared informer's store - pLister lister.PolicyLister + pLister kyvernolister.PolicyLister // pvListerSynced retrns true if the Policy store has been synced at least once pvListerSynced cache.InformerSynced // pvLister can list/get policy violation from the shared informer's store - pvLister lister.PolicyViolationLister + pvLister kyvernolister.PolicyViolationLister // eventGen provides interface to generate evenets eventGen event.Interface @@ -57,8 +57,8 @@ type NamespaceController struct { func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, nsInformer v1Informer.NamespaceInformer, - pInformer informer.PolicyInformer, - pvInformer informer.PolicyViolationInformer, + pInformer kyvernoinformer.PolicyInformer, + pvInformer kyvernoinformer.PolicyViolationInformer, eventGen event.Interface) *NamespaceController { //TODO: do we need to event recorder for this controller? // create the controller diff --git a/pkg/namespace/generation.go b/pkg/namespace/generation.go index c21dc50638..18a6a2908d 100644 --- a/pkg/namespace/generation.go +++ b/pkg/namespace/generation.go @@ -11,7 +11,7 @@ import ( kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -116,7 +116,7 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i return policyInfos } -func listpolicies(ns unstructured.Unstructured, pLister lister.PolicyLister) []*kyverno.Policy { +func listpolicies(ns unstructured.Unstructured, pLister kyvernolister.PolicyLister) []*kyverno.Policy { var filteredpolicies []*kyverno.Policy glog.V(4).Infof("listing policies for namespace %s", ns.GetName()) policies, err := pLister.List(labels.NewSelector()) diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 9f71dc1542..142f28594f 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -9,10 +9,10 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" - informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/utils" @@ -57,9 +57,9 @@ type PolicyController struct { // Policys that need to be synced queue workqueue.RateLimitingInterface // pLister can list/get policy from the shared informer's store - pLister lister.PolicyLister + pLister kyvernolister.PolicyLister // pvLister can list/get policy violation from the shared informer's store - pvLister lister.PolicyViolationLister + pvLister kyvernolister.PolicyViolationLister // pListerSynced returns true if the Policy store has been synced at least once pListerSynced cache.InformerSynced // pvListerSynced retrns true if the Policy store has been synced at least once @@ -71,7 +71,7 @@ type PolicyController struct { } // NewPolicyController create a new PolicyController -func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer informer.PolicyInformer, pvInformer informer.PolicyViolationInformer, eventGen event.Interface) (*PolicyController, error) { +func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer kyvernoinformer.PolicyInformer, pvInformer kyvernoinformer.PolicyViolationInformer, eventGen event.Interface) (*PolicyController, error) { // Event broad caster eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) diff --git a/pkg/policyviolation/controller.go b/pkg/policyviolation/controller.go index 0c1ff50741..9447199fa9 100644 --- a/pkg/policyviolation/controller.go +++ b/pkg/policyviolation/controller.go @@ -7,10 +7,10 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned/scheme" - informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -47,9 +47,9 @@ type PolicyViolationController struct { // Policys that need to be synced queue workqueue.RateLimitingInterface // pvLister can list/get policy violation from the shared informer's store - pvLister lister.PolicyViolationLister + pvLister kyvernolister.PolicyViolationLister // pLister can list/get policy from the shared informer's store - pLister lister.PolicyLister + pLister kyvernolister.PolicyLister // pListerSynced returns true if the Policy store has been synced at least once pListerSynced cache.InformerSynced // pvListerSynced retrns true if the Policy store has been synced at least once @@ -59,7 +59,7 @@ type PolicyViolationController struct { } //NewPolicyViolationController creates a new NewPolicyViolationController -func NewPolicyViolationController(client *client.Client, kyvernoClient *kyvernoclient.Clientset, pInformer informer.PolicyInformer, pvInformer informer.PolicyViolationInformer) (*PolicyViolationController, error) { +func NewPolicyViolationController(client *client.Client, kyvernoClient *kyvernoclient.Clientset, pInformer kyvernoinformer.PolicyInformer, pvInformer kyvernoinformer.PolicyViolationInformer) (*PolicyViolationController, error) { // Event broad caster eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) diff --git a/pkg/policyviolation/helpers.go b/pkg/policyviolation/helpers.go index ae62e87b5a..98ab01c0a9 100644 --- a/pkg/policyviolation/helpers.go +++ b/pkg/policyviolation/helpers.go @@ -6,8 +6,8 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/cache" @@ -54,7 +54,7 @@ func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation //generatePolicyViolations generate policyViolation resources for the rules that failed //TODO: check if pvListerSynced is needed -func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { +func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister kyvernolister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) { var pvs []kyverno.PolicyViolation for _, policyInfo := range policyInfos { if !policyInfo.IsSuccessful() { @@ -100,7 +100,7 @@ func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister list } //TODO: change the name -func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister lister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { +func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister kyvernolister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) { // TODO: check for existing ov using label selectors on resource and policy labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()} ls := &metav1.LabelSelector{} diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 77d3858e62..649a0f3f9d 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -11,9 +11,9 @@ import ( "time" "github.com/golang/glog" - kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" @@ -29,8 +29,8 @@ type WebhookServer struct { server http.Server client *client.Client kyvernoClient *kyvernoclient.Clientset - pLister lister.PolicyLister - pvLister lister.PolicyViolationLister + pLister kyvernolister.PolicyLister + pvLister kyvernolister.PolicyViolationLister pListerSynced cache.InformerSynced pvListerSynced cache.InformerSynced eventGen event.Interface @@ -43,8 +43,8 @@ func NewWebhookServer( kyvernoClient *kyvernoclient.Clientset, client *client.Client, tlsPair *tlsutils.TlsPemPair, - pInformer informer.PolicyInformer, - pvInormer informer.PolicyViolationInformer, + pInformer kyvernoinformer.PolicyInformer, + pvInormer kyvernoinformer.PolicyViolationInformer, eventGen event.Interface, filterK8Resources string) (*WebhookServer, error) { diff --git a/scripts/new_update-codegen.sh b/scripts/new_update-codegen.sh deleted file mode 100755 index 38f54515e6..0000000000 --- a/scripts/new_update-codegen.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -set -o errexit -set -o nounset -set -o pipefail - -case "$(uname -s)" in - Linux*) linkutil=readlink;; - Darwin*) linkutil=greadlink;; - *) machine="UNKNOWN:${unameOut}" -esac - -# get nirmata root -NIRMATA_DIR=$(dirname ${BASH_SOURCE})/.. -NIRMATA_ROOT=$(${linkutil} -f ${NIRMATA_DIR}) - -# get relative path to code generation script -CODEGEN_PKG=${NIRMATA_DIR}/vendor/k8s.io/code-generator - -# get relative path of nirmata -NIRMATA_PKG=${NIRMATA_ROOT#"${GOPATH}/src/"} - -# perform code generation -${CODEGEN_PKG}/generate-groups.sh \ - "deepcopy,client,informer,lister" \ - ${NIRMATA_PKG}/pkg/clientNew \ - ${NIRMATA_PKG}/pkg/api \ - kyverno:v1alpha1 diff --git a/scripts/update-codegen.sh b/scripts/update-codegen.sh index fd53c78cb5..b339547fa8 100755 --- a/scripts/update-codegen.sh +++ b/scripts/update-codegen.sh @@ -23,5 +23,5 @@ NIRMATA_PKG=${NIRMATA_ROOT#"${GOPATH}/src/"} ${CODEGEN_PKG}/generate-groups.sh \ "deepcopy,client,informer,lister" \ ${NIRMATA_PKG}/pkg/client \ - ${NIRMATA_PKG}/pkg/apis \ - policy:v1alpha1 + ${NIRMATA_PKG}/pkg/api \ + kyverno:v1alpha1 From 24c9ad0926bcf6634fb0805585c2d281d5585b91 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Sat, 17 Aug 2019 09:59:13 -0700 Subject: [PATCH 34/66] update openapi spec --- definitions/install.yaml | 6 ++++-- definitions/install_debug.yaml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/definitions/install.yaml b/definitions/install.yaml index 1e44e139f7..a0829c6f16 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -54,8 +54,10 @@ spec: type: string name: type: string - namespace: - type: string + namespaces: + type: array + items: + type: string selector: properties: matchLabels: diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 0d50728fdd..3111562f22 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -54,8 +54,10 @@ spec: type: string name: type: string - namespace: - type: string + namespaces: + type: array + items: + type: string selector: properties: matchLabels: From 4087fdb184e8d2a898a32a4435da4c0b1d51c7d2 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Sat, 17 Aug 2019 10:11:47 -0700 Subject: [PATCH 35/66] change for client refactor --- main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index b829fee827..68a6d0765d 100644 --- a/main.go +++ b/main.go @@ -5,8 +5,8 @@ import ( "time" "github.com/golang/glog" - clientNew "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - kyvernoinformer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions" "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" event "github.com/nirmata/kyverno/pkg/event" @@ -39,7 +39,7 @@ func main() { // access CRD resources // - Policy // - PolicyViolation - pclient, err := clientNew.NewForConfig(clientConfig) + pclient, err := kyvernoclient.NewForConfig(clientConfig) if err != nil { glog.Fatalf("Error creating client: %v\n", err) } From d4c0bbc59ebd100746e3a69d07a270d6861933a3 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 19 Aug 2019 10:00:39 -0700 Subject: [PATCH 36/66] rebase --- main.go | 6 +++--- scripts/new_update-codegen.sh | 27 --------------------------- 2 files changed, 3 insertions(+), 30 deletions(-) delete mode 100755 scripts/new_update-codegen.sh diff --git a/main.go b/main.go index b829fee827..68a6d0765d 100644 --- a/main.go +++ b/main.go @@ -5,8 +5,8 @@ import ( "time" "github.com/golang/glog" - clientNew "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned" - kyvernoinformer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions" "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" event "github.com/nirmata/kyverno/pkg/event" @@ -39,7 +39,7 @@ func main() { // access CRD resources // - Policy // - PolicyViolation - pclient, err := clientNew.NewForConfig(clientConfig) + pclient, err := kyvernoclient.NewForConfig(clientConfig) if err != nil { glog.Fatalf("Error creating client: %v\n", err) } diff --git a/scripts/new_update-codegen.sh b/scripts/new_update-codegen.sh deleted file mode 100755 index 38f54515e6..0000000000 --- a/scripts/new_update-codegen.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -set -o errexit -set -o nounset -set -o pipefail - -case "$(uname -s)" in - Linux*) linkutil=readlink;; - Darwin*) linkutil=greadlink;; - *) machine="UNKNOWN:${unameOut}" -esac - -# get nirmata root -NIRMATA_DIR=$(dirname ${BASH_SOURCE})/.. -NIRMATA_ROOT=$(${linkutil} -f ${NIRMATA_DIR}) - -# get relative path to code generation script -CODEGEN_PKG=${NIRMATA_DIR}/vendor/k8s.io/code-generator - -# get relative path of nirmata -NIRMATA_PKG=${NIRMATA_ROOT#"${GOPATH}/src/"} - -# perform code generation -${CODEGEN_PKG}/generate-groups.sh \ - "deepcopy,client,informer,lister" \ - ${NIRMATA_PKG}/pkg/clientNew \ - ${NIRMATA_PKG}/pkg/api \ - kyverno:v1alpha1 From 9cfdf45bbc3b997293d114dfb2013888e1e79f8d Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 19 Aug 2019 11:52:48 -0700 Subject: [PATCH 37/66] fixes --- pkg/api/kyverno/v1alpha1/types.go | 2 +- pkg/policy/existing.go | 1 + pkg/policyviolation/controller.go | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/api/kyverno/v1alpha1/types.go b/pkg/api/kyverno/v1alpha1/types.go index 4d33f62941..5597f97e23 100644 --- a/pkg/api/kyverno/v1alpha1/types.go +++ b/pkg/api/kyverno/v1alpha1/types.go @@ -47,7 +47,7 @@ type ExcludeResources struct { type ResourceDescription struct { Kinds []string `json:"kinds"` Name string `json:"name"` - Namespaces []string `json:"namespace,omitempty"` + Namespaces []string `json:"namespaces,omitempty"` Selector *metav1.LabelSelector `json:"selector"` } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index cf376dfc92..eaa5d0657d 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -66,6 +66,7 @@ func listResources(client *client.Client, policy kyverno.Policy, filterK8Resourc } if len(rule.MatchResources.Namespaces) > 0 { namespaces = append(namespaces, rule.MatchResources.Namespaces...) + glog.V(4).Infof("namespaces specified for inclusion: %v", rule.MatchResources.Namespaces) } else { glog.V(4).Infof("processing policy %s rule %s, namespace not defined, getting all namespaces ", policy.Name, rule.Name) // get all namespaces diff --git a/pkg/policyviolation/controller.go b/pkg/policyviolation/controller.go index 9447199fa9..045f59b857 100644 --- a/pkg/policyviolation/controller.go +++ b/pkg/policyviolation/controller.go @@ -212,6 +212,7 @@ func (pvc *PolicyViolationController) syncPolicyViolation(key string) error { pv := policyViolation.DeepCopy() // TODO: Update Status to update ObserverdGeneration // TODO: check if the policy violation refers to a resource thats active ? // done by policy controller + // TODO: remove the PV, if the corresponding policy is not present // TODO: additional check on deleted webhook for a resource, to delete a policy violation it has a policy violation // list the resource with label selectors, but this can be expensive for each delete request of a resource if err := pvc.syncActiveResource(pv); err != nil { @@ -242,6 +243,8 @@ func (pvc *PolicyViolationController) syncActiveResource(curPv *kyverno.PolicyVi glog.V(4).Infof("error while retrieved resource %s/%s/%s: %v", rspec.Kind, rspec.Namespace, rspec.Name, err) return err } + //TODO- if the policy is not present, remove the policy violation + return nil } From bbf30a9ec0a20f04a7de3d8fbc0d7128100cc84e Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 19 Aug 2019 11:54:25 -0700 Subject: [PATCH 38/66] update crd --- definitions/install.yaml | 6 ++++-- definitions/install_debug.yaml | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/definitions/install.yaml b/definitions/install.yaml index a0829c6f16..72112a508d 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -94,8 +94,10 @@ spec: type: string name: type: string - namespace: - type: string + namespaces: + type: array + items: + type: string selector: properties: matchLabels: diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 3111562f22..7a7cb7c95d 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -94,8 +94,10 @@ spec: type: string name: type: string - namespace: - type: string + namespaces: + type: array + items: + type: string selector: properties: matchLabels: From 4c34ff7a3775164ec480ea174746df8421645553 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Mon, 19 Aug 2019 16:10:10 -0700 Subject: [PATCH 39/66] Change of annotation purpose #262 --- pkg/engine/mutation.go | 12 ++-- pkg/engine/validation.go | 2 +- pkg/info/info.go | 2 +- pkg/webhooks/annotations.go | 113 ++++++++++++++++++++++++++++++++++++ pkg/webhooks/mutation.go | 10 ++-- pkg/webhooks/validation.go | 1 - 6 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 pkg/webhooks/annotations.go diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 2eb42a19d2..fe367a2def 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -56,11 +56,8 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes ruleInfo.Addf("Rule %s: Overlay succesfully applied.", rule.Name) - // merge the json patches - patch := JoinPatches(rulePatches) - // strip slashes from string - ruleInfo.Changes = string(patch) + ruleInfo.Patches = rulePatches allPatches = append(allPatches, rulePatches...) glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) @@ -83,6 +80,8 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes } else { glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) ruleInfo.Addf("Patches succesfully applied.") + + ruleInfo.Patches = rulePatches allPatches = append(allPatches, rulePatches...) } } @@ -96,6 +95,11 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes } patchedResource, err := ConvertToUnstructured(patchedDocument) + if err != nil { + glog.Errorf("Failed to convert patched resource to unstructuredtype, err%v\n:", err) + return EngineResponse{PatchedResource: resource} + } + return EngineResponse{ Patches: allPatches, PatchedResource: *patchedResource, diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 29e017befa..2d8e36560c 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -20,7 +20,7 @@ import ( func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse { resourceRaw, err := resource.MarshalJSON() if err != nil { - glog.V(4).Infof("unable to marshal resource : %v\n", err) + glog.V(4).Infof("Skip processing validating rule, unable to marshal resource : %v\n", err) return EngineResponse{PatchedResource: resource} } diff --git a/pkg/info/info.go b/pkg/info/info.go index d8b742e350..b8aaa57db3 100644 --- a/pkg/info/info.go +++ b/pkg/info/info.go @@ -104,7 +104,7 @@ type RuleInfo struct { Name string RuleType RuleType Msgs []string - Changes string // this will store the mutation patch being applied by the rule + Patches [][]byte // this will store the mutation patch being applied by the rule success bool } diff --git a/pkg/webhooks/annotations.go b/pkg/webhooks/annotations.go new file mode 100644 index 0000000000..41a37633f7 --- /dev/null +++ b/pkg/webhooks/annotations.go @@ -0,0 +1,113 @@ +package webhooks + +import ( + "encoding/json" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + jsonpatch "github.com/evanphx/json-patch" + "github.com/golang/glog" + "github.com/nirmata/kyverno/pkg/info" +) + +const ( + policyAnnotation = "policies.kyverno.io" + // lastAppliedPatches = policyAnnotation + "last-applied-patches" +) + +type policyPatch struct { + PolicyName string `json:"policyname"` + // RulePatches []string `json:"patches"` + RulePatches interface{} `json:"patches"` +} + +type rulePatch struct { + RuleName string `json:"rulename"` + Op string `json:"op"` + Path string `json:"path"` +} + +type response struct { + Op string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value"` +} + +func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos []info.PolicyInfo) []byte { + annots := resource.GetAnnotations() + if annots == nil { + annots = map[string]string{} + } + + var patchResponse response + value := annotationFromPolicies(policyInfos) + if _, ok := annots[policyAnnotation]; ok { + // create update patch string + patchResponse = response{ + Op: "replace", + Path: "/metadata/annotations/" + policyAnnotation, + Value: string(value), + } + } else { + patchResponse = response{ + Op: "add", + Path: "/metadata/annotations", + Value: map[string]string{policyAnnotation: string(value)}, + } + } + + patchByte, _ := json.Marshal(patchResponse) + + // check the patch + _, err := jsonpatch.DecodePatch([]byte("[" + string(patchByte) + "]")) + if err != nil { + glog.Errorf("Failed to make patch from annotation'%s', err: %v\n ", string(patchByte), err) + } + + return patchByte +} + +func annotationFromPolicies(policyInfos []info.PolicyInfo) []byte { + var policyPatches []policyPatch + for _, policyInfo := range policyInfos { + var pp policyPatch + + pp.PolicyName = policyInfo.Name + pp.RulePatches = annotationFromPolicy(policyInfo) + policyPatches = append(policyPatches, pp) + } + + result, _ := json.Marshal(policyPatches) + + return result +} + +func annotationFromPolicy(policyInfo info.PolicyInfo) []rulePatch { + if !policyInfo.IsSuccessful() { + glog.V(2).Infof("Policy %s failed, skip preparing annotation\n", policyInfo.Name) + return nil + } + + var rulePatches []rulePatch + for _, ruleInfo := range policyInfo.Rules { + + for _, patch := range ruleInfo.Patches { + var patchmap map[string]string + + if err := json.Unmarshal(patch, &patchmap); err != nil { + glog.Errorf("Failed to parse patch bytes, err: %v\n", err) + continue + } + + rp := rulePatch{ + RuleName: ruleInfo.Name, + Op: patchmap["op"], + Path: patchmap["path"]} + + rulePatches = append(rulePatches, rp) + glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches) + } + } + + return rulePatches +} diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index a54a6151f4..88c60afa4a 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -62,18 +62,20 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool } continue } - // build annotations per policy being applied to show the mutation changes - patches = append(patches, engineResponse.Patches...) - glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) + patches = append(patches, engineResponse.Patches...) + policyInfos = append(policyInfos, policyInfo) + glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) } // ADD ANNOTATIONS - // TODO: merge the annotation patch with the patch response // ADD EVENTS if len(patches) > 0 { eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation) ws.eventGen.Add(eventsInfo...) + + annotation := prepareAnnotationPatches(resource, policyInfos) + patches = append(patches, annotation) } ok, msg := isAdmSuccesful(policyInfos) diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index ddad42e725..56aa4d1970 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -52,7 +52,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, res engineResponse := engine.Validate(*policy, resource) if len(engineResponse.RuleInfos) == 0 { - glog.Errorln("Failed to process validate rule, error parsing rawResource") continue } From 8b1066be29af5c858ee1a967a364f7f86ae3d955 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 19 Aug 2019 16:40:10 -0700 Subject: [PATCH 40/66] initial commit --- pkg/engine/generation.go | 16 ++++++++++++++++ pkg/engine/mutation.go | 18 +++++++++++++++++- pkg/engine/validation.go | 16 ++++++++++++++++ pkg/policy/status.go | 14 ++++++++++++++ pkg/policyviolation/controller.go | 2 +- 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 pkg/policy/status.go diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 5577cdc768..cddc891743 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -3,6 +3,7 @@ package engine import ( "encoding/json" "errors" + "time" "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" @@ -15,6 +16,20 @@ import ( //Generate apply generation rules on a resource func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) []info.RuleInfo { + var executionTime time.Duration + var rulesAppliedCount int + startTime := time.Now() + glog.V(4).Infof("started applying generation rules of policy %q (%v)", policy.Name, startTime) + defer func() { + executionTime = time.Since(startTime) + glog.V(4).Infof("Finished applying generation rules policy %q (%v)", policy.Name, executionTime) + glog.V(4).Infof("Generation Rules appplied succesfully count %q for policy %q", rulesAppliedCount, policy.Name) + }() + succesfulRuleCount := func() { + // rules applied succesfully count + rulesAppliedCount++ + } + ris := []info.RuleInfo{} for _, rule := range policy.Spec.Rules { if rule.Generation == (kyverno.Generation{}) { @@ -30,6 +45,7 @@ func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unst } else { ri.Addf("Generation succesfully.", rule.Name) glog.Infof("succesfully applied policy %s rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName()) + succesfulRuleCount() } ris = append(ris, ri) } diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 392913ee61..a23caa706c 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -2,6 +2,7 @@ package engine import ( "reflect" + "time" "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" @@ -12,6 +13,20 @@ import ( // Mutate performs mutation. Overlay first and then mutation patches //TODO: check if gvk needs to be passed or can be set in resource func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte, []info.RuleInfo) { + var executionTime time.Duration + var rulesAppliedCount int + startTime := time.Now() + glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime) + defer func() { + executionTime = time.Since(startTime) + glog.V(4).Infof("Finished applying mutation rules policy %q (%v)", policy.Name, executionTime) + glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", rulesAppliedCount, policy.Name) + }() + succesfulRuleCount := func() { + // rules applied succesfully count + rulesAppliedCount++ + } + //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information var patches [][]byte var ruleInfos []info.RuleInfo @@ -46,12 +61,12 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) ruleInfo.Add("Overlay succesfully applied") - // update rule information // strip slashes from string patch := JoinPatches(oPatches) ruleInfo.Changes = string(patch) patches = append(patches, oPatches...) + succesfulRuleCount() } else { glog.V(4).Infof("failed to apply overlay: %v", err) ruleInfo.Fail() @@ -72,6 +87,7 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) ruleInfo.Addf("Patches succesfully applied.") patches = append(patches, jsonPatches...) + succesfulRuleCount() } } ruleInfos = append(ruleInfos, ruleInfo) diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 665c986feb..9e65c6e43d 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -8,6 +8,7 @@ import ( "reflect" "strconv" "strings" + "time" "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" @@ -18,6 +19,20 @@ import ( // Validate handles validating admission request // Checks the target resources for rules defined in the policy func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) { + var executionTime time.Duration + var rulesAppliedCount int + startTime := time.Now() + glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime) + defer func() { + executionTime = time.Since(startTime) + glog.V(4).Infof("Finished applying validation rules policy %q (%v)", policy.Name, executionTime) + glog.V(4).Infof("Validation Rules appplied succesfully count %q for policy %q", rulesAppliedCount, policy.Name) + }() + succesfulRuleCount := func() { + // rules applied succesfully count + rulesAppliedCount++ + } + //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information //TODO: pass unstructured instead of rawResource ? @@ -57,6 +72,7 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info } else { ruleInfo.Add("Pattern succesfully validated") glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) + succesfulRuleCount() } ruleInfos = append(ruleInfos, ruleInfo) } diff --git a/pkg/policy/status.go b/pkg/policy/status.go new file mode 100644 index 0000000000..7a49726b98 --- /dev/null +++ b/pkg/policy/status.go @@ -0,0 +1,14 @@ +package policy + +import "time" + +type PolicyStatus struct { + // average time required to process the policy rules on a resource + avgExecutionTime time.Duration + // Count of rules that were applied succesfully + rulesAppliedCount int + // Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules + resourcesBlockedCount int + // Count of the resource for whom the mutation rules were applied succesfully + resourcesMutatedCount int +} diff --git a/pkg/policyviolation/controller.go b/pkg/policyviolation/controller.go index 045f59b857..53c34b2cf8 100644 --- a/pkg/policyviolation/controller.go +++ b/pkg/policyviolation/controller.go @@ -238,13 +238,13 @@ func (pvc *PolicyViolationController) syncActiveResource(curPv *kyverno.PolicyVi return err } glog.V(4).Infof("removing policy violation %s as the corresponding resource %s/%s/%s does not exist anymore", curPv.Name, rspec.Kind, rspec.Namespace, rspec.Name) + return nil } if err != nil { glog.V(4).Infof("error while retrieved resource %s/%s/%s: %v", rspec.Kind, rspec.Namespace, rspec.Name, err) return err } //TODO- if the policy is not present, remove the policy violation - return nil } From 8676aef6f1ac6b7161940d6a4818227bcfaabcd8 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 19 Aug 2019 16:43:15 -0700 Subject: [PATCH 41/66] add listers --- .../kyverno/v1alpha1/expansion_generated.go | 101 ++++++++++++++++++ pkg/client/listers/kyverno/v1alpha1/policy.go | 65 +++++++++++ .../kyverno/v1alpha1/policyviolation.go | 65 +++++++++++ 3 files changed, 231 insertions(+) create mode 100644 pkg/client/listers/kyverno/v1alpha1/expansion_generated.go create mode 100644 pkg/client/listers/kyverno/v1alpha1/policy.go create mode 100644 pkg/client/listers/kyverno/v1alpha1/policyviolation.go diff --git a/pkg/client/listers/kyverno/v1alpha1/expansion_generated.go b/pkg/client/listers/kyverno/v1alpha1/expansion_generated.go new file mode 100644 index 0000000000..f37f2e4f76 --- /dev/null +++ b/pkg/client/listers/kyverno/v1alpha1/expansion_generated.go @@ -0,0 +1,101 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "fmt" + + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" +) + +// PolicyListerExpansion allows custom methods to be added to +// PolicyLister. +type PolicyListerExpansion interface { + GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) + ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error) +} + +// PolicyViolationListerExpansion allows custom methods to be added to +// PolicyViolationLister. +type PolicyViolationListerExpansion interface { + // List lists all PolicyViolations in the indexer with GVK. + // List lists all PolicyViolations in the indexer with GVK. + ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) +} + +//ListResources is a wrapper to List and adds the resource kind information +// as the lister is specific to a gvk we can harcode the values here +func (pvl *policyViolationLister) ListResources(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) { + policyviolations, err := pvl.List(selector) + for index := range policyviolations { + policyviolations[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("PolicyViolation")) + } + return policyviolations, nil +} + +//ListResources is a wrapper to List and adds the resource kind information +// as the lister is specific to a gvk we can harcode the values here +func (pl *policyLister) ListResources(selector labels.Selector) (ret []*v1alpha1.Policy, err error) { + policies, err := pl.List(selector) + for index := range policies { + policies[index].SetGroupVersionKind(kyverno.SchemeGroupVersion.WithKind("Policy")) + } + return policies, err +} + +func (pl *policyLister) GetPolicyForPolicyViolation(pv *kyverno.PolicyViolation) ([]*kyverno.Policy, error) { + if len(pv.Labels) == 0 { + return nil, fmt.Errorf("no Policy found for PolicyViolation %v because it has no labels", pv.Name) + } + + pList, err := pl.List(labels.Everything()) + if err != nil { + return nil, err + } + + var policies []*kyverno.Policy + for _, p := range pList { + policyLabelmap := map[string]string{"policy": p.Name} + + ls := &metav1.LabelSelector{} + err = metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&policyLabelmap, ls, nil) + if err != nil { + return nil, fmt.Errorf("failed to generate label sector of Policy name %s: %v", p.Name, err) + } + selector, err := metav1.LabelSelectorAsSelector(ls) + if err != nil { + return nil, fmt.Errorf("invalid label selector: %v", err) + } + // If a policy with a nil or empty selector creeps in, it should match nothing, not everything. + if selector.Empty() || !selector.Matches(labels.Set(pv.Labels)) { + continue + } + policies = append(policies, p) + } + + if len(policies) == 0 { + return nil, fmt.Errorf("could not find Policy set for PolicyViolation %s with labels: %v", pv.Name, pv.Labels) + } + + return policies, nil + +} diff --git a/pkg/client/listers/kyverno/v1alpha1/policy.go b/pkg/client/listers/kyverno/v1alpha1/policy.go new file mode 100644 index 0000000000..c981855c33 --- /dev/null +++ b/pkg/client/listers/kyverno/v1alpha1/policy.go @@ -0,0 +1,65 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PolicyLister helps list Policies. +type PolicyLister interface { + // List lists all Policies in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) + // Get retrieves the Policy from the index for a given name. + Get(name string) (*v1alpha1.Policy, error) + PolicyListerExpansion +} + +// policyLister implements the PolicyLister interface. +type policyLister struct { + indexer cache.Indexer +} + +// NewPolicyLister returns a new PolicyLister. +func NewPolicyLister(indexer cache.Indexer) PolicyLister { + return &policyLister{indexer: indexer} +} + +// List lists all Policies in the indexer. +func (s *policyLister) List(selector labels.Selector) (ret []*v1alpha1.Policy, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Policy)) + }) + return ret, err +} + +// Get retrieves the Policy from the index for a given name. +func (s *policyLister) Get(name string) (*v1alpha1.Policy, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("policy"), name) + } + return obj.(*v1alpha1.Policy), nil +} diff --git a/pkg/client/listers/kyverno/v1alpha1/policyviolation.go b/pkg/client/listers/kyverno/v1alpha1/policyviolation.go new file mode 100644 index 0000000000..e93ec95228 --- /dev/null +++ b/pkg/client/listers/kyverno/v1alpha1/policyviolation.go @@ -0,0 +1,65 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PolicyViolationLister helps list PolicyViolations. +type PolicyViolationLister interface { + // List lists all PolicyViolations in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) + // Get retrieves the PolicyViolation from the index for a given name. + Get(name string) (*v1alpha1.PolicyViolation, error) + PolicyViolationListerExpansion +} + +// policyViolationLister implements the PolicyViolationLister interface. +type policyViolationLister struct { + indexer cache.Indexer +} + +// NewPolicyViolationLister returns a new PolicyViolationLister. +func NewPolicyViolationLister(indexer cache.Indexer) PolicyViolationLister { + return &policyViolationLister{indexer: indexer} +} + +// List lists all PolicyViolations in the indexer. +func (s *policyViolationLister) List(selector labels.Selector) (ret []*v1alpha1.PolicyViolation, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.PolicyViolation)) + }) + return ret, err +} + +// Get retrieves the PolicyViolation from the index for a given name. +func (s *policyViolationLister) Get(name string) (*v1alpha1.PolicyViolation, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("policyviolation"), name) + } + return obj.(*v1alpha1.PolicyViolation), nil +} From 742f8083ad08f29673c2e42a46d927fd0394e617 Mon Sep 17 00:00:00 2001 From: Shivkumar Dudhani Date: Mon, 19 Aug 2019 17:06:05 -0700 Subject: [PATCH 42/66] file removed in clean up --- pkg/annotations/annotations.go | 401 --------------------------------- 1 file changed, 401 deletions(-) delete mode 100644 pkg/annotations/annotations.go diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go deleted file mode 100644 index 46725b8cc2..0000000000 --- a/pkg/annotations/annotations.go +++ /dev/null @@ -1,401 +0,0 @@ -package annotations - -import ( - "encoding/json" - "reflect" - - "github.com/golang/glog" - pinfo "github.com/nirmata/kyverno/pkg/info" - - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" -) - -//Policy information for annotations -type Policy struct { - Status string `json:"status"` - // Key Type/Name - MutationRules map[string]Rule `json:"mutationrules,omitempty"` - ValidationRules map[string]Rule `json:"validationrules,omitempty"` - GenerationRules map[string]Rule `json:"generationrules,omitempty"` -} - -//Rule information for annotations -type Rule struct { - Status string `json:"status"` - Changes string `json:"changes,omitempty"` // TODO for mutation changes - Message string `json:"message,omitempty"` -} - -func (p *Policy) getOverAllStatus() string { - // mutation - for _, v := range p.MutationRules { - if v.Status == "Failure" { - return "Failure" - } - } - // validation - for _, v := range p.ValidationRules { - if v.Status == "Failure" { - return "Failure" - } - } - // generation - for _, v := range p.GenerationRules { - if v.Status == "Failure" { - return "Failure" - } - } - return "Success" -} - -func getRules(rules []*pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule { - if len(rules) == 0 { - return nil - } - annrules := make(map[string]Rule, 0) - // var annrules map[string]Rule - for _, r := range rules { - if r.RuleType != ruleType { - continue - } - - rule := Rule{Status: getStatus(r.IsSuccessful())} - if !r.IsSuccessful() { - rule.Message = r.GetErrorString() - } else { - if ruleType == pinfo.Mutation { - // If ruleType is Mutation - // then for succesful mutation we store the json patch being applied in the annotation information - rule.Changes = r.Changes - } - } - annrules[r.Name] = rule - } - return annrules -} - -func (p *Policy) updatePolicy(obj *Policy, ruleType pinfo.RuleType) bool { - updates := false - // Check Mutation rules - switch ruleType { - case pinfo.All: - if p.compareMutationRules(obj.MutationRules) { - updates = true - } - if p.compareValidationRules(obj.ValidationRules) { - updates = true - } - if p.compareGenerationRules(obj.GenerationRules) { - updates = true - } - case pinfo.Mutation: - if p.compareMutationRules(obj.MutationRules) { - updates = true - } - case pinfo.Validation: - if p.compareValidationRules(obj.ValidationRules) { - updates = true - } - case pinfo.Generation: - if p.compareGenerationRules(obj.GenerationRules) { - updates = true - } - if p.Status != obj.Status { - updates = true - } - } - // check if any rules failed - p.Status = p.getOverAllStatus() - // If there are any updates then the annotation can be updated, can skip - return updates -} - -func (p *Policy) compareMutationRules(rules map[string]Rule) bool { - // check if the rules have changed - if !reflect.DeepEqual(p.MutationRules, rules) { - p.MutationRules = rules - return true - } - return false -} - -func (p *Policy) compareValidationRules(rules map[string]Rule) bool { - // check if the rules have changed - if !reflect.DeepEqual(p.ValidationRules, rules) { - p.ValidationRules = rules - return true - } - return false -} - -func (p *Policy) compareGenerationRules(rules map[string]Rule) bool { - // check if the rules have changed - if !reflect.DeepEqual(p.GenerationRules, rules) { - p.GenerationRules = rules - return true - } - return false -} - -func newAnnotationForPolicy(pi *pinfo.PolicyInfo) *Policy { - return &Policy{Status: getStatus(pi.IsSuccessful()), - MutationRules: getRules(pi.Rules, pinfo.Mutation), - ValidationRules: getRules(pi.Rules, pinfo.Validation), - GenerationRules: getRules(pi.Rules, pinfo.Generation), - } -} - -//AddPolicy will add policy annotation if not present or update if present -// modifies obj -// returns true, if there is any update -> caller need to update the obj -// returns false, if there is no change -> caller can skip the update -func AddPolicy(obj *unstructured.Unstructured, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) bool { - PolicyObj := newAnnotationForPolicy(pi) - // get annotation - ann := obj.GetAnnotations() - // check if policy already has annotation - cPolicy, ok := ann[BuildKey(pi.Name)] - if !ok { - PolicyByte, err := json.Marshal(PolicyObj) - if err != nil { - glog.Error(err) - return false - } - // insert policy information - ann[BuildKey(pi.Name)] = string(PolicyByte) - // set annotation back to unstr - obj.SetAnnotations(ann) - return true - } - cPolicyObj := Policy{} - err := json.Unmarshal([]byte(cPolicy), &cPolicyObj) - if err != nil { - return false - } - // update policy information inside the annotation - // 1> policy status - // 2> Mutation, Validation, Generation - if cPolicyObj.updatePolicy(PolicyObj, ruleType) { - - cPolicyByte, err := json.Marshal(cPolicyObj) - if err != nil { - return false - } - // update policy information - ann[BuildKey(pi.Name)] = string(cPolicyByte) - // set annotation back to unstr - obj.SetAnnotations(ann) - return true - } - return false -} - -//RemovePolicy to remove annotations -// return true -> if there was an entry and we deleted it -// return false -> if there is no entry, caller need not update -func RemovePolicy(obj *unstructured.Unstructured, policy string) bool { - // get annotations - ann := obj.GetAnnotations() - if ann == nil { - return false - } - if _, ok := ann[BuildKey(policy)]; !ok { - return false - } - delete(ann, BuildKey(policy)) - // set annotation back to unstr - obj.SetAnnotations(ann) - return true -} - -//ParseAnnotationsFromObject extracts annotations from the JSON obj -func ParseAnnotationsFromObject(bytes []byte) map[string]string { - var objectJSON map[string]interface{} - json.Unmarshal(bytes, &objectJSON) - meta, ok := objectJSON["metadata"].(map[string]interface{}) - if !ok { - glog.Error("unable to parse") - return nil - } - ann, ok, err := unstructured.NestedStringMap(meta, "annotations") - if err != nil || !ok { - return nil - } - return ann -} - -func PatchAnnotations(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) ([]byte, error) { - if ruleType != pinfo.All && !pi.ContainsRuleType(ruleType) { - // the rule was not proceesed in the current policy application - return nil, nil - } - // transform the PolicyInfo to anotation struct - policyObj := newAnnotationForPolicy(pi) - if ann == nil { - ann = make(map[string]string, 0) - policyByte, err := json.Marshal(policyObj) - if err != nil { - return nil, err - } - // create a json patch to add annotation object - ann[BuildKeyString(pi.Name)] = string(policyByte) - // patch adds the annotation map with the policy information - jsonPatch, err := createAddJSONPatchMap(ann) - return jsonPatch, err - } - // if the annotations map already exists then we need to update it by adding a patch to the field inside the annotation - cPolicy, ok := ann[BuildKey(pi.Name)] - if !ok { - // annotations does not contain the policy - policyByte, err := json.Marshal(policyObj) - if err != nil { - return nil, err - } - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(policyByte)) - return jsonPatch, err - } - // an annotaion exists for the policy, we need to update the information if anything has changed - cPolicyObj := Policy{} - err := json.Unmarshal([]byte(cPolicy), &cPolicyObj) - if err != nil { - // error while unmarshallign the content - return nil, err - } - // update policy information inside the annotation - // 1> policy status - // 2> rule (name, status,changes,type) - update := cPolicyObj.updatePolicy(policyObj, ruleType) - if !update { - // there is not update, so we dont - return nil, nil - } - policyByte, err := json.Marshal(cPolicyObj) - if err != nil { - return nil, err - } - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(policyByte)) - return jsonPatch, err -} - -//AddPolicyJSONPatch generate JSON Patch to add policy informatino JSON patch -func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) (map[string]string, []byte, error) { - if !pi.ContainsRuleType(ruleType) { - return nil, nil, nil - } - PolicyObj := newAnnotationForPolicy(pi) - if ann == nil { - ann = make(map[string]string, 0) - PolicyByte, err := json.Marshal(PolicyObj) - if err != nil { - return nil, nil, err - } - // create a json patch to add annotation object - ann[BuildKey(pi.Name)] = string(PolicyByte) - // create add JSON patch - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(PolicyByte)) - return ann, jsonPatch, err - } - // if the annotations map is present then we - cPolicy, ok := ann[BuildKey(pi.Name)] - if !ok { - PolicyByte, err := json.Marshal(PolicyObj) - if err != nil { - return nil, nil, err - } - // insert policy information - ann[BuildKey(pi.Name)] = string(PolicyByte) - // create add JSON patch - jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(PolicyByte)) - - return ann, jsonPatch, err - } - cPolicyObj := Policy{} - err := json.Unmarshal([]byte(cPolicy), &cPolicyObj) - // update policy information inside the annotation - // 1> policy status - // 2> rule (name, status,changes,type) - update := cPolicyObj.updatePolicy(PolicyObj, ruleType) - if !update { - return nil, nil, err - } - - cPolicyByte, err := json.Marshal(cPolicyObj) - if err != nil { - return nil, nil, err - } - // update policy information - ann[BuildKey(pi.Name)] = string(cPolicyByte) - // create update JSON patch - jsonPatch, err := createReplaceJSONPatch(BuildKey(pi.Name), string(cPolicyByte)) - return ann, jsonPatch, err -} - -//RemovePolicyJSONPatch remove JSON patch -func RemovePolicyJSONPatch(ann map[string]string, policy string) (map[string]string, []byte, error) { - if ann == nil { - return nil, nil, nil - } - jsonPatch, err := createRemoveJSONPatchKey(policy) - return ann, jsonPatch, err -} - -type patchMapValue struct { - Op string `json:"op"` - Path string `json:"path"` - Value map[string]string `json:"value"` -} - -type patchStringValue struct { - Op string `json:"op"` - Path string `json:"path"` - Value string `json:"value"` -} - -func createRemoveJSONPatchMap() ([]byte, error) { - payload := []patchMapValue{{ - Op: "remove", - Path: "/metadata/annotations", - }} - return json.Marshal(payload) - -} -func createAddJSONPatchMap(ann map[string]string) ([]byte, error) { - - payload := []patchMapValue{{ - Op: "add", - Path: "/metadata/annotations", - Value: ann, - }} - return json.Marshal(payload) -} - -func createAddJSONPatch(key, value string) ([]byte, error) { - - payload := []patchStringValue{{ - Op: "add", - Path: "/metadata/annotations/" + key, - Value: value, - }} - return json.Marshal(payload) -} - -func createReplaceJSONPatch(key, value string) ([]byte, error) { - // if ann == nil { - // ann = make(map[string]string, 0) - // } - payload := []patchStringValue{{ - Op: "replace", - Path: "/metadata/annotations/" + key, - Value: value, - }} - return json.Marshal(payload) -} - -func createRemoveJSONPatchKey(key string) ([]byte, error) { - payload := []patchStringValue{{ - Op: "remove", - Path: "/metadata/annotations/" + key, - }} - return json.Marshal(payload) - -} From a1692b50be05aefdde65122b25ec7ab0ec7f4368 Mon Sep 17 00:00:00 2001 From: Shivkumar Dudhani Date: Mon, 19 Aug 2019 17:07:48 -0700 Subject: [PATCH 43/66] remove in cleanup --- pkg/annotations/controller.go | 108 ---------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 pkg/annotations/controller.go diff --git a/pkg/annotations/controller.go b/pkg/annotations/controller.go deleted file mode 100644 index e251719492..0000000000 --- a/pkg/annotations/controller.go +++ /dev/null @@ -1,108 +0,0 @@ -package annotations - -import ( - "fmt" - "time" - - "github.com/golang/glog" - client "github.com/nirmata/kyverno/pkg/dclient" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/util/workqueue" -) - -type controller struct { - client *client.Client - queue workqueue.RateLimitingInterface -} - -type Interface interface { - Add(rkind, rns, rname string, patch []byte) -} - -type Controller interface { - Interface - Run(stopCh <-chan struct{}) - Stop() -} - -func NewAnnotationControler(client *client.Client) Controller { - return &controller{ - client: client, - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), annotationQueueName), - } -} - -func (c *controller) Add(rkind, rns, rname string, patch []byte) { - c.queue.Add(newInfo(rkind, rns, rname, &patch)) -} - -func (c *controller) Run(stopCh <-chan struct{}) { - defer utilruntime.HandleCrash() - for i := 0; i < workerThreadCount; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - glog.Info("Started annotation controller workers") -} - -func (c *controller) Stop() { - c.queue.ShutDown() - glog.Info("Shutting down annotation controller workers") -} - -func (c *controller) runWorker() { - for c.processNextWorkItem() { - } -} - -func (pc *controller) processNextWorkItem() bool { - obj, shutdown := pc.queue.Get() - if shutdown { - return false - } - - err := func(obj interface{}) error { - defer pc.queue.Done(obj) - err := pc.syncHandler(obj) - pc.handleErr(err, obj) - return nil - }(obj) - if err != nil { - glog.Error(err) - return true - } - return true -} -func (pc *controller) handleErr(err error, key interface{}) { - if err == nil { - pc.queue.Forget(key) - return - } - // This controller retries if something goes wrong. After that, it stops trying. - if pc.queue.NumRequeues(key) < workQueueRetryLimit { - glog.Warningf("Error syncing events %v: %v", key, err) - // Re-enqueue the key rate limited. Based on the rate limiter on the - // queue and the re-enqueue history, the key will be processed later again. - pc.queue.AddRateLimited(key) - return - } - pc.queue.Forget(key) - glog.Error(err) - glog.Warningf("Dropping the key out of the queue: %v", err) -} - -func (c *controller) syncHandler(obj interface{}) error { - var key info - var ok bool - if key, ok = obj.(info); !ok { - return fmt.Errorf("expected string in workqueue but got %#v", obj) - } - - var err error - _, err = c.client.PatchResource(key.RKind, key.RNs, key.RName, *key.patch) - if err != nil { - glog.Errorf("Error creating annotation: unable to get resource %s/%s/%s, will retry: %s", key.RKind, key.RNs, key.RName, err) - return err - } - return nil -} From 606c519789fafd790b02de23c0b51f94e3f36b43 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 19 Aug 2019 17:17:52 -0700 Subject: [PATCH 44/66] clean up --- pkg/controller/controller.go | 281 ------------------------------ pkg/controller/controller_test.go | 147 ---------------- pkg/gencontroller/controller.go | 160 ----------------- pkg/gencontroller/generation.go | 155 ---------------- pkg/webhooks/server.go | 6 +- 5 files changed, 3 insertions(+), 746 deletions(-) delete mode 100644 pkg/controller/controller.go delete mode 100644 pkg/controller/controller_test.go delete mode 100644 pkg/gencontroller/controller.go delete mode 100644 pkg/gencontroller/generation.go diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go deleted file mode 100644 index 40b5e283c8..0000000000 --- a/pkg/controller/controller.go +++ /dev/null @@ -1,281 +0,0 @@ -package controller - -import ( - "fmt" - "reflect" - "time" - - "github.com/nirmata/kyverno/pkg/annotations" - "github.com/nirmata/kyverno/pkg/info" - "github.com/nirmata/kyverno/pkg/utils" - - "github.com/nirmata/kyverno/pkg/engine" - - "github.com/golang/glog" - v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" - "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/sharedinformer" - violation "github.com/nirmata/kyverno/pkg/violation" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -//PolicyController to manage Policy CRD -type PolicyController struct { - client *client.Client - policyLister lister.PolicyLister - policySynced cache.InformerSynced - violationBuilder violation.Generator - eventController event.Generator - queue workqueue.RateLimitingInterface - filterK8Resources []utils.K8Resource -} - -// NewPolicyController from cmd args -func NewPolicyController(client *client.Client, - policyInformer sharedinformer.PolicyInformer, - violationBuilder violation.Generator, - eventController event.Generator, - filterK8Resources string) *PolicyController { - - controller := &PolicyController{ - client: client, - policyLister: policyInformer.GetLister(), - policySynced: policyInformer.GetInfomer().HasSynced, - violationBuilder: violationBuilder, - eventController: eventController, - filterK8Resources: utils.ParseKinds(filterK8Resources), - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), policyWorkQueueName), - } - - policyInformer.GetInfomer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.createPolicyHandler, - UpdateFunc: controller.updatePolicyHandler, - DeleteFunc: controller.deletePolicyHandler, - }) - return controller -} - -func (pc *PolicyController) createPolicyHandler(resource interface{}) { - pc.enqueuePolicy(resource) -} - -func (pc *PolicyController) updatePolicyHandler(oldResource, newResource interface{}) { - newPolicy := newResource.(*v1alpha1.Policy) - oldPolicy := oldResource.(*v1alpha1.Policy) - newPolicy.Status = v1alpha1.Status{} - oldPolicy.Status = v1alpha1.Status{} - newPolicy.ResourceVersion = "" - oldPolicy.ResourceVersion = "" - if reflect.DeepEqual(newPolicy, oldPolicy) { - return - } - pc.enqueuePolicy(newResource) -} - -func (pc *PolicyController) deletePolicyHandler(resource interface{}) { - var object metav1.Object - var ok bool - if object, ok = resource.(metav1.Object); !ok { - glog.Error("error decoding object, invalid type") - return - } - cleanAnnotations(pc.client, resource, pc.filterK8Resources) - glog.Infof("policy deleted: %s", object.GetName()) -} - -func (pc *PolicyController) enqueuePolicy(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - glog.Error(err) - return - } - pc.queue.Add(key) -} - -// Run is main controller thread -func (pc *PolicyController) Run(stopCh <-chan struct{}) error { - defer utilruntime.HandleCrash() - - if ok := cache.WaitForCacheSync(stopCh, pc.policySynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - for i := 0; i < policyControllerWorkerCount; i++ { - go wait.Until(pc.runWorker, time.Second, stopCh) - } - glog.Info("started policy controller workers") - - return nil -} - -//Stop to perform actions when controller is stopped -func (pc *PolicyController) Stop() { - pc.queue.ShutDown() - glog.Info("shutting down policy controller workers") -} - -func (pc *PolicyController) runWorker() { - for pc.processNextWorkItem() { - } -} - -func (pc *PolicyController) processNextWorkItem() bool { - obj, shutdown := pc.queue.Get() - if shutdown { - return false - } - - err := func(obj interface{}) error { - defer pc.queue.Done(obj) - err := pc.syncHandler(obj) - pc.handleErr(err, obj) - return nil - }(obj) - if err != nil { - glog.Error(err) - return true - } - return true -} - -func (pc *PolicyController) handleErr(err error, key interface{}) { - if err == nil { - pc.queue.Forget(key) - return - } - // This controller retries if something goes wrong. After that, it stops trying. - if pc.queue.NumRequeues(key) < policyWorkQueueRetryLimit { - glog.Warningf("Error syncing events %v: %v", key, err) - // Re-enqueue the key rate limited. Based on the rate limiter on the - // queue and the re-enqueue history, the key will be processed later again. - pc.queue.AddRateLimited(key) - return - } - pc.queue.Forget(key) - glog.Error(err) - glog.Warningf("Dropping the key out of the queue: %v", err) -} - -func (pc *PolicyController) syncHandler(obj interface{}) error { - var key string - var ok bool - if key, ok = obj.(string); !ok { - return fmt.Errorf("expected string in workqueue but got %#v", obj) - } - _, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - glog.Errorf("invalid policy key: %s", key) - return nil - } - // Get Policy - policy, err := pc.policyLister.Get(name) - if err != nil { - if errors.IsNotFound(err) { - glog.Errorf("policy '%s' in work queue no longer exists", key) - return nil - } - return err - } - - glog.Infof("process policy %s on existing resources", policy.GetName()) - // Process policy on existing resources - policyInfos := engine.ProcessExisting(pc.client, policy, pc.filterK8Resources) - - events, violations := pc.createEventsAndViolations(policyInfos) - // Events, Violations - pc.eventController.Add(events...) - err = pc.violationBuilder.Add(violations...) - if err != nil { - glog.Error(err) - } - - // Annotations - pc.createAnnotations(policyInfos) - - return nil -} - -func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) { - for _, pi := range policyInfos { - //get resource - obj, err := pc.client.GetResource(pi.RKind, pi.RNamespace, pi.RName) - if err != nil { - glog.Error(err) - continue - } - // add annotation for policy application - ann := obj.GetAnnotations() - // if annotations are nil then create a map and patch - // else - // add the exact patch - patch, err := annotations.PatchAnnotations(ann, pi, info.All) - if patch == nil { - /// nothing to patch - return - } - _, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch) - if err != nil { - glog.Error(err) - continue - } - } -} - -func (pc *PolicyController) createEventsAndViolations(policyInfos []*info.PolicyInfo) ([]*event.Info, []*violation.Info) { - events := []*event.Info{} - violations := []*violation.Info{} - // Create events from the policyInfo - for _, policyInfo := range policyInfos { - frules := []v1alpha1.FailedRule{} - sruleNames := []string{} - - for _, rule := range policyInfo.Rules { - if !rule.IsSuccessful() { - e := &event.Info{} - frule := v1alpha1.FailedRule{Name: rule.Name} - switch rule.RuleType { - case info.Mutation, info.Validation, info.Generation: - // Events - e = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation, event.FProcessRule, rule.Name, policyInfo.Name) - switch rule.RuleType { - case info.Mutation: - frule.Type = info.Mutation.String() - case info.Validation: - frule.Type = info.Validation.String() - case info.Generation: - frule.Type = info.Generation.String() - } - frule.Error = rule.GetErrorString() - default: - glog.Info("Unsupported Rule type") - } - frule.Error = rule.GetErrorString() - frules = append(frules, frule) - events = append(events, e) - } else { - sruleNames = append(sruleNames, rule.Name) - } - } - - if !policyInfo.IsSuccessful() { - e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyViolation, event.FResourcePolcy, policyInfo.RNamespace+"/"+policyInfo.RName, concatFailedRules(frules)) - events = append(events, e) - // Violation - v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.GetFailedRules()) - violations = append(violations, v) - } else { - // clean up violations - pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Mutation) - pc.violationBuilder.RemoveInactiveViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, info.Validation) - } - } - return events, violations -} diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go deleted file mode 100644 index 1adace6946..0000000000 --- a/pkg/controller/controller_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package controller - -import ( - "testing" - - "github.com/golang/glog" - types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" - client "github.com/nirmata/kyverno/pkg/dclient" - event "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/sharedinformer" - violation "github.com/nirmata/kyverno/pkg/violation" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/sample-controller/pkg/signals" -) - -func TestCreatePolicy(t *testing.T) { - f := newFixture(t) - // new policy is added to policy lister and explictly passed to sync-handler - // to process the existing - policy := newPolicy("test-policy") - f.policyLister = append(f.policyLister, policy) - f.objects = append(f.objects, policy) - // run controller - f.runControler("test-policy") -} - -func (f *fixture) runControler(policyName string) { - policyInformerFactory, err := sharedinformer.NewFakeSharedInformerFactory() - if err != nil { - f.t.Fatal(err) - } - - eventController := event.NewEventController(f.Client, policyInformerFactory) - violationBuilder := violation.NewPolicyViolationBuilder(f.Client, policyInformerFactory, eventController) - - // new controller - policyController := NewPolicyController( - f.Client, - policyInformerFactory, - violationBuilder, - eventController, - "") - - stopCh := signals.SetupSignalHandler() - // start informer & controller - policyInformerFactory.Run(stopCh) - if err = policyController.Run(stopCh); err != nil { - glog.Fatalf("Error running PolicyController: %v\n", err) - } - // add policy to the informer - for _, p := range f.policyLister { - policyInformerFactory.GetInfomer().GetIndexer().Add(p) - } - - // sync handler - // reads the policy from the policy lister and processes them - err = policyController.syncHandler(policyName) - if err != nil { - f.t.Fatal(err) - } - policyController.Stop() - -} - -type fixture struct { - t *testing.T - Client *client.Client - policyLister []*types.Policy - objects []runtime.Object -} - -func newFixture(t *testing.T) *fixture { - - // init groupversion - regResource := []schema.GroupVersionResource{ - schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"}, - schema.GroupVersionResource{Group: "group2", Version: "version", Resource: "thekinds"}, - schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}, - schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}, - } - - objects := []runtime.Object{newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), - newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"), - newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"), - newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"), - newUnstructured("group2/version", "TheKind", "ns-foo", "name2-baz"), - newUnstructured("apps/v1", "Deployment", "kyverno", "kyverno"), - } - - scheme := runtime.NewScheme() - // Create mock client - fclient, err := client.NewMockClient(scheme, objects...) - if err != nil { - t.Fatal(err) - } - - // set discovery Client - fclient.SetDiscovery(client.NewFakeDiscoveryClient(regResource)) - - f := &fixture{ - t: t, - Client: fclient, - } - - return f -} - -// create mock client with initial resouces -// set registered resources for gvr -func (f *fixture) setupFixture() { - scheme := runtime.NewScheme() - fclient, err := client.NewMockClient(scheme, f.objects...) - if err != nil { - f.t.Fatal(err) - } - - regresource := []schema.GroupVersionResource{ - schema.GroupVersionResource{Group: "kyverno.io", - Version: "v1alpha1", - Resource: "policys"}} - fclient.SetDiscovery(client.NewFakeDiscoveryClient(regresource)) -} - -func newPolicy(name string) *types.Policy { - return &types.Policy{ - TypeMeta: metav1.TypeMeta{APIVersion: types.SchemeGroupVersion.String()}, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - } -} - -func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured { - return &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": apiVersion, - "kind": kind, - "metadata": map[string]interface{}{ - "namespace": namespace, - "name": name, - }, - }, - } -} diff --git a/pkg/gencontroller/controller.go b/pkg/gencontroller/controller.go deleted file mode 100644 index 78233b3850..0000000000 --- a/pkg/gencontroller/controller.go +++ /dev/null @@ -1,160 +0,0 @@ -package gencontroller - -import ( - "fmt" - "time" - - "k8s.io/apimachinery/pkg/util/wait" - - "github.com/golang/glog" - client "github.com/nirmata/kyverno/pkg/dclient" - "github.com/nirmata/kyverno/pkg/event" - "k8s.io/apimachinery/pkg/api/errors" - - v1Informer "k8s.io/client-go/informers/core/v1" - v1CoreLister "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/workqueue" -) - -//Controller watches the 'Namespace' resource creation/update and applied the generation rules on them -type Controller struct { - client *client.Client - namespaceLister v1CoreLister.NamespaceLister - namespaceSynced cache.InformerSynced - policyLister policyLister.PolicyLister - eventController event.Generator - violationBuilder violation.Generator - annotationsController annotations.Controller - workqueue workqueue.RateLimitingInterface -} - -//NewGenController returns a new Controller to manage generation rules -func NewGenController(client *client.Client, - eventController event.Generator, - policyInformer policySharedInformer.PolicyInformer, - violationBuilder violation.Generator, - namespaceInformer v1Informer.NamespaceInformer, - annotationsController annotations.Controller) *Controller { - - // create the controller - controller := &Controller{ - client: client, - namespaceLister: namespaceInformer.Lister(), - namespaceSynced: namespaceInformer.Informer().HasSynced, - policyLister: policyInformer.GetLister(), - eventController: eventController, - violationBuilder: violationBuilder, - annotationsController: annotationsController, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), wqNamespace), - } - namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.createNamespaceHandler, - UpdateFunc: controller.updateNamespaceHandler, - }) - - return controller -} -func (c *Controller) createNamespaceHandler(resource interface{}) { - c.enqueueNamespace(resource) -} - -func (c *Controller) updateNamespaceHandler(oldResoruce, newResource interface{}) { - // DO we need to anything if the namespace is modified ? -} - -func (c *Controller) enqueueNamespace(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - glog.Error(err) - return - } - c.workqueue.Add(key) -} - -//Run to run the controller -func (c *Controller) Run(stopCh <-chan struct{}) error { - - if ok := cache.WaitForCacheSync(stopCh, c.namespaceSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - for i := 0; i < workerCount; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - glog.Info("started namespace controller workers") - return nil -} - -//Stop to stop the controller -func (c *Controller) Stop() { - c.workqueue.ShutDown() - glog.Info("shutting down namespace controller workers") -} - -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - if shutdown { - return false - } - err := func(obj interface{}) error { - defer c.workqueue.Done(obj) - err := c.syncHandler(obj) - c.handleErr(err, obj) - return nil - }(obj) - if err != nil { - glog.Error(err) - return true - } - return true -} - -func (c *Controller) handleErr(err error, key interface{}) { - if err == nil { - c.workqueue.Forget(key) - return - } - if c.workqueue.NumRequeues(key) < wqRetryLimit { - glog.Warningf("Error syncing events %v: %v", key, err) - c.workqueue.AddRateLimited(key) - return - } - c.workqueue.Forget(key) - glog.Error(err) - glog.Warningf("Dropping the key %q out of the queue: %v", key, err) -} - -func (c *Controller) syncHandler(obj interface{}) error { - var key string - var ok bool - if key, ok = obj.(string); !ok { - return fmt.Errorf("expected string in workqueue but got %v", obj) - } - // Namespace is cluster wide resource - _, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - glog.Errorf("invalid namespace key: %s", key) - return err - } - // Get Namespace - ns, err := c.namespaceLister.Get(name) - if err != nil { - if errors.IsNotFound(err) { - glog.Errorf("namespace '%s' in work queue no longer exists", key) - return nil - } - } - - //TODO: need to find a way to store the policy such that we can directly queury the - // policies with generation policies - // PolicyListerExpansion - c.processNamespace(ns) - return nil -} diff --git a/pkg/gencontroller/generation.go b/pkg/gencontroller/generation.go deleted file mode 100644 index 9ddd3e54c9..0000000000 --- a/pkg/gencontroller/generation.go +++ /dev/null @@ -1,155 +0,0 @@ -package gencontroller - -import ( - "encoding/json" - "fmt" - "strings" - - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - - "github.com/golang/glog" - "github.com/nirmata/kyverno/pkg/engine" - event "github.com/nirmata/kyverno/pkg/event" - "github.com/nirmata/kyverno/pkg/info" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" -) - -func (c *Controller) processNamespace(ns *corev1.Namespace) error { - //Get all policies and then verify if the namespace matches any of the defined selectors - policies, err := c.listPolicies(ns) - if err != nil { - return err - } - // process policy on namespace - for _, p := range policies { - c.processPolicy(ns, p) - } - - return nil -} - -func (c *Controller) listPolicies(ns *corev1.Namespace) ([]*v1alpha1.Policy, error) { - var fpolicies []*v1alpha1.Policy - policies, err := c.policyLister.List(labels.NewSelector()) - if err != nil { - glog.Error("Unable to connect to policy controller. Unable to access policies not applying GENERATION rules") - return nil, err - } - for _, p := range policies { - // Check if the policy contains a generatoin rule - for _, r := range p.Spec.Rules { - if r.Generation != nil { - // Check if the resource meets the description - data, err := json.Marshal(ns) - if err != nil { - glog.Error(err) - continue - } - // convert types of GVK - nsGvk := schema.FromAPIVersionAndKind("v1", "Namespace") - // Hardcode as we have a informer on specified gvk - gvk := metav1.GroupVersionKind{Group: nsGvk.Group, Kind: nsGvk.Kind, Version: nsGvk.Version} - if engine.ResourceMeetsDescription(data, r.MatchResources.ResourceDescription, r.ExcludeResources.ResourceDescription, gvk) { - fpolicies = append(fpolicies, p) - break - } - } - } - } - - return fpolicies, nil -} - -func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) { - var eventInfo *event.Info - var onViolation bool - var msg string - - policyInfo := info.NewPolicyInfo(p.Name, - "Namespace", - ns.Name, - "", - p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW - - // convert to unstructured - unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns) - if err != nil { - glog.Error(err) - return - } - unstObj := unstructured.Unstructured{Object: unstrMap} - ruleInfos := engine.Generate(c.client, p, unstObj) - policyInfo.AddRuleInfos(ruleInfos) - - // generate annotations on namespace - c.createAnnotations(policyInfo) - //TODO generate namespace on created resources - - if !policyInfo.IsSuccessful() { - glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name) - for _, r := range ruleInfos { - glog.Warning(r.Msgs) - - if msg = strings.Join(r.Msgs, " "); strings.Contains(msg, "rule configuration not present in resource") { - onViolation = true - msg = fmt.Sprintf(`Resource creation violates generate rule '%s' of policy '%s'`, r.Name, policyInfo.Name) - } - } - - if onViolation { - glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name) - // Policy Violation - v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.FailedRules()) - c.violationBuilder.Add(v) - } else { - // Event - eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked, - event.FPolicyApplyBlockCreate, policyInfo.RNamespace+"/"+policyInfo.RName, policyInfo.GetRuleNames(false)) - - glog.V(2).Infof("Request blocked event info has prepared for %s/%s\n", policyKind, policyInfo.Name) - - c.eventController.Add(eventInfo) - } - return - } - - glog.Infof("Generation from policy %s has succesfully applied to %s/%s", p.Name, policyInfo.RKind, policyInfo.RName) - - eventInfo = event.NewEvent(policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, - event.PolicyApplied, event.SRulesApply, policyInfo.GetRuleNames(true), policyInfo.Name) - - glog.V(2).Infof("Success event info has prepared for %s/%s\n", policyInfo.RKind, policyInfo.RName) - - c.eventController.Add(eventInfo) -} - -func (c *Controller) createAnnotations(pi *info.PolicyInfo) { - //get resource - obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName) - if err != nil { - glog.Error(err) - return - } - // add annotation for policy application - ann := obj.GetAnnotations() - // Generation rules - gpatch, err := annotations.PatchAnnotations(ann, pi, info.Generation) - if err != nil { - glog.Error(err) - return - } - if gpatch == nil { - // nothing to patch - return - } - // add the anotation to the resource - _, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch) - if err != nil { - glog.Error(err) - return - } -} diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 16d3d19a15..2bfb048c45 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -15,7 +15,7 @@ import ( "github.com/golang/glog" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" - lister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" @@ -31,8 +31,8 @@ type WebhookServer struct { server http.Server client *client.Client kyvernoClient *kyvernoclient.Clientset - pLister lister.PolicyLister - pvLister lister.PolicyViolationLister + pLister kyvernolister.PolicyLister + pvLister kyvernolister.PolicyViolationLister pListerSynced cache.InformerSynced pvListerSynced cache.InformerSynced eventGen event.Interface From 0b5cc03b2d0fb580901936a4ac2809d589a0a2b6 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Mon, 19 Aug 2019 18:57:19 -0700 Subject: [PATCH 45/66] engineResponse to contain stats --- pkg/engine/generation.go | 14 ++++++++------ pkg/engine/mutation.go | 24 +++++++++++++----------- pkg/engine/utils.go | 11 +++++++++++ pkg/engine/validation.go | 19 +++++++++++-------- pkg/namespace/generation.go | 4 ++-- 5 files changed, 45 insertions(+), 27 deletions(-) diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index b90311822e..9578d39674 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -18,19 +18,20 @@ import ( ) //Generate apply generation rules on a resource -func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) []info.RuleInfo { +func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) EngineResponse { + var response EngineResponse var executionTime time.Duration - var rulesAppliedCount int startTime := time.Now() glog.V(4).Infof("started applying generation rules of policy %q (%v)", policy.Name, startTime) defer func() { executionTime = time.Since(startTime) - glog.V(4).Infof("Finished applying generation rules policy %q (%v)", policy.Name, executionTime) - glog.V(4).Infof("Generation Rules appplied succesfully count %q for policy %q", rulesAppliedCount, policy.Name) + response.ExecutionTime = time.Since(startTime) + glog.V(4).Infof("Finished applying generation rules policy %q (%v)", policy.Name, response.ExecutionTime) + glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", response.RulesAppliedCount, policy.Name) }() succesfulRuleCount := func() { // rules applied succesfully count - rulesAppliedCount++ + response.RulesAppliedCount++ } ris := []info.RuleInfo{} @@ -52,7 +53,8 @@ func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unst } ris = append(ris, ri) } - return ris + response.RuleInfos = ris + return response } func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen kyverno.Generation, policyCreationTime metav1.Time) error { diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 7cade297b1..88ae7d7644 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -13,22 +13,23 @@ import ( // Mutate performs mutation. Overlay first and then mutation patches func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse { + var response EngineResponse var allPatches, rulePatches [][]byte var err error var errs []error ris := []info.RuleInfo{} var executionTime time.Duration - var rulesAppliedCount int startTime := time.Now() glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime) defer func() { executionTime = time.Since(startTime) - glog.V(4).Infof("Finished applying mutation rules policy %q (%v)", policy.Name, executionTime) - glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", rulesAppliedCount, policy.Name) + response.ExecutionTime = time.Since(startTime) + glog.V(4).Infof("Finished applying mutation rules policy %q (%v)", policy.Name, response.ExecutionTime) + glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", response.RulesAppliedCount, policy.Name) }() succesfulRuleCount := func() { // rules applied succesfully count - rulesAppliedCount++ + response.RulesAppliedCount++ } patchedDocument, err := resource.MarshalJSON() @@ -38,7 +39,8 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes if err != nil { glog.V(4).Infof("unable to marshal resource : %v", err) - return EngineResponse{PatchedResource: resource} + response.PatchedResource = resource + return response } for _, rule := range policy.Spec.Rules { @@ -114,12 +116,12 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes patchedResource, err := ConvertToUnstructured(patchedDocument) if err != nil { glog.Errorf("Failed to convert patched resource to unstructuredtype, err%v\n:", err) - return EngineResponse{PatchedResource: resource} + response.PatchedResource = resource + return response } - return EngineResponse{ - Patches: allPatches, - PatchedResource: *patchedResource, - RuleInfos: ris, - } + response.Patches = allPatches + response.PatchedResource = *patchedResource + response.RuleInfos = ris + return response } diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index 9096a74869..ff2f3f9745 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -5,6 +5,7 @@ import ( "fmt" "strconv" "strings" + "time" "github.com/golang/glog" @@ -18,10 +19,20 @@ import ( "k8s.io/apimachinery/pkg/labels" ) +//EngineResponse provides the response to the application of a policy rule set on a resource type EngineResponse struct { Patches [][]byte PatchedResource unstructured.Unstructured RuleInfos []info.RuleInfo + EngineStats +} + +//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 + ExecutionTime time.Duration + // Count of rules that were applied succesfully + RulesAppliedCount int } // //ListResourcesThatApplyToPolicy returns list of resources that are filtered by policy rules diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 42d917f998..9dcaca2ae1 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -19,29 +19,32 @@ import ( // Validate handles validating admission request // Checks the target resources for rules defined in the policy func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse { + var response EngineResponse var executionTime time.Duration - var rulesAppliedCount int startTime := time.Now() glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime) defer func() { executionTime = time.Since(startTime) - glog.V(4).Infof("Finished applying validation rules policy %q (%v)", policy.Name, executionTime) - glog.V(4).Infof("Validation Rules appplied succesfully count %q for policy %q", rulesAppliedCount, policy.Name) + response.ExecutionTime = time.Since(startTime) + glog.V(4).Infof("Finished applying mutation rules policy %q (%v)", policy.Name, response.ExecutionTime) + glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", response.RulesAppliedCount, policy.Name) }() succesfulRuleCount := func() { // rules applied succesfully count - rulesAppliedCount++ + response.RulesAppliedCount++ } resourceRaw, err := resource.MarshalJSON() if err != nil { glog.V(4).Infof("Skip processing validating rule, unable to marshal resource : %v\n", err) - return EngineResponse{PatchedResource: resource} + response.PatchedResource = resource + return response } var resourceInt interface{} if err := json.Unmarshal(resourceRaw, &resourceInt); err != nil { glog.V(4).Infof("unable to unmarshal resource : %v\n", err) - return EngineResponse{PatchedResource: resource} + response.PatchedResource = resource + return response } var ruleInfos []info.RuleInfo @@ -73,8 +76,8 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineR } ruleInfos = append(ruleInfos, ruleInfo) } - - return EngineResponse{RuleInfos: ruleInfos} + response.RuleInfos = ruleInfos + return response } // validateResourceWithPattern is a start of element-by-element validation process diff --git a/pkg/namespace/generation.go b/pkg/namespace/generation.go index 18a6a2908d..ca715e20ce 100644 --- a/pkg/namespace/generation.go +++ b/pkg/namespace/generation.go @@ -148,8 +148,8 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, poli glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime)) }() policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) - ruleInfos := engine.Generate(client, policy, resource) - policyInfo.AddRuleInfos(ruleInfos) + engineResponse := engine.Generate(client, policy, resource) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) return policyInfo } From 0157d80b2c093470007c9615305338152cd8569e Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Mon, 19 Aug 2019 19:26:51 -0700 Subject: [PATCH 46/66] add check for registerinig webhookconfiguration in policy controller --- main.go | 61 ++++++++++++++++------------------ pkg/dclient/client.go | 8 ++--- pkg/dclient/client_test.go | 4 +-- pkg/dclient/utils.go | 2 +- pkg/policy/controller.go | 58 +++++++++++++++++++++++++++----- pkg/webhooks/registration.go | 6 ++-- pkg/webhooks/webhookManager.go | 2 +- 7 files changed, 90 insertions(+), 51 deletions(-) diff --git a/main.go b/main.go index 6db33a255e..76e2d79e3f 100644 --- a/main.go +++ b/main.go @@ -13,9 +13,8 @@ import ( "github.com/nirmata/kyverno/pkg/namespace" "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/policyviolation" - "github.com/nirmata/kyverno/pkg/utils" "github.com/nirmata/kyverno/pkg/webhooks" - kubeinformers "k8s.io/client-go/informers" + "k8s.io/client-go/informers" "k8s.io/sample-controller/pkg/signals" ) @@ -28,6 +27,8 @@ var ( webhookTimeout int ) +const defaultReSyncTime = 10 * time.Second + func main() { defer glog.Flush() printVersionInfo() @@ -59,40 +60,14 @@ func main() { // - Policy // - PolicyVolation // - cache resync time: 10 seconds - pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10*time.Second) + pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, defaultReSyncTime) // EVENT GENERATOR // - generate event with retry egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies()) - // POLICY CONTROLLER - // - reconciliation policy and policy violation - // - process policy on existing resources - // - status: violation count - - pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) - if err != nil { - glog.Fatalf("error creating policy controller: %v\n", err) - } - - // POLICY VIOLATION CONTROLLER - // status: lastUpdatTime - pvc, err := policyviolation.NewPolicyViolationController(client, pclient, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) - if err != nil { - glog.Fatalf("error creating policy violation controller: %v\n", err) - } - - // NAMESPACE INFORMER - // watches namespace resource - // - cache resync time: 10 seconds - kubeClient, err := utils.NewKubeClient(clientConfig) - if err != nil { - glog.Fatalf("Error creating kubernetes client: %v\n", err) - } - kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Second) - - // GENERATE CONTROLLER - // - watches for Namespace resource and generates resource based on the policy generate rule - nsc := namespace.NewNamespaceController(pclient, client, kubeInformer.Core().V1().Namespaces(), pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) + // mutatingWebhookConfiguration Informer + kubeInformer := informers.NewSharedInformerFactory(client.Kclient, defaultReSyncTime) + mutatingWebhookConfigurationLister := kubeInformer.Admissionregistration().V1beta1().MutatingWebhookConfigurations().Lister() tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { @@ -110,6 +85,28 @@ func main() { if err = webhookRegistrationClient.Register(); err != nil { glog.Fatalf("Failed registering Admission Webhooks: %v\n", err) } + + // POLICY CONTROLLER + // - reconciliation policy and policy violation + // - process policy on existing resources + // - status: violation count + + pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, mutatingWebhookConfigurationLister, webhookRegistrationClient) + if err != nil { + glog.Fatalf("error creating policy controller: %v\n", err) + } + + // POLICY VIOLATION CONTROLLER + // status: lastUpdatTime + pvc, err := policyviolation.NewPolicyViolationController(client, pclient, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) + if err != nil { + glog.Fatalf("error creating policy violation controller: %v\n", err) + } + + // GENERATE CONTROLLER + // - watches for Namespace resource and generates resource based on the policy generate rule + nsc := namespace.NewNamespaceController(pclient, client, kubeInformer.Core().V1().Namespaces(), pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) + server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go index 50a1f1d3e2..4c46dd13f9 100644 --- a/pkg/dclient/client.go +++ b/pkg/dclient/client.go @@ -31,7 +31,7 @@ type Client struct { client dynamic.Interface cachedClient discovery.CachedDiscoveryInterface clientConfig *rest.Config - kclient kubernetes.Interface + Kclient kubernetes.Interface DiscoveryClient IDiscovery } @@ -48,7 +48,7 @@ func NewClient(config *rest.Config) (*Client, error) { client := Client{ client: dclient, clientConfig: config, - kclient: kclient, + Kclient: kclient, } // Set discovery client // @@ -75,12 +75,12 @@ func (c *Client) GetKubePolicyDeployment() (*apps.Deployment, error) { //TODO: can we use dynamic client to fetch the typed interface // or generate a kube client value to access the interface func (c *Client) GetEventsInterface() (event.EventInterface, error) { - return c.kclient.CoreV1().Events(""), nil + return c.Kclient.CoreV1().Events(""), nil } //GetCSRInterface provides type interface for CSR func (c *Client) GetCSRInterface() (csrtype.CertificateSigningRequestInterface, error) { - return c.kclient.CertificatesV1beta1().CertificateSigningRequests(), nil + return c.Kclient.CertificatesV1beta1().CertificateSigningRequests(), nil } func (c *Client) getInterface(resource string) dynamic.NamespaceableResourceInterface { diff --git a/pkg/dclient/client_test.go b/pkg/dclient/client_test.go index db4396f1c0..2673698b18 100644 --- a/pkg/dclient/client_test.go +++ b/pkg/dclient/client_test.go @@ -3,7 +3,7 @@ package client import ( "testing" - policytypes "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + policytypes "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -130,7 +130,7 @@ func TestGenerateResource(t *testing.T) { } gen := policytypes.Generation{Kind: "TheKind", Name: "gen-kind", - Clone: &policytypes.CloneFrom{Namespace: "ns-foo", Name: "name-foo"}} + Clone: policytypes.CloneFrom{Namespace: "ns-foo", Name: "name-foo"}} err = f.client.GenerateResource(gen, ns.GetName(), false) if err != nil { t.Errorf("GenerateResource not working: %s", err) diff --git a/pkg/dclient/utils.go b/pkg/dclient/utils.go index 6492187038..d82500f0d0 100644 --- a/pkg/dclient/utils.go +++ b/pkg/dclient/utils.go @@ -32,7 +32,7 @@ func NewMockClient(scheme *runtime.Scheme, objects ...runtime.Object) (*Client, kclient := kubernetesfake.NewSimpleClientset(objects...) return &Client{ client: client, - kclient: kclient, + Kclient: kclient, }, nil } diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 142f28594f..7438c4f2c5 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -13,9 +13,11 @@ import ( "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/utils" + "github.com/nirmata/kyverno/pkg/webhooks" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -26,6 +28,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" + v1beta1 "k8s.io/client-go/listers/admissionregistration/v1beta1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" @@ -62,8 +65,12 @@ type PolicyController struct { pvLister kyvernolister.PolicyViolationLister // pListerSynced returns true if the Policy store has been synced at least once pListerSynced cache.InformerSynced - // pvListerSynced retrns true if the Policy store has been synced at least once + // pvListerSynced returns true if the Policy store has been synced at least once pvListerSynced cache.InformerSynced + // mutationwebhookInformer can list/get mutatingwebhookconfigurations + mutationwebhookInformer v1beta1.MutatingWebhookConfigurationLister + // WebhookRegistrationClient + webhookRegistrationClient *webhooks.WebhookRegistrationClient // Resource manager, manages the mapping for already processed resource rm resourceManager // filter the resources defined in the list @@ -71,7 +78,8 @@ type PolicyController struct { } // NewPolicyController create a new PolicyController -func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer kyvernoinformer.PolicyInformer, pvInformer kyvernoinformer.PolicyViolationInformer, eventGen event.Interface) (*PolicyController, error) { +func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer kyvernoinformer.PolicyInformer, pvInformer kyvernoinformer.PolicyViolationInformer, + eventGen event.Interface, mutationwebhookInformer v1beta1.MutatingWebhookConfigurationLister, webhookRegistrationClient *webhooks.WebhookRegistrationClient) (*PolicyController, error) { // Event broad caster eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) @@ -82,11 +90,13 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: eventInterface}) pc := PolicyController{ - client: client, - kyvernoClient: kyvernoClient, - eventGen: eventGen, - eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policy_controller"}), - queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policy"), + client: client, + kyvernoClient: kyvernoClient, + eventGen: eventGen, + eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policy_controller"}), + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policy"), + mutationwebhookInformer: mutationwebhookInformer, + webhookRegistrationClient: webhookRegistrationClient, } pc.pvControl = RealPVControl{Client: kyvernoClient, Recorder: pc.eventRecorder} @@ -384,13 +394,18 @@ func (pc *PolicyController) syncPolicy(key string) error { policy, err := pc.pLister.Get(key) if errors.IsNotFound(err) { glog.V(2).Infof("Policy %v has been deleted", key) - return nil + err = pc.handleWebhookRegistration(true) + return err } if err != nil { return err } + if err := pc.handleWebhookRegistration(false); err != nil { + glog.Errorln(err) + } + // Deep-copy otherwise we are mutating our cache. // TODO: Deep-copy only when needed. p := policy.DeepCopy() @@ -406,6 +421,33 @@ func (pc *PolicyController) syncPolicy(key string) error { return pc.syncStatusOnly(p, pvList) } +func (pc *PolicyController) handleWebhookRegistration(emptyPolicy bool) error { + selector := &metav1.LabelSelector{MatchLabels: config.KubePolicyAppLabels} + webhookSelector, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return fmt.Errorf("invalid label selector: %v", err) + } + + list, err := pc.mutationwebhookInformer.List(webhookSelector) + if err != nil { + return fmt.Errorf("failed to list mutatingwebhookconfigurations, err %v", err) + } + + if emptyPolicy { + // deregister webhookconfigurations it it exists + if list != nil { + pc.webhookRegistrationClient.DeregisterMutatingWebhook() + } + return nil + } + + if list == nil { + pc.webhookRegistrationClient.RegisterMutatingWebhook() + } + + return nil +} + //syncStatusOnly updates the policy status subresource // status: // - violations : (count of the resources that violate this policy ) diff --git a/pkg/webhooks/registration.go b/pkg/webhooks/registration.go index 99c2638148..bbdbf94afb 100644 --- a/pkg/webhooks/registration.go +++ b/pkg/webhooks/registration.go @@ -108,7 +108,7 @@ func (wrc *WebhookRegistrationClient) RegisterPolicyValidatingWebhook() error { // This function does not fail on error: // Register will fail if the config exists, so there is no need to fail on error func (wrc *WebhookRegistrationClient) DeregisterAll() { - wrc.deregisterMutatingWebhook() + wrc.DeregisterMutatingWebhook() wrc.deregisterValidatingWebhook() if wrc.serverIP != "" { @@ -124,11 +124,11 @@ func (wrc *WebhookRegistrationClient) DeregisterAll() { } func (wrc *WebhookRegistrationClient) deregister() { - wrc.deregisterMutatingWebhook() + wrc.DeregisterMutatingWebhook() wrc.deregisterValidatingWebhook() } -func (wrc *WebhookRegistrationClient) deregisterMutatingWebhook() { +func (wrc *WebhookRegistrationClient) DeregisterMutatingWebhook() { if wrc.serverIP != "" { err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationDebug, &v1.DeleteOptions{}) if err != nil && !errorsapi.IsNotFound(err) { diff --git a/pkg/webhooks/webhookManager.go b/pkg/webhooks/webhookManager.go index 2cb082a91d..5a498f68fa 100644 --- a/pkg/webhooks/webhookManager.go +++ b/pkg/webhooks/webhookManager.go @@ -41,7 +41,7 @@ func (ws *WebhookServer) deregisterWebhookConfigurations(policy kyverno.Policy) // deregister webhook if no policy found in cluster if len(policies) == 1 { - ws.webhookRegistrationClient.deregisterMutatingWebhook() + ws.webhookRegistrationClient.DeregisterMutatingWebhook() glog.Infoln("Mutating webhook deregistered") } From 54fc55f47a760c8d99e2639a779af8c3c1bbc5ae Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Tue, 20 Aug 2019 10:08:00 -0700 Subject: [PATCH 47/66] add debug log --- pkg/policy/controller.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 7438c4f2c5..6e816621cb 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -436,12 +436,14 @@ func (pc *PolicyController) handleWebhookRegistration(emptyPolicy bool) error { if emptyPolicy { // deregister webhookconfigurations it it exists if list != nil { + glog.V(3).Infoln("No policy found in the cluster, deregistering webhook") pc.webhookRegistrationClient.DeregisterMutatingWebhook() } return nil } if list == nil { + glog.V(3).Infoln("Found policy without mutatingwebhook, registering webhook") pc.webhookRegistrationClient.RegisterMutatingWebhook() } From e83cb51313001b6bc20a515f9b446be44a147454 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Tue, 20 Aug 2019 10:16:26 -0700 Subject: [PATCH 48/66] update gitignore --- .gitignore | 1 - .../versioned/typed/kyverno/v1alpha1/doc.go | 20 ++ .../typed/kyverno/v1alpha1/fake/doc.go | 20 ++ .../v1alpha1/fake/fake_kyverno_client.go | 44 +++++ .../kyverno/v1alpha1/fake/fake_policy.go | 131 +++++++++++++ .../v1alpha1/fake/fake_policyviolation.go | 131 +++++++++++++ .../kyverno/v1alpha1/generated_expansion.go | 23 +++ .../typed/kyverno/v1alpha1/kyverno_client.go | 95 +++++++++ .../typed/kyverno/v1alpha1/policy.go | 180 ++++++++++++++++++ .../typed/kyverno/v1alpha1/policyviolation.go | 180 ++++++++++++++++++ .../externalversions/kyverno/interface.go | 46 +++++ .../kyverno/v1alpha1/interface.go | 52 +++++ .../kyverno/v1alpha1/policy.go | 88 +++++++++ .../kyverno/v1alpha1/policyviolation.go | 88 +++++++++ 14 files changed, 1098 insertions(+), 1 deletion(-) create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/doc.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policy.go create mode 100644 pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go create mode 100644 pkg/client/informers/externalversions/kyverno/interface.go create mode 100644 pkg/client/informers/externalversions/kyverno/v1alpha1/interface.go create mode 100644 pkg/client/informers/externalversions/kyverno/v1alpha1/policy.go create mode 100644 pkg/client/informers/externalversions/kyverno/v1alpha1/policyviolation.go diff --git a/.gitignore b/.gitignore index fe301c5536..d4b5601032 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ certs Gopkg.lock .vscode -kyverno gh-pages/public _output coverage.txt diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/doc.go new file mode 100644 index 0000000000..df51baa4d4 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go new file mode 100644 index 0000000000..16f4439906 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go new file mode 100644 index 0000000000..b6bfe1d779 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_kyverno_client.go @@ -0,0 +1,44 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeKyvernoV1alpha1 struct { + *testing.Fake +} + +func (c *FakeKyvernoV1alpha1) Policies() v1alpha1.PolicyInterface { + return &FakePolicies{c} +} + +func (c *FakeKyvernoV1alpha1) PolicyViolations() v1alpha1.PolicyViolationInterface { + return &FakePolicyViolations{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeKyvernoV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go new file mode 100644 index 0000000000..76bd94b95f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policy.go @@ -0,0 +1,131 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakePolicies implements PolicyInterface +type FakePolicies struct { + Fake *FakeKyvernoV1alpha1 +} + +var policiesResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policies"} + +var policiesKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "Policy"} + +// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. +func (c *FakePolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(policiesResource, name), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// List takes label and field selectors, and returns the list of Policies that match those selectors. +func (c *FakePolicies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(policiesResource, policiesKind, opts), &v1alpha1.PolicyList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.PolicyList{ListMeta: obj.(*v1alpha1.PolicyList).ListMeta} + for _, item := range obj.(*v1alpha1.PolicyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested policies. +func (c *FakePolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(policiesResource, opts)) +} + +// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *FakePolicies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(policiesResource, policy), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *FakePolicies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(policiesResource, policy), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePolicies) UpdateStatus(policy *v1alpha1.Policy) (*v1alpha1.Policy, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(policiesResource, "status", policy), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} + +// Delete takes name of the policy and deletes it. Returns an error if one occurs. +func (c *FakePolicies) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(policiesResource, name), &v1alpha1.Policy{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(policiesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.PolicyList{}) + return err +} + +// Patch applies the patch and returns the patched policy. +func (c *FakePolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(policiesResource, name, pt, data, subresources...), &v1alpha1.Policy{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Policy), err +} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go new file mode 100644 index 0000000000..e5306f11fe --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/fake/fake_policyviolation.go @@ -0,0 +1,131 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakePolicyViolations implements PolicyViolationInterface +type FakePolicyViolations struct { + Fake *FakeKyvernoV1alpha1 +} + +var policyviolationsResource = schema.GroupVersionResource{Group: "kyverno.io", Version: "v1alpha1", Resource: "policyviolations"} + +var policyviolationsKind = schema.GroupVersionKind{Group: "kyverno.io", Version: "v1alpha1", Kind: "PolicyViolation"} + +// Get takes name of the policyViolation, and returns the corresponding policyViolation object, and an error if there is any. +func (c *FakePolicyViolations) Get(name string, options v1.GetOptions) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(policyviolationsResource, name), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// List takes label and field selectors, and returns the list of PolicyViolations that match those selectors. +func (c *FakePolicyViolations) List(opts v1.ListOptions) (result *v1alpha1.PolicyViolationList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(policyviolationsResource, policyviolationsKind, opts), &v1alpha1.PolicyViolationList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.PolicyViolationList{ListMeta: obj.(*v1alpha1.PolicyViolationList).ListMeta} + for _, item := range obj.(*v1alpha1.PolicyViolationList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested policyViolations. +func (c *FakePolicyViolations) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(policyviolationsResource, opts)) +} + +// Create takes the representation of a policyViolation and creates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *FakePolicyViolations) Create(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(policyviolationsResource, policyViolation), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// Update takes the representation of a policyViolation and updates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *FakePolicyViolations) Update(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(policyviolationsResource, policyViolation), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePolicyViolations) UpdateStatus(policyViolation *v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(policyviolationsResource, "status", policyViolation), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} + +// Delete takes name of the policyViolation and deletes it. Returns an error if one occurs. +func (c *FakePolicyViolations) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(policyviolationsResource, name), &v1alpha1.PolicyViolation{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePolicyViolations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(policyviolationsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.PolicyViolationList{}) + return err +} + +// Patch applies the patch and returns the patched policyViolation. +func (c *FakePolicyViolations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(policyviolationsResource, name, pt, data, subresources...), &v1alpha1.PolicyViolation{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.PolicyViolation), err +} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go new file mode 100644 index 0000000000..442fa55942 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/generated_expansion.go @@ -0,0 +1,23 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type PolicyExpansion interface{} + +type PolicyViolationExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go new file mode 100644 index 0000000000..ed9024fd36 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/kyverno_client.go @@ -0,0 +1,95 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +type KyvernoV1alpha1Interface interface { + RESTClient() rest.Interface + PoliciesGetter + PolicyViolationsGetter +} + +// KyvernoV1alpha1Client is used to interact with features provided by the kyverno.io group. +type KyvernoV1alpha1Client struct { + restClient rest.Interface +} + +func (c *KyvernoV1alpha1Client) Policies() PolicyInterface { + return newPolicies(c) +} + +func (c *KyvernoV1alpha1Client) PolicyViolations() PolicyViolationInterface { + return newPolicyViolations(c) +} + +// NewForConfig creates a new KyvernoV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*KyvernoV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &KyvernoV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new KyvernoV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *KyvernoV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new KyvernoV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *KyvernoV1alpha1Client { + return &KyvernoV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *KyvernoV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policy.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policy.go new file mode 100644 index 0000000000..8c2ae0058a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policy.go @@ -0,0 +1,180 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + scheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PoliciesGetter has a method to return a PolicyInterface. +// A group's client should implement this interface. +type PoliciesGetter interface { + Policies() PolicyInterface +} + +// PolicyInterface has methods to work with Policy resources. +type PolicyInterface interface { + Create(*v1alpha1.Policy) (*v1alpha1.Policy, error) + Update(*v1alpha1.Policy) (*v1alpha1.Policy, error) + UpdateStatus(*v1alpha1.Policy) (*v1alpha1.Policy, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Policy, error) + List(opts v1.ListOptions) (*v1alpha1.PolicyList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) + PolicyExpansion +} + +// policies implements PolicyInterface +type policies struct { + client rest.Interface +} + +// newPolicies returns a Policies +func newPolicies(c *KyvernoV1alpha1Client) *policies { + return &policies{ + client: c.RESTClient(), + } +} + +// Get takes name of the policy, and returns the corresponding policy object, and an error if there is any. +func (c *policies) Get(name string, options v1.GetOptions) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Get(). + Resource("policies"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Policies that match those selectors. +func (c *policies) List(opts v1.ListOptions) (result *v1alpha1.PolicyList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PolicyList{} + err = c.client.Get(). + Resource("policies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested policies. +func (c *policies) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("policies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a policy and creates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *policies) Create(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Post(). + Resource("policies"). + Body(policy). + Do(). + Into(result) + return +} + +// Update takes the representation of a policy and updates it. Returns the server's representation of the policy, and an error, if there is any. +func (c *policies) Update(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Put(). + Resource("policies"). + Name(policy.Name). + Body(policy). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *policies) UpdateStatus(policy *v1alpha1.Policy) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Put(). + Resource("policies"). + Name(policy.Name). + SubResource("status"). + Body(policy). + Do(). + Into(result) + return +} + +// Delete takes name of the policy and deletes it. Returns an error if one occurs. +func (c *policies) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("policies"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *policies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("policies"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched policy. +func (c *policies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Policy, err error) { + result = &v1alpha1.Policy{} + err = c.client.Patch(pt). + Resource("policies"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go new file mode 100644 index 0000000000..33e76f8343 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/kyverno/v1alpha1/policyviolation.go @@ -0,0 +1,180 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + scheme "github.com/nirmata/kyverno/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PolicyViolationsGetter has a method to return a PolicyViolationInterface. +// A group's client should implement this interface. +type PolicyViolationsGetter interface { + PolicyViolations() PolicyViolationInterface +} + +// PolicyViolationInterface has methods to work with PolicyViolation resources. +type PolicyViolationInterface interface { + Create(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) + Update(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) + UpdateStatus(*v1alpha1.PolicyViolation) (*v1alpha1.PolicyViolation, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.PolicyViolation, error) + List(opts v1.ListOptions) (*v1alpha1.PolicyViolationList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) + PolicyViolationExpansion +} + +// policyViolations implements PolicyViolationInterface +type policyViolations struct { + client rest.Interface +} + +// newPolicyViolations returns a PolicyViolations +func newPolicyViolations(c *KyvernoV1alpha1Client) *policyViolations { + return &policyViolations{ + client: c.RESTClient(), + } +} + +// Get takes name of the policyViolation, and returns the corresponding policyViolation object, and an error if there is any. +func (c *policyViolations) Get(name string, options v1.GetOptions) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Get(). + Resource("policyviolations"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PolicyViolations that match those selectors. +func (c *policyViolations) List(opts v1.ListOptions) (result *v1alpha1.PolicyViolationList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.PolicyViolationList{} + err = c.client.Get(). + Resource("policyviolations"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested policyViolations. +func (c *policyViolations) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("policyviolations"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a policyViolation and creates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *policyViolations) Create(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Post(). + Resource("policyviolations"). + Body(policyViolation). + Do(). + Into(result) + return +} + +// Update takes the representation of a policyViolation and updates it. Returns the server's representation of the policyViolation, and an error, if there is any. +func (c *policyViolations) Update(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Put(). + Resource("policyviolations"). + Name(policyViolation.Name). + Body(policyViolation). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *policyViolations) UpdateStatus(policyViolation *v1alpha1.PolicyViolation) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Put(). + Resource("policyviolations"). + Name(policyViolation.Name). + SubResource("status"). + Body(policyViolation). + Do(). + Into(result) + return +} + +// Delete takes name of the policyViolation and deletes it. Returns an error if one occurs. +func (c *policyViolations) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("policyviolations"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *policyViolations) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("policyviolations"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched policyViolation. +func (c *policyViolations) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PolicyViolation, err error) { + result = &v1alpha1.PolicyViolation{} + err = c.client.Patch(pt). + Resource("policyviolations"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/kyverno/interface.go b/pkg/client/informers/externalversions/kyverno/interface.go new file mode 100644 index 0000000000..dfbb1603d8 --- /dev/null +++ b/pkg/client/informers/externalversions/kyverno/interface.go @@ -0,0 +1,46 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package kyverno + +import ( + internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/kyverno/v1alpha1/interface.go b/pkg/client/informers/externalversions/kyverno/v1alpha1/interface.go new file mode 100644 index 0000000000..43f5d6d10f --- /dev/null +++ b/pkg/client/informers/externalversions/kyverno/v1alpha1/interface.go @@ -0,0 +1,52 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Policies returns a PolicyInformer. + Policies() PolicyInformer + // PolicyViolations returns a PolicyViolationInformer. + PolicyViolations() PolicyViolationInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// Policies returns a PolicyInformer. +func (v *version) Policies() PolicyInformer { + return &policyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// PolicyViolations returns a PolicyViolationInformer. +func (v *version) PolicyViolations() PolicyViolationInformer { + return &policyViolationInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/kyverno/v1alpha1/policy.go b/pkg/client/informers/externalversions/kyverno/v1alpha1/policy.go new file mode 100644 index 0000000000..5ea3a9e039 --- /dev/null +++ b/pkg/client/informers/externalversions/kyverno/v1alpha1/policy.go @@ -0,0 +1,88 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PolicyInformer provides access to a shared informer and lister for +// Policies. +type PolicyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.PolicyLister +} + +type policyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewPolicyInformer constructs a new informer for Policy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPolicyInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredPolicyInformer constructs a new informer for Policy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().Policies().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().Policies().Watch(options) + }, + }, + &kyvernov1alpha1.Policy{}, + resyncPeriod, + indexers, + ) +} + +func (f *policyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *policyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&kyvernov1alpha1.Policy{}, f.defaultInformer) +} + +func (f *policyInformer) Lister() v1alpha1.PolicyLister { + return v1alpha1.NewPolicyLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/kyverno/v1alpha1/policyviolation.go b/pkg/client/informers/externalversions/kyverno/v1alpha1/policyviolation.go new file mode 100644 index 0000000000..f5994e19db --- /dev/null +++ b/pkg/client/informers/externalversions/kyverno/v1alpha1/policyviolation.go @@ -0,0 +1,88 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + kyvernov1alpha1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + versioned "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + internalinterfaces "github.com/nirmata/kyverno/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PolicyViolationInformer provides access to a shared informer and lister for +// PolicyViolations. +type PolicyViolationInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.PolicyViolationLister +} + +type policyViolationInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewPolicyViolationInformer constructs a new informer for PolicyViolation type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPolicyViolationInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPolicyViolationInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredPolicyViolationInformer constructs a new informer for PolicyViolation type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPolicyViolationInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().PolicyViolations().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KyvernoV1alpha1().PolicyViolations().Watch(options) + }, + }, + &kyvernov1alpha1.PolicyViolation{}, + resyncPeriod, + indexers, + ) +} + +func (f *policyViolationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPolicyViolationInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *policyViolationInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&kyvernov1alpha1.PolicyViolation{}, f.defaultInformer) +} + +func (f *policyViolationInformer) Lister() v1alpha1.PolicyViolationLister { + return v1alpha1.NewPolicyViolationLister(f.Informer().GetIndexer()) +} From e507fb642292803c408aeddfe5f7c2419629ae1d Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 12:51:25 -0700 Subject: [PATCH 49/66] recieve stats + update violation status move to aggregator --- main.go | 2 +- pkg/engine/generation.go | 6 +- pkg/engine/mutation.go | 16 +++--- pkg/engine/validation.go | 14 ++--- pkg/policy/controller.go | 18 +++--- pkg/policy/status.go | 109 ++++++++++++++++++++++++++++++++++++- pkg/webhooks/mutation.go | 26 +++++++++ pkg/webhooks/server.go | 7 ++- pkg/webhooks/validation.go | 21 +++++++ 9 files changed, 187 insertions(+), 32 deletions(-) diff --git a/main.go b/main.go index 6db33a255e..2c2a950354 100644 --- a/main.go +++ b/main.go @@ -110,7 +110,7 @@ func main() { if err = webhookRegistrationClient.Register(); err != nil { glog.Fatalf("Failed registering Admission Webhooks: %v\n", err) } - server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, filterK8Resources) + server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, pc.GetPolicyStatusAggregator(), filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 9578d39674..9b46aa755f 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -20,16 +20,14 @@ import ( //Generate apply generation rules on a resource func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) EngineResponse { var response EngineResponse - var executionTime time.Duration startTime := time.Now() glog.V(4).Infof("started applying generation rules of policy %q (%v)", policy.Name, startTime) defer func() { - executionTime = time.Since(startTime) response.ExecutionTime = time.Since(startTime) glog.V(4).Infof("Finished applying generation rules policy %q (%v)", policy.Name, response.ExecutionTime) glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", response.RulesAppliedCount, policy.Name) }() - succesfulRuleCount := func() { + incrementAppliedRuleCount := func() { // rules applied succesfully count response.RulesAppliedCount++ } @@ -49,9 +47,9 @@ func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unst } else { ri.Addf("Generation succesfully.", rule.Name) glog.Infof("succesfully applied policy %s rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName()) - succesfulRuleCount() } ris = append(ris, ri) + incrementAppliedRuleCount() } response.RuleInfos = ris return response diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 88ae7d7644..fd453f4d16 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -12,22 +12,20 @@ import ( // Mutate performs mutation. Overlay first and then mutation patches -func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse { - var response EngineResponse +func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponse) { + // var response EngineResponse var allPatches, rulePatches [][]byte var err error var errs []error ris := []info.RuleInfo{} - var executionTime time.Duration startTime := time.Now() glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime) defer func() { - executionTime = time.Since(startTime) response.ExecutionTime = time.Since(startTime) - glog.V(4).Infof("Finished applying mutation rules policy %q (%v)", policy.Name, response.ExecutionTime) - glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", response.RulesAppliedCount, policy.Name) + glog.V(4).Infof("finished applying mutation rules policy %v (%v)", policy.Name, response.ExecutionTime) + glog.V(4).Infof("Mutation Rules appplied succesfully count %v for policy %q", response.RulesAppliedCount, policy.Name) }() - succesfulRuleCount := func() { + incrementAppliedRuleCount := func() { // rules applied succesfully count response.RulesAppliedCount++ } @@ -78,12 +76,12 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes allPatches = append(allPatches, rulePatches...) glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) - succesfulRuleCount() } else { glog.V(4).Infof("failed to apply overlay: %v", err) ruleInfo.Fail() ruleInfo.Addf("failed to apply overlay: %v", err) } + incrementAppliedRuleCount() } // Process Patches @@ -101,8 +99,8 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes ruleInfo.Patches = rulePatches allPatches = append(allPatches, rulePatches...) - succesfulRuleCount() } + incrementAppliedRuleCount() } patchedDocument, err = ApplyPatches(patchedDocument, rulePatches) diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 9dcaca2ae1..f43c59c2c1 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -18,18 +18,16 @@ import ( // Validate handles validating admission request // Checks the target resources for rules defined in the policy -func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse { - var response EngineResponse - var executionTime time.Duration +func Validate(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponse) { + // var response EngineResponse startTime := time.Now() glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime) defer func() { - executionTime = time.Since(startTime) response.ExecutionTime = time.Since(startTime) - glog.V(4).Infof("Finished applying mutation rules policy %q (%v)", policy.Name, response.ExecutionTime) - glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", response.RulesAppliedCount, policy.Name) + glog.V(4).Infof("Finished applying validation rules policy %v (%v)", policy.Name, response.ExecutionTime) + glog.V(4).Infof("Validation Rules appplied succesfully count %v for policy %q", response.RulesAppliedCount, policy.Name) }() - succesfulRuleCount := func() { + incrementAppliedRuleCount := func() { // rules applied succesfully count response.RulesAppliedCount++ } @@ -72,8 +70,8 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineR } else { ruleInfo.Add("Pattern succesfully validated") glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) - succesfulRuleCount() } + incrementAppliedRuleCount() ruleInfos = append(ruleInfos, ruleInfo) } response.RuleInfos = ruleInfos diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 142f28594f..b7bd93cef8 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -68,6 +68,8 @@ type PolicyController struct { rm resourceManager // filter the resources defined in the list filterK8Resources []utils.K8Resource + // recieves stats and aggregates details + statusAggregator *PolicyStatusAggregator } // NewPolicyController create a new PolicyController @@ -116,6 +118,9 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. //TODO: pass the time in seconds instead of converting it internally pc.rm = NewResourceManager(30) + // aggregator + pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient) + return &pc, nil } @@ -335,6 +340,9 @@ func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) { for i := 0; i < workers; i++ { go wait.Until(pc.worker, time.Second, stopCh) } + // policy status aggregator + //TODO: workers required for aggergation + pc.statusAggregator.Run(1, stopCh) <-stopCh } @@ -403,7 +411,8 @@ func (pc *PolicyController) syncPolicy(key string) error { policyInfos := pc.processExistingResources(*p) // report errors pc.report(policyInfos) - return pc.syncStatusOnly(p, pvList) + return pc.statusAggregator.UpdateViolationCount(p, pvList) + // return pc.syncStatusOnly(p, pvList) } //syncStatusOnly updates the policy status subresource @@ -422,13 +431,6 @@ func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno. return err } -func calculateStatus(pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus { - violationCount := len(pvList) - status := kyverno.PolicyStatus{ - Violations: violationCount, - } - return status -} func (pc *PolicyController) getPolicyViolationsForPolicy(p *kyverno.Policy) ([]*kyverno.PolicyViolation, error) { // List all PolicyViolation to find those we own but that no longer match our // selector. They will be orphaned by ClaimPolicyViolation(). diff --git a/pkg/policy/status.go b/pkg/policy/status.go index 7a49726b98..acc1ed3dbf 100644 --- a/pkg/policy/status.go +++ b/pkg/policy/status.go @@ -1,6 +1,15 @@ package policy -import "time" +import ( + "reflect" + "time" + + "github.com/golang/glog" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" +) type PolicyStatus struct { // average time required to process the policy rules on a resource @@ -12,3 +21,101 @@ type PolicyStatus struct { // Count of the resource for whom the mutation rules were applied succesfully resourcesMutatedCount int } + +type PolicyStatusAggregator struct { + // time since we start aggregating the stats + startTime time.Time + // channel to recieve stats + ch chan PolicyStat + // update polict status + psControl PStatusControlInterface +} + +//NewPolicyStatAggregator returns a new policy status +func NewPolicyStatAggregator(client *kyvernoclient.Clientset) *PolicyStatusAggregator { + psa := PolicyStatusAggregator{ + startTime: time.Now(), + ch: make(chan PolicyStat), + } + psa.psControl = PSControl{Client: client} + //TODO: add WaitGroup + return &psa +} + +func (psa *PolicyStatusAggregator) Run(workers int, stopCh <-chan struct{}) { + defer utilruntime.HandleCrash() + glog.V(4).Info("Started aggregator for policy status stats") + defer func() { + glog.V(4).Info("Shutting down aggregator for policy status stats") + }() + for i := 0; i < workers; i++ { + go wait.Until(psa.aggregate, time.Second, stopCh) + } +} +func (psa *PolicyStatusAggregator) aggregate() { + for r := range psa.ch { + glog.V(4).Infof("recieved policy stats %v", r) + } +} + +type PolicyStatusInterface interface { + SendStat(stat PolicyStat) + UpdateViolationCount(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error +} + +type PolicyStat struct { + PolicyName string + MutationExecutionTime time.Duration + ValidationExecutionTime time.Duration + RulesAppliedCount int + ResourceBlocked bool +} + +//SendStat sends the stat information for aggregation +func (psa *PolicyStatusAggregator) SendStat(stat PolicyStat) { + glog.V(4).Infof("sending policy stats: %v", stat) + // Send over channel + psa.ch <- stat +} + +//UpdateViolationCount updates the active violation count +func (psa *PolicyStatusAggregator) UpdateViolationCount(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error { + newStatus := calculateStatus(pvList) + if reflect.DeepEqual(newStatus, p.Status) { + // no update to status + return nil + } + // update status + newPolicy := p + newPolicy.Status = newStatus + + return psa.psControl.UpdatePolicyStatus(newPolicy) +} + +func calculateStatus(pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus { + violationCount := len(pvList) + status := kyverno.PolicyStatus{ + Violations: violationCount, + } + return status +} + +//GetPolicyStatusAggregator returns interface to send policy status stats +func (pc *PolicyController) GetPolicyStatusAggregator() PolicyStatusInterface { + return pc.statusAggregator +} + +//PStatusControlInterface Provides interface to operate on policy status +type PStatusControlInterface interface { + UpdatePolicyStatus(newPolicy *kyverno.Policy) error +} + +type PSControl struct { + Client kyvernoclient.Interface +} + +//UpdatePolicyStatus update policy status +func (c PSControl) UpdatePolicyStatus(newPolicy *kyverno.Policy) error { + _, err := c.Client.KyvernoV1alpha1().Policies().UpdateStatus(newPolicy) + return err +} diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index d1e9247af6..f706020627 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -4,6 +4,7 @@ import ( "github.com/golang/glog" engine "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" + policyctr "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/utils" v1beta1 "k8s.io/api/admission/v1beta1" "k8s.io/apimachinery/pkg/labels" @@ -14,6 +15,23 @@ import ( func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, engine.EngineResponse) { var patches [][]byte var policyInfos []info.PolicyInfo + var policyStats []policyctr.PolicyStat + // gather stats from the engine response + gatherStat := func(policyName string, er engine.EngineResponse) { + ps := policyctr.PolicyStat{} + ps.PolicyName = policyName + ps.MutationExecutionTime = er.ExecutionTime + ps.RulesAppliedCount = er.RulesAppliedCount + policyStats = append(policyStats, ps) + } + // send stats for aggregation + sendStat := func(blocked bool) { + for _, stat := range policyStats { + stat.ResourceBlocked = blocked + //SEND + ws.policyStatus.SendStat(stat) + } + } glog.V(5).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) @@ -54,6 +72,10 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool engineResponse = engine.Mutate(*policy, *resource) policyInfo.AddRuleInfos(engineResponse.RuleInfos) + // Gather policy application statistics + gatherStat(policy.Name, engineResponse) + + // ps := policyctr.NewPolicyStat(policy.Name, engineResponse.ExecutionTime, nil, engineResponse.RulesAppliedCount) if !policyInfo.IsSuccessful() { glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName()) @@ -67,6 +89,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool patches = append(patches, engineResponse.Patches...) policyInfos = append(policyInfos, policyInfo) glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) + } // ADD ANNOTATIONS @@ -80,11 +103,14 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool } ok, msg := isAdmSuccesful(policyInfos) + // Send policy engine Stats if ok { + sendStat(false) engineResponse.Patches = patches return true, engineResponse } + sendStat(true) glog.Errorf("Failed to mutate the resource: %s\n", msg) return false, engineResponse } diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 2bfb048c45..af6ae8a880 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -19,6 +19,7 @@ import ( "github.com/nirmata/kyverno/pkg/config" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" + "github.com/nirmata/kyverno/pkg/policy" tlsutils "github.com/nirmata/kyverno/pkg/tls" "github.com/nirmata/kyverno/pkg/utils" v1beta1 "k8s.io/api/admission/v1beta1" @@ -37,7 +38,9 @@ type WebhookServer struct { pvListerSynced cache.InformerSynced eventGen event.Interface webhookRegistrationClient *WebhookRegistrationClient - filterK8Resources []utils.K8Resource + // API to send policy stats for aggregation + policyStatus policy.PolicyStatusInterface + filterK8Resources []utils.K8Resource } // NewWebhookServer creates new instance of WebhookServer accordingly to given configuration @@ -50,6 +53,7 @@ func NewWebhookServer( pvInormer kyvernoinformer.PolicyViolationInformer, eventGen event.Interface, webhookRegistrationClient *WebhookRegistrationClient, + policyStatus policy.PolicyStatusInterface, filterK8Resources string) (*WebhookServer, error) { if tlsPair == nil { @@ -73,6 +77,7 @@ func NewWebhookServer( pvListerSynced: pInformer.Informer().HasSynced, eventGen: eventGen, webhookRegistrationClient: webhookRegistrationClient, + policyStatus: policyStatus, filterK8Resources: utils.ParseKinds(filterK8Resources), } mux := http.NewServeMux() diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 6e2953b324..088694bd67 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -4,6 +4,7 @@ import ( "github.com/golang/glog" engine "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" + policyctr "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/policyviolation" "github.com/nirmata/kyverno/pkg/utils" v1beta1 "k8s.io/api/admission/v1beta1" @@ -17,6 +18,23 @@ import ( // If there are no errors in validating rule we apply generation rules func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, resource unstructured.Unstructured) *v1beta1.AdmissionResponse { var policyInfos []info.PolicyInfo + var policyStats []policyctr.PolicyStat + // gather stats from the engine response + gatherStat := func(policyName string, er engine.EngineResponse) { + ps := policyctr.PolicyStat{} + ps.PolicyName = policyName + ps.ValidationExecutionTime = er.ExecutionTime + ps.RulesAppliedCount = er.RulesAppliedCount + policyStats = append(policyStats, ps) + } + // send stats for aggregation + sendStat := func(blocked bool) { + for _, stat := range policyStats { + stat.ResourceBlocked = blocked + //SEND + ws.policyStatus.SendStat(stat) + } + } glog.V(5).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation) @@ -55,6 +73,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, res if len(engineResponse.RuleInfos) == 0 { continue } + gatherStat(policy.Name, engineResponse) if len(engineResponse.RuleInfos) > 0 { glog.V(4).Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName()) @@ -87,6 +106,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, res // Even if one the policy being applied ok, msg := isAdmSuccesful(policyInfos) if !ok && toBlock(policyInfos) { + sendStat(true) return &v1beta1.AdmissionResponse{ Allowed: false, Result: &metav1.Status{ @@ -98,6 +118,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, res // ADD POLICY VIOLATIONS policyviolation.GeneratePolicyViolations(ws.pvListerSynced, ws.pvLister, ws.kyvernoClient, policyInfos) + sendStat(false) return &v1beta1.AdmissionResponse{ Allowed: true, } From bcad9ada2df1a6c98991ca51eb03265a408bfd9e Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 13:35:03 -0700 Subject: [PATCH 50/66] introduce locking for policy status updates --- pkg/policy/controller.go | 5 +-- pkg/policy/status.go | 66 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index b7bd93cef8..0457423250 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -119,7 +119,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. pc.rm = NewResourceManager(30) // aggregator - pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient) + pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient, pInformer.Lister()) return &pc, nil } @@ -411,7 +411,8 @@ func (pc *PolicyController) syncPolicy(key string) error { policyInfos := pc.processExistingResources(*p) // report errors pc.report(policyInfos) - return pc.statusAggregator.UpdateViolationCount(p, pvList) + // fetch the policy again via the aggreagator to remain consistent + return pc.statusAggregator.UpdateViolationCount(p.Name, pvList) // return pc.syncStatusOnly(p, pvList) } diff --git a/pkg/policy/status.go b/pkg/policy/status.go index acc1ed3dbf..fe8a77512a 100644 --- a/pkg/policy/status.go +++ b/pkg/policy/status.go @@ -2,11 +2,13 @@ package policy import ( "reflect" + "sync" "time" "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" ) @@ -29,14 +31,20 @@ type PolicyStatusAggregator struct { ch chan PolicyStat // update polict status psControl PStatusControlInterface + // pLister can list/get policy from the shared informer's store + pLister kyvernolister.PolicyLister + // UpdateViolationCount and SendStat can update same policy status + // we need to sync the updates using policyName + policyUpdateData sync.Map } //NewPolicyStatAggregator returns a new policy status -func NewPolicyStatAggregator(client *kyvernoclient.Clientset) *PolicyStatusAggregator { +func NewPolicyStatAggregator(client *kyvernoclient.Clientset, pLister kyvernolister.PolicyLister) *PolicyStatusAggregator { psa := PolicyStatusAggregator{ startTime: time.Now(), ch: make(chan PolicyStat), } + psa.pLister = pLister psa.psControl = PSControl{Client: client} //TODO: add WaitGroup return &psa @@ -53,14 +61,46 @@ func (psa *PolicyStatusAggregator) Run(workers int, stopCh <-chan struct{}) { } } func (psa *PolicyStatusAggregator) aggregate() { + // As mutation and validation are handled seperately + // ideally we need to combine the exection time from both for a policy + // but its tricky to detect here the type of rules policy contains + // so we dont combine the results, but instead compute the execution time for + // mutation & validation rules seperately for r := range psa.ch { glog.V(4).Infof("recieved policy stats %v", r) + if err := psa.updateStats(r); err != nil { + glog.Info("Failed to update stats for policy %s: %v", r.PolicyName, err) + } } + +} + +func (psa *PolicyStatusAggregator) updateStats(stats PolicyStat) error { + func() { + glog.V(4).Infof("lock updates for policy name %s", stats.PolicyName) + // Lock the update for policy + psa.policyUpdateData.Store(stats.PolicyName, struct{}{}) + }() + defer func() { + glog.V(4).Infof("Unlock updates for policy name %s", stats.PolicyName) + psa.policyUpdateData.Delete(stats.PolicyName) + }() + // get policy + policy, err := psa.pLister.Get(stats.PolicyName) + if err != nil { + glog.V(4).Infof("failed to get policy %s. Unable to update violation count: %v", stats.PolicyName, err) + return err + } + glog.V(4).Infof("updating stats for policy %s", policy.Name) + // update the statistics + // policy.Status + + return nil } type PolicyStatusInterface interface { SendStat(stat PolicyStat) - UpdateViolationCount(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error + UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error } type PolicyStat struct { @@ -79,14 +119,30 @@ func (psa *PolicyStatusAggregator) SendStat(stat PolicyStat) { } //UpdateViolationCount updates the active violation count -func (psa *PolicyStatusAggregator) UpdateViolationCount(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error { +func (psa *PolicyStatusAggregator) UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error { + func() { + glog.V(4).Infof("lock updates for policy name %s", policyName) + // Lock the update for policy + psa.policyUpdateData.Store(policyName, struct{}{}) + }() + defer func() { + glog.V(4).Infof("Unlock updates for policy name %s", policyName) + psa.policyUpdateData.Delete(policyName) + }() + // get policy + policy, err := psa.pLister.Get(policyName) + if err != nil { + glog.V(4).Infof("failed to get policy %s. Unable to update violation count: %v", policyName, err) + return err + } + newStatus := calculateStatus(pvList) - if reflect.DeepEqual(newStatus, p.Status) { + if reflect.DeepEqual(newStatus, policy.Status) { // no update to status return nil } // update status - newPolicy := p + newPolicy := policy newPolicy.Status = newStatus return psa.psControl.UpdatePolicyStatus(newPolicy) From 3f876e6f4681bf7b906834781685ac5371c99160 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 15:13:52 -0700 Subject: [PATCH 51/66] update status v1 --- pkg/api/kyverno/v1alpha1/types.go | 12 ++++- pkg/policy/controller.go | 2 +- pkg/policy/status.go | 83 ++++++++++++++++++++++--------- 3 files changed, 72 insertions(+), 25 deletions(-) diff --git a/pkg/api/kyverno/v1alpha1/types.go b/pkg/api/kyverno/v1alpha1/types.go index 5597f97e23..691a0c1f11 100644 --- a/pkg/api/kyverno/v1alpha1/types.go +++ b/pkg/api/kyverno/v1alpha1/types.go @@ -1,6 +1,8 @@ package v1alpha1 import ( + "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -89,7 +91,15 @@ type CloneFrom struct { //PolicyStatus provides status for violations type PolicyStatus struct { - Violations int `json:"violations"` + ViolationCount int `json:"violationCount"` + // Count of rules that were applied + RulesAppliedCount int `json:"rulesAppliedCount"` + // Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules + ResourcesBlockedCount int `json:"resourcesBlockedCount"` + // average time required to process the policy Mutation rules on a resource + AvgExecutionTimeMutation time.Duration `json:"averageMutationExecutionTime"` + // average time required to process the policy Validation rules on a resource + AvgExecutionTimeValidation time.Duration `json:"averageValidationExecutionTime"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 0457423250..02cc1c072e 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -119,7 +119,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. pc.rm = NewResourceManager(30) // aggregator - pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient, pInformer.Lister()) + pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient, pInformer) return &pc, nil } diff --git a/pkg/policy/status.go b/pkg/policy/status.go index fe8a77512a..f19961dba5 100644 --- a/pkg/policy/status.go +++ b/pkg/policy/status.go @@ -1,6 +1,7 @@ package policy import ( + "fmt" "reflect" "sync" "time" @@ -8,48 +9,55 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" + kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/cache" ) -type PolicyStatus struct { - // average time required to process the policy rules on a resource - avgExecutionTime time.Duration - // Count of rules that were applied succesfully - rulesAppliedCount int - // Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules - resourcesBlockedCount int - // Count of the resource for whom the mutation rules were applied succesfully - resourcesMutatedCount int -} +// type PolicyStatus struct { +// // average time required to process the policy rules on a resource +// avgExecutionTime time.Duration +// // Count of rules that were applied succesfully +// rulesAppliedCount int +// // Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules +// resourcesBlockedCount int +// // Count of the resource for whom the mutation rules were applied succesfully +// resourcesMutatedCount int +// } +//PolicyStatusAggregator stores information abt aggregation type PolicyStatusAggregator struct { // time since we start aggregating the stats startTime time.Time // channel to recieve stats ch chan PolicyStat - // update polict status + // update policy status psControl PStatusControlInterface // pLister can list/get policy from the shared informer's store pLister kyvernolister.PolicyLister + // pListerSynced returns true if the Policy store has been synced at least once + pListerSynced cache.InformerSynced // UpdateViolationCount and SendStat can update same policy status // we need to sync the updates using policyName policyUpdateData sync.Map } //NewPolicyStatAggregator returns a new policy status -func NewPolicyStatAggregator(client *kyvernoclient.Clientset, pLister kyvernolister.PolicyLister) *PolicyStatusAggregator { +func NewPolicyStatAggregator(client *kyvernoclient.Clientset, pInformer kyvernoinformer.PolicyInformer) *PolicyStatusAggregator { psa := PolicyStatusAggregator{ startTime: time.Now(), ch: make(chan PolicyStat), } - psa.pLister = pLister + psa.pLister = pInformer.Lister() + psa.pListerSynced = pInformer.Informer().HasSynced psa.psControl = PSControl{Client: client} //TODO: add WaitGroup return &psa } +//Run begins aggregator func (psa *PolicyStatusAggregator) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() glog.V(4).Info("Started aggregator for policy status stats") @@ -69,7 +77,7 @@ func (psa *PolicyStatusAggregator) aggregate() { for r := range psa.ch { glog.V(4).Infof("recieved policy stats %v", r) if err := psa.updateStats(r); err != nil { - glog.Info("Failed to update stats for policy %s: %v", r.PolicyName, err) + glog.Infof("Failed to update stats for policy %s: %v", r.PolicyName, err) } } @@ -77,32 +85,59 @@ func (psa *PolicyStatusAggregator) aggregate() { func (psa *PolicyStatusAggregator) updateStats(stats PolicyStat) error { func() { - glog.V(4).Infof("lock updates for policy name %s", stats.PolicyName) + glog.V(4).Infof("lock updates for policy %s", stats.PolicyName) // Lock the update for policy psa.policyUpdateData.Store(stats.PolicyName, struct{}{}) }() defer func() { - glog.V(4).Infof("Unlock updates for policy name %s", stats.PolicyName) + glog.V(4).Infof("Unlock updates for policy %s", stats.PolicyName) psa.policyUpdateData.Delete(stats.PolicyName) }() + + // //wait for cache sync + // if !cache.WaitForCacheSync(nil, psa.pListerSynced) { + // glog.Infof("unable to sync cache for policy informer") + // return nil + // } // get policy policy, err := psa.pLister.Get(stats.PolicyName) if err != nil { glog.V(4).Infof("failed to get policy %s. Unable to update violation count: %v", stats.PolicyName, err) return err } + newpolicy := policy + fmt.Println(newpolicy.ResourceVersion) + newpolicy.Status = kyverno.PolicyStatus{} glog.V(4).Infof("updating stats for policy %s", policy.Name) - // update the statistics - // policy.Status - - return nil + // rules applied count + newpolicy.Status.RulesAppliedCount = newpolicy.Status.RulesAppliedCount + stats.RulesAppliedCount + // resource blocked count + if stats.ResourceBlocked { + policy.Status.ResourcesBlockedCount++ + } + var zeroDuration time.Duration + if newpolicy.Status.AvgExecutionTimeMutation != zeroDuration { + // avg execution time for mutation rules + newpolicy.Status.AvgExecutionTimeMutation = (newpolicy.Status.AvgExecutionTimeMutation + stats.MutationExecutionTime) / 2 + } else { + newpolicy.Status.AvgExecutionTimeMutation = stats.MutationExecutionTime + } + if policy.Status.AvgExecutionTimeValidation != zeroDuration { + // avg execution time for validation rules + newpolicy.Status.AvgExecutionTimeValidation = (newpolicy.Status.AvgExecutionTimeValidation + stats.ValidationExecutionTime) / 2 + } else { + newpolicy.Status.AvgExecutionTimeValidation = stats.ValidationExecutionTime + } + return psa.psControl.UpdatePolicyStatus(newpolicy) } +//PolicyStatusInterface provides methods to modify policyStatus type PolicyStatusInterface interface { SendStat(stat PolicyStat) UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error } +//PolicyStat stored stats for policy type PolicyStat struct { PolicyName string MutationExecutionTime time.Duration @@ -121,12 +156,12 @@ func (psa *PolicyStatusAggregator) SendStat(stat PolicyStat) { //UpdateViolationCount updates the active violation count func (psa *PolicyStatusAggregator) UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error { func() { - glog.V(4).Infof("lock updates for policy name %s", policyName) + glog.V(4).Infof("lock updates for policy %s", policyName) // Lock the update for policy psa.policyUpdateData.Store(policyName, struct{}{}) }() defer func() { - glog.V(4).Infof("Unlock updates for policy name %s", policyName) + glog.V(4).Infof("Unlock updates for policy %s", policyName) psa.policyUpdateData.Delete(policyName) }() // get policy @@ -139,6 +174,7 @@ func (psa *PolicyStatusAggregator) UpdateViolationCount(policyName string, pvLis newStatus := calculateStatus(pvList) if reflect.DeepEqual(newStatus, policy.Status) { // no update to status + glog.V(4).Infof("no changes in policy violation count for policy %s", policy.Name) return nil } // update status @@ -151,7 +187,7 @@ func (psa *PolicyStatusAggregator) UpdateViolationCount(policyName string, pvLis func calculateStatus(pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus { violationCount := len(pvList) status := kyverno.PolicyStatus{ - Violations: violationCount, + ViolationCount: violationCount, } return status } @@ -166,6 +202,7 @@ type PStatusControlInterface interface { UpdatePolicyStatus(newPolicy *kyverno.Policy) error } +//PSControl allows update for policy status type PSControl struct { Client kyvernoclient.Interface } From dc47132ade87df1f713da32726659123983abf53 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 16:40:20 -0700 Subject: [PATCH 52/66] update policy status --- pkg/api/kyverno/v1alpha1/types.go | 6 +- pkg/policy/controller.go | 26 +++- pkg/policy/status.go | 211 +++++++++++------------------- pkg/utils/util.go | 8 ++ pkg/webhooks/mutation.go | 6 +- pkg/webhooks/validation.go | 6 +- 6 files changed, 117 insertions(+), 146 deletions(-) diff --git a/pkg/api/kyverno/v1alpha1/types.go b/pkg/api/kyverno/v1alpha1/types.go index 691a0c1f11..44769bd446 100644 --- a/pkg/api/kyverno/v1alpha1/types.go +++ b/pkg/api/kyverno/v1alpha1/types.go @@ -1,8 +1,6 @@ package v1alpha1 import ( - "time" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -97,9 +95,9 @@ type PolicyStatus struct { // Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules ResourcesBlockedCount int `json:"resourcesBlockedCount"` // average time required to process the policy Mutation rules on a resource - AvgExecutionTimeMutation time.Duration `json:"averageMutationExecutionTime"` + AvgExecutionTimeMutation string `json:"averageMutationExecutionTime"` // average time required to process the policy Validation rules on a resource - AvgExecutionTimeValidation time.Duration `json:"averageValidationExecutionTime"` + AvgExecutionTimeValidation string `json:"averageValidationExecutionTime"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 02cc1c072e..fccbed13c5 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -119,7 +119,8 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. pc.rm = NewResourceManager(30) // aggregator - pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient, pInformer) + // pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient, pInformer) + pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient) return &pc, nil } @@ -392,6 +393,8 @@ func (pc *PolicyController) syncPolicy(key string) error { policy, err := pc.pLister.Get(key) if errors.IsNotFound(err) { glog.V(2).Infof("Policy %v has been deleted", key) + // remove the recorded stats for the policy + pc.statusAggregator.RemovePolicyStats(key) return nil } @@ -412,15 +415,15 @@ func (pc *PolicyController) syncPolicy(key string) error { // report errors pc.report(policyInfos) // fetch the policy again via the aggreagator to remain consistent - return pc.statusAggregator.UpdateViolationCount(p.Name, pvList) - // return pc.syncStatusOnly(p, pvList) + // return pc.statusAggregator.UpdateViolationCount(p.Name, pvList) + return pc.syncStatusOnly(p, pvList) } //syncStatusOnly updates the policy status subresource // status: // - violations : (count of the resources that violate this policy ) func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno.PolicyViolation) error { - newStatus := calculateStatus(pvList) + newStatus := pc.calculateStatus(p.Name, pvList) if reflect.DeepEqual(newStatus, p.Status) { // no update to status return nil @@ -432,6 +435,21 @@ func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno. return err } +func (pc *PolicyController) calculateStatus(policyName string, pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus { + violationCount := len(pvList) + status := kyverno.PolicyStatus{ + ViolationCount: violationCount, + } + // get stats + stats := pc.statusAggregator.GetPolicyStats(policyName) + if stats != (PolicyStatInfo{}) { + status.RulesAppliedCount = stats.RulesAppliedCount + status.ResourcesBlockedCount = stats.ResourceBlocked + status.AvgExecutionTimeMutation = stats.MutationExecutionTime.String() + status.AvgExecutionTimeValidation = stats.ValidationExecutionTime.String() + } + return status +} func (pc *PolicyController) getPolicyViolationsForPolicy(p *kyverno.Policy) ([]*kyverno.PolicyViolation, error) { // List all PolicyViolation to find those we own but that no longer match our // selector. They will be orphaned by ClaimPolicyViolation(). diff --git a/pkg/policy/status.go b/pkg/policy/status.go index f19961dba5..60a6a6dbf0 100644 --- a/pkg/policy/status.go +++ b/pkg/policy/status.go @@ -1,59 +1,38 @@ package policy import ( - "fmt" - "reflect" "sync" "time" "github.com/golang/glog" - kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" - kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1" - kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/cache" ) -// type PolicyStatus struct { -// // average time required to process the policy rules on a resource -// avgExecutionTime time.Duration -// // Count of rules that were applied succesfully -// rulesAppliedCount int -// // Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules -// resourcesBlockedCount int -// // Count of the resource for whom the mutation rules were applied succesfully -// resourcesMutatedCount int -// } - //PolicyStatusAggregator stores information abt aggregation type PolicyStatusAggregator struct { // time since we start aggregating the stats startTime time.Time // channel to recieve stats ch chan PolicyStat - // update policy status - psControl PStatusControlInterface - // pLister can list/get policy from the shared informer's store - pLister kyvernolister.PolicyLister - // pListerSynced returns true if the Policy store has been synced at least once - pListerSynced cache.InformerSynced - // UpdateViolationCount and SendStat can update same policy status - // we need to sync the updates using policyName - policyUpdateData sync.Map + //TODO: lock based on key, possibly sync.Map ? + //sync RW for policyData + mux sync.RWMutex + // stores aggregated stats for policy + policyData map[string]PolicyStatInfo } //NewPolicyStatAggregator returns a new policy status -func NewPolicyStatAggregator(client *kyvernoclient.Clientset, pInformer kyvernoinformer.PolicyInformer) *PolicyStatusAggregator { +func NewPolicyStatAggregator(client *kyvernoclient.Clientset, + +// pInformer kyvernoinformer.PolicyInformer +) *PolicyStatusAggregator { psa := PolicyStatusAggregator{ - startTime: time.Now(), - ch: make(chan PolicyStat), + startTime: time.Now(), + ch: make(chan PolicyStat), + policyData: map[string]PolicyStatInfo{}, } - psa.pLister = pInformer.Lister() - psa.pListerSynced = pInformer.Informer().HasSynced - psa.psControl = PSControl{Client: client} - //TODO: add WaitGroup return &psa } @@ -65,10 +44,11 @@ func (psa *PolicyStatusAggregator) Run(workers int, stopCh <-chan struct{}) { glog.V(4).Info("Shutting down aggregator for policy status stats") }() for i := 0; i < workers; i++ { - go wait.Until(psa.aggregate, time.Second, stopCh) + go wait.Until(psa.process, time.Second, stopCh) } } -func (psa *PolicyStatusAggregator) aggregate() { + +func (psa *PolicyStatusAggregator) process() { // As mutation and validation are handled seperately // ideally we need to combine the exection time from both for a policy // but its tricky to detect here the type of rules policy contains @@ -76,74 +56,96 @@ func (psa *PolicyStatusAggregator) aggregate() { // mutation & validation rules seperately for r := range psa.ch { glog.V(4).Infof("recieved policy stats %v", r) - if err := psa.updateStats(r); err != nil { - glog.Infof("Failed to update stats for policy %s: %v", r.PolicyName, err) - } + // if err := psa.updateStats(r); err != nil { + // glog.Infof("Failed to update stats for policy %s: %v", r.PolicyName, err) + // } + psa.aggregate(r) } - } -func (psa *PolicyStatusAggregator) updateStats(stats PolicyStat) error { +func (psa *PolicyStatusAggregator) aggregate(ps PolicyStat) { func() { - glog.V(4).Infof("lock updates for policy %s", stats.PolicyName) - // Lock the update for policy - psa.policyUpdateData.Store(stats.PolicyName, struct{}{}) + glog.V(4).Infof("write lock update policy %s", ps.PolicyName) + psa.mux.Lock() }() defer func() { - glog.V(4).Infof("Unlock updates for policy %s", stats.PolicyName) - psa.policyUpdateData.Delete(stats.PolicyName) + glog.V(4).Infof("write Unlock update policy %s", ps.PolicyName) + psa.mux.Unlock() }() - - // //wait for cache sync - // if !cache.WaitForCacheSync(nil, psa.pListerSynced) { - // glog.Infof("unable to sync cache for policy informer") - // return nil - // } - // get policy - policy, err := psa.pLister.Get(stats.PolicyName) - if err != nil { - glog.V(4).Infof("failed to get policy %s. Unable to update violation count: %v", stats.PolicyName, err) - return err + info, ok := psa.policyData[ps.PolicyName] + if !ok { + psa.policyData[ps.PolicyName] = ps.Stats + glog.V(4).Infof("added stats for policy %s", ps.PolicyName) + return } - newpolicy := policy - fmt.Println(newpolicy.ResourceVersion) - newpolicy.Status = kyverno.PolicyStatus{} - glog.V(4).Infof("updating stats for policy %s", policy.Name) - // rules applied count - newpolicy.Status.RulesAppliedCount = newpolicy.Status.RulesAppliedCount + stats.RulesAppliedCount - // resource blocked count - if stats.ResourceBlocked { - policy.Status.ResourcesBlockedCount++ + // aggregate + info.RulesAppliedCount = info.RulesAppliedCount + ps.Stats.RulesAppliedCount + if ps.Stats.ResourceBlocked == 1 { + info.ResourceBlocked++ } var zeroDuration time.Duration - if newpolicy.Status.AvgExecutionTimeMutation != zeroDuration { - // avg execution time for mutation rules - newpolicy.Status.AvgExecutionTimeMutation = (newpolicy.Status.AvgExecutionTimeMutation + stats.MutationExecutionTime) / 2 + if info.MutationExecutionTime != zeroDuration { + info.MutationExecutionTime = (info.MutationExecutionTime + ps.Stats.MutationExecutionTime) / 2 + glog.V(4).Infof("updated avg mutation time %v", info.MutationExecutionTime) } else { - newpolicy.Status.AvgExecutionTimeMutation = stats.MutationExecutionTime + info.MutationExecutionTime = ps.Stats.MutationExecutionTime } - if policy.Status.AvgExecutionTimeValidation != zeroDuration { - // avg execution time for validation rules - newpolicy.Status.AvgExecutionTimeValidation = (newpolicy.Status.AvgExecutionTimeValidation + stats.ValidationExecutionTime) / 2 + if info.ValidationExecutionTime != zeroDuration { + info.ValidationExecutionTime = (info.ValidationExecutionTime + ps.Stats.ValidationExecutionTime) / 2 + glog.V(4).Infof("updated avg validation time %v", info.ValidationExecutionTime) } else { - newpolicy.Status.AvgExecutionTimeValidation = stats.ValidationExecutionTime + info.ValidationExecutionTime = ps.Stats.ValidationExecutionTime } - return psa.psControl.UpdatePolicyStatus(newpolicy) + // update + psa.policyData[ps.PolicyName] = info + glog.V(4).Infof("updated stats for policy %s", ps.PolicyName) +} + +//GetPolicyStats returns the policy stats +func (psa *PolicyStatusAggregator) GetPolicyStats(policyName string) PolicyStatInfo { + func() { + glog.V(4).Infof("read lock update policy %s", policyName) + psa.mux.RLock() + }() + defer func() { + glog.V(4).Infof("read Unlock update policy %s", policyName) + psa.mux.RUnlock() + }() + glog.V(4).Infof("read stats for policy %s", policyName) + return psa.policyData[policyName] +} + +//RemovePolicyStats rmves policy stats records +func (psa *PolicyStatusAggregator) RemovePolicyStats(policyName string) { + func() { + glog.V(4).Infof("write lock update policy %s", policyName) + psa.mux.Lock() + }() + defer func() { + glog.V(4).Infof("write Unlock update policy %s", policyName) + psa.mux.Unlock() + }() + glog.V(4).Infof("removing stats for policy %s", policyName) + delete(psa.policyData, policyName) } //PolicyStatusInterface provides methods to modify policyStatus type PolicyStatusInterface interface { SendStat(stat PolicyStat) - UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error + // UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error } //PolicyStat stored stats for policy type PolicyStat struct { - PolicyName string + PolicyName string + Stats PolicyStatInfo +} + +type PolicyStatInfo struct { MutationExecutionTime time.Duration ValidationExecutionTime time.Duration RulesAppliedCount int - ResourceBlocked bool + ResourceBlocked int } //SendStat sends the stat information for aggregation @@ -153,62 +155,7 @@ func (psa *PolicyStatusAggregator) SendStat(stat PolicyStat) { psa.ch <- stat } -//UpdateViolationCount updates the active violation count -func (psa *PolicyStatusAggregator) UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error { - func() { - glog.V(4).Infof("lock updates for policy %s", policyName) - // Lock the update for policy - psa.policyUpdateData.Store(policyName, struct{}{}) - }() - defer func() { - glog.V(4).Infof("Unlock updates for policy %s", policyName) - psa.policyUpdateData.Delete(policyName) - }() - // get policy - policy, err := psa.pLister.Get(policyName) - if err != nil { - glog.V(4).Infof("failed to get policy %s. Unable to update violation count: %v", policyName, err) - return err - } - - newStatus := calculateStatus(pvList) - if reflect.DeepEqual(newStatus, policy.Status) { - // no update to status - glog.V(4).Infof("no changes in policy violation count for policy %s", policy.Name) - return nil - } - // update status - newPolicy := policy - newPolicy.Status = newStatus - - return psa.psControl.UpdatePolicyStatus(newPolicy) -} - -func calculateStatus(pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus { - violationCount := len(pvList) - status := kyverno.PolicyStatus{ - ViolationCount: violationCount, - } - return status -} - //GetPolicyStatusAggregator returns interface to send policy status stats func (pc *PolicyController) GetPolicyStatusAggregator() PolicyStatusInterface { return pc.statusAggregator } - -//PStatusControlInterface Provides interface to operate on policy status -type PStatusControlInterface interface { - UpdatePolicyStatus(newPolicy *kyverno.Policy) error -} - -//PSControl allows update for policy status -type PSControl struct { - Client kyvernoclient.Interface -} - -//UpdatePolicyStatus update policy status -func (c PSControl) UpdatePolicyStatus(newPolicy *kyverno.Policy) error { - _, err := c.Client.KyvernoV1alpha1().Policies().UpdateStatus(newPolicy) - return err -} diff --git a/pkg/utils/util.go b/pkg/utils/util.go index e77e1a55db..eb548c328d 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -86,3 +86,11 @@ func NewKubeClient(config *rest.Config) (kubernetes.Interface, error) { } return kclient, nil } + +//Btoi converts boolean to int +func Btoi(b bool) int { + if b { + return 1 + } + return 0 +} diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index f706020627..620ab3242c 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -20,14 +20,14 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool gatherStat := func(policyName string, er engine.EngineResponse) { ps := policyctr.PolicyStat{} ps.PolicyName = policyName - ps.MutationExecutionTime = er.ExecutionTime - ps.RulesAppliedCount = er.RulesAppliedCount + ps.Stats.MutationExecutionTime = er.ExecutionTime + ps.Stats.RulesAppliedCount = er.RulesAppliedCount policyStats = append(policyStats, ps) } // send stats for aggregation sendStat := func(blocked bool) { for _, stat := range policyStats { - stat.ResourceBlocked = blocked + stat.Stats.ResourceBlocked = utils.Btoi(blocked) //SEND ws.policyStatus.SendStat(stat) } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 088694bd67..14105ed0f3 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -23,14 +23,14 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, res gatherStat := func(policyName string, er engine.EngineResponse) { ps := policyctr.PolicyStat{} ps.PolicyName = policyName - ps.ValidationExecutionTime = er.ExecutionTime - ps.RulesAppliedCount = er.RulesAppliedCount + ps.Stats.ValidationExecutionTime = er.ExecutionTime + ps.Stats.RulesAppliedCount = er.RulesAppliedCount policyStats = append(policyStats, ps) } // send stats for aggregation sendStat := func(blocked bool) { for _, stat := range policyStats { - stat.ResourceBlocked = blocked + stat.Stats.ResourceBlocked = utils.Btoi(blocked) //SEND ws.policyStatus.SendStat(stat) } From 4f309480afadd8e5de6d88996ed6c2a5442164aa Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 16:57:19 -0700 Subject: [PATCH 53/66] report stats from existing resources --- pkg/policy/apply.go | 42 +++++++++++++++++++++++++++++++++++++++--- pkg/policy/existing.go | 6 +++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go index ec779c330a..6a1b5db9ee 100644 --- a/pkg/policy/apply.go +++ b/pkg/policy/apply.go @@ -14,7 +14,20 @@ import ( // applyPolicy applies policy on a resource //TODO: generation rules -func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (info.PolicyInfo, error) { +func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (info.PolicyInfo, error) { + var ps PolicyStat + gatherStat := func(policyName string, er engine.EngineResponse) { + // ps := policyctr.PolicyStat{} + ps.PolicyName = policyName + ps.Stats.ValidationExecutionTime = er.ExecutionTime + ps.Stats.RulesAppliedCount = er.RulesAppliedCount + } + // send stats for aggregation + sendStat := func(blocked bool) { + //SEND + policyStatus.SendStat(ps) + } + startTime := time.Now() glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime) defer func() { @@ -24,7 +37,7 @@ func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (inf policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) //MUTATION - mruleInfos, err := mutation(policy, resource) + mruleInfos, err := mutation(policy, resource, policyStatus) policyInfo.AddRuleInfos(mruleInfos) if err != nil { return policyInfo, err @@ -35,13 +48,36 @@ func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (inf if len(engineResponse.RuleInfos) != 0 { policyInfo.AddRuleInfos(engineResponse.RuleInfos) } + // gather stats + gatherStat(policy.Name, engineResponse) + //send stats + sendStat(false) //TODO: GENERATION return policyInfo, nil } -func mutation(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) { +func mutation(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) ([]info.RuleInfo, error) { + var ps PolicyStat + // gather stats from the engine response + gatherStat := func(policyName string, er engine.EngineResponse) { + // ps := policyctr.PolicyStat{} + ps.PolicyName = policyName + ps.Stats.MutationExecutionTime = er.ExecutionTime + ps.Stats.RulesAppliedCount = er.RulesAppliedCount + } + // send stats for aggregation + sendStat := func(blocked bool) { + //SEND + policyStatus.SendStat(ps) + } + engineResponse := engine.Mutate(policy, resource) + // gather stats + gatherStat(policy.Name, engineResponse) + //send stats + sendStat(false) + patches := engineResponse.Patches ruleInfos := engineResponse.RuleInfos if len(ruleInfos) == 0 { diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index eaa5d0657d..092d657736 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -29,7 +29,7 @@ func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []in } // apply the policy on each glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) - policyInfo := applyPolicyOnResource(policy, resource) + policyInfo := applyPolicyOnResource(policy, resource, pc.statusAggregator) policyInfos = append(policyInfos, *policyInfo) // post-processing, register the resource as processed pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) @@ -37,8 +37,8 @@ func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []in return policyInfos } -func applyPolicyOnResource(policy kyverno.Policy, resource unstructured.Unstructured) *info.PolicyInfo { - policyInfo, err := applyPolicy(policy, resource) +func applyPolicyOnResource(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) *info.PolicyInfo { + policyInfo, err := applyPolicy(policy, resource, policyStatus) if err != nil { glog.V(4).Infof("failed to process policy %s on resource %s/%s/%s: %v", policy.GetName(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) return nil From dcc851dee2bc862c2762bafa5a8486ad0409c967 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Tue, 20 Aug 2019 17:01:47 -0700 Subject: [PATCH 54/66] fix pr comments --- main.go | 20 ++++++++++++---- pkg/dclient/client.go | 8 +++---- pkg/dclient/utils.go | 2 +- pkg/policy/controller.go | 42 +++++++++++++++++++++++----------- pkg/webhooks/webhookManager.go | 29 +++++++++++++++++++++-- 5 files changed, 76 insertions(+), 25 deletions(-) diff --git a/main.go b/main.go index 76e2d79e3f..d89fac5be9 100644 --- a/main.go +++ b/main.go @@ -13,8 +13,9 @@ import ( "github.com/nirmata/kyverno/pkg/namespace" "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/policyviolation" + "github.com/nirmata/kyverno/pkg/utils" "github.com/nirmata/kyverno/pkg/webhooks" - "k8s.io/client-go/informers" + kubeinformer "k8s.io/client-go/informers" "k8s.io/sample-controller/pkg/signals" ) @@ -27,6 +28,7 @@ var ( webhookTimeout int ) +// TODO: tune resync time differently for each informer const defaultReSyncTime = 10 * time.Second func main() { @@ -48,6 +50,7 @@ func main() { if err != nil { glog.Fatalf("Error creating client: %v\n", err) } + // DYNAMIC CLIENT // - client for all registered resources client, err := client.NewClient(clientConfig) @@ -65,9 +68,16 @@ func main() { // - generate event with retry egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies()) - // mutatingWebhookConfiguration Informer - kubeInformer := informers.NewSharedInformerFactory(client.Kclient, defaultReSyncTime) - mutatingWebhookConfigurationLister := kubeInformer.Admissionregistration().V1beta1().MutatingWebhookConfigurations().Lister() + kubeClient, err := utils.NewKubeClient(clientConfig) + if err != nil { + glog.Fatalf("Error creating kubernetes client: %v\n", err) + } + + // - cache resync time: 10 seconds + kubeInformer := kubeinformer.NewSharedInformerFactoryWithOptions(kubeClient, defaultReSyncTime) + + // MutatingWebhookConfiguration Informer + mutatingWebhookConfigurationInformer := kubeInformer.Admissionregistration().V1beta1().MutatingWebhookConfigurations() tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { @@ -91,7 +101,7 @@ func main() { // - process policy on existing resources // - status: violation count - pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, mutatingWebhookConfigurationLister, webhookRegistrationClient) + pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, mutatingWebhookConfigurationInformer, webhookRegistrationClient) if err != nil { glog.Fatalf("error creating policy controller: %v\n", err) } diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go index 4c46dd13f9..50a1f1d3e2 100644 --- a/pkg/dclient/client.go +++ b/pkg/dclient/client.go @@ -31,7 +31,7 @@ type Client struct { client dynamic.Interface cachedClient discovery.CachedDiscoveryInterface clientConfig *rest.Config - Kclient kubernetes.Interface + kclient kubernetes.Interface DiscoveryClient IDiscovery } @@ -48,7 +48,7 @@ func NewClient(config *rest.Config) (*Client, error) { client := Client{ client: dclient, clientConfig: config, - Kclient: kclient, + kclient: kclient, } // Set discovery client // @@ -75,12 +75,12 @@ func (c *Client) GetKubePolicyDeployment() (*apps.Deployment, error) { //TODO: can we use dynamic client to fetch the typed interface // or generate a kube client value to access the interface func (c *Client) GetEventsInterface() (event.EventInterface, error) { - return c.Kclient.CoreV1().Events(""), nil + return c.kclient.CoreV1().Events(""), nil } //GetCSRInterface provides type interface for CSR func (c *Client) GetCSRInterface() (csrtype.CertificateSigningRequestInterface, error) { - return c.Kclient.CertificatesV1beta1().CertificateSigningRequests(), nil + return c.kclient.CertificatesV1beta1().CertificateSigningRequests(), nil } func (c *Client) getInterface(resource string) dynamic.NamespaceableResourceInterface { diff --git a/pkg/dclient/utils.go b/pkg/dclient/utils.go index d82500f0d0..6492187038 100644 --- a/pkg/dclient/utils.go +++ b/pkg/dclient/utils.go @@ -32,7 +32,7 @@ func NewMockClient(scheme *runtime.Scheme, objects ...runtime.Object) (*Client, kclient := kubernetesfake.NewSimpleClientset(objects...) return &Client{ client: client, - Kclient: kclient, + kclient: kclient, }, nil } diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 6e816621cb..6746022b8e 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -27,8 +27,9 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" + webhookinformer "k8s.io/client-go/informers/admissionregistration/v1beta1" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - v1beta1 "k8s.io/client-go/listers/admissionregistration/v1beta1" + webhooklister "k8s.io/client-go/listers/admissionregistration/v1beta1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" @@ -67,8 +68,8 @@ type PolicyController struct { pListerSynced cache.InformerSynced // pvListerSynced returns true if the Policy store has been synced at least once pvListerSynced cache.InformerSynced - // mutationwebhookInformer can list/get mutatingwebhookconfigurations - mutationwebhookInformer v1beta1.MutatingWebhookConfigurationLister + // mutationwebhookLister can list/get mutatingwebhookconfigurations + mutationwebhookLister webhooklister.MutatingWebhookConfigurationLister // WebhookRegistrationClient webhookRegistrationClient *webhooks.WebhookRegistrationClient // Resource manager, manages the mapping for already processed resource @@ -79,7 +80,7 @@ type PolicyController struct { // NewPolicyController create a new PolicyController func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer kyvernoinformer.PolicyInformer, pvInformer kyvernoinformer.PolicyViolationInformer, - eventGen event.Interface, mutationwebhookInformer v1beta1.MutatingWebhookConfigurationLister, webhookRegistrationClient *webhooks.WebhookRegistrationClient) (*PolicyController, error) { + eventGen event.Interface, webhookInformer webhookinformer.MutatingWebhookConfigurationInformer, webhookRegistrationClient *webhooks.WebhookRegistrationClient) (*PolicyController, error) { // Event broad caster eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) @@ -95,7 +96,6 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. eventGen: eventGen, eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "policy_controller"}), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "policy"), - mutationwebhookInformer: mutationwebhookInformer, webhookRegistrationClient: webhookRegistrationClient, } @@ -121,6 +121,8 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client. pc.pListerSynced = pInformer.Informer().HasSynced pc.pvListerSynced = pInformer.Informer().HasSynced + pc.mutationwebhookLister = webhookInformer.Lister() + // resource manager // rebuild after 300 seconds/ 5 mins //TODO: pass the time in seconds instead of converting it internally @@ -394,7 +396,9 @@ func (pc *PolicyController) syncPolicy(key string) error { policy, err := pc.pLister.Get(key) if errors.IsNotFound(err) { glog.V(2).Infof("Policy %v has been deleted", key) - err = pc.handleWebhookRegistration(true) + if err := pc.handleWebhookRegistration(true, nil); err != nil { + glog.Errorln(err) + } return err } @@ -402,7 +406,7 @@ func (pc *PolicyController) syncPolicy(key string) error { return err } - if err := pc.handleWebhookRegistration(false); err != nil { + if err := pc.handleWebhookRegistration(false, policy); err != nil { glog.Errorln(err) } @@ -421,28 +425,40 @@ func (pc *PolicyController) syncPolicy(key string) error { return pc.syncStatusOnly(p, pvList) } -func (pc *PolicyController) handleWebhookRegistration(emptyPolicy bool) error { +// TODO: here checks mutatingwebhook only +// as 'kubectl scale' is not funtional with validatingwebhook +// refer to https://github.com/nirmata/kyverno/issues/250 +func (pc *PolicyController) handleWebhookRegistration(delete bool, policy *kyverno.Policy) error { + policies, _ := pc.pLister.List(labels.NewSelector()) selector := &metav1.LabelSelector{MatchLabels: config.KubePolicyAppLabels} webhookSelector, err := metav1.LabelSelectorAsSelector(selector) if err != nil { return fmt.Errorf("invalid label selector: %v", err) } - list, err := pc.mutationwebhookInformer.List(webhookSelector) + webhookList, err := pc.mutationwebhookLister.List(webhookSelector) if err != nil { return fmt.Errorf("failed to list mutatingwebhookconfigurations, err %v", err) } - if emptyPolicy { - // deregister webhookconfigurations it it exists - if list != nil { + if delete { + if webhookList == nil { + return nil + } + + // webhook exist, deregister webhookconfigurations on condition + // check empty policy first, then rule type in terms of O(time) + if policies == nil { glog.V(3).Infoln("No policy found in the cluster, deregistering webhook") pc.webhookRegistrationClient.DeregisterMutatingWebhook() + } else if !webhooks.HasMutateOrValidatePolicies(policies) { + glog.V(3).Infoln("No muatate/validate policy found in the cluster, deregistering webhook") + pc.webhookRegistrationClient.DeregisterMutatingWebhook() } return nil } - if list == nil { + if webhookList == nil && webhooks.HasMutateOrValidate(*policy) { glog.V(3).Infoln("Found policy without mutatingwebhook, registering webhook") pc.webhookRegistrationClient.RegisterMutatingWebhook() } diff --git a/pkg/webhooks/webhookManager.go b/pkg/webhooks/webhookManager.go index 5a498f68fa..e808bb53aa 100644 --- a/pkg/webhooks/webhookManager.go +++ b/pkg/webhooks/webhookManager.go @@ -1,6 +1,8 @@ package webhooks import ( + "reflect" + "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" v1beta1 "k8s.io/api/admission/v1beta1" @@ -26,6 +28,10 @@ func (ws *WebhookServer) manageWebhookConfigurations(policy kyverno.Policy, op v } func (ws *WebhookServer) registerWebhookConfigurations(policy kyverno.Policy) error { + if !HasMutateOrValidate(policy) { + return nil + } + if !ws.webhookRegistrationClient.MutationRegistered.IsSet() { if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil { return err @@ -39,11 +45,30 @@ func (ws *WebhookServer) registerWebhookConfigurations(policy kyverno.Policy) er func (ws *WebhookServer) deregisterWebhookConfigurations(policy kyverno.Policy) error { policies, _ := ws.pLister.List(labels.NewSelector()) - // deregister webhook if no policy found in cluster - if len(policies) == 1 { + // deregister webhook if no mutate/validate policy found in cluster + if !HasMutateOrValidatePolicies(policies) { ws.webhookRegistrationClient.DeregisterMutatingWebhook() glog.Infoln("Mutating webhook deregistered") } return nil } + +func HasMutateOrValidatePolicies(policies []*kyverno.Policy) bool { + for _, policy := range policies { + if HasMutateOrValidate(*policy) { + return true + } + } + return false +} + +func HasMutateOrValidate(policy kyverno.Policy) bool { + for _, rule := range policy.Spec.Rules { + if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) || !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) { + glog.Infoln(rule.Name) + return true + } + } + return false +} From 292a644bf8e13c0615f2e646b43f9749511a11dc Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 17:35:40 -0700 Subject: [PATCH 55/66] generate stats for generate rules --- pkg/api/kyverno/v1alpha1/types.go | 6 ++++-- pkg/engine/generation.go | 5 ++--- pkg/namespace/controller.go | 6 +++++- pkg/namespace/generation.go | 24 ++++++++++++++++++++++-- pkg/policy/controller.go | 1 + pkg/policy/status.go | 10 +++++++--- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/pkg/api/kyverno/v1alpha1/types.go b/pkg/api/kyverno/v1alpha1/types.go index 44769bd446..29fb9506a4 100644 --- a/pkg/api/kyverno/v1alpha1/types.go +++ b/pkg/api/kyverno/v1alpha1/types.go @@ -95,9 +95,11 @@ type PolicyStatus struct { // Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules ResourcesBlockedCount int `json:"resourcesBlockedCount"` // average time required to process the policy Mutation rules on a resource - AvgExecutionTimeMutation string `json:"averageMutationExecutionTime"` + AvgExecutionTimeMutation string `json:"averageMutationRulesExecutionTime"` // average time required to process the policy Validation rules on a resource - AvgExecutionTimeValidation string `json:"averageValidationExecutionTime"` + AvgExecutionTimeValidation string `json:"averageValidationRulesExecutionTime"` + // average time required to process the policy Validation rules on a resource + AvgExecutionTimeGeneration string `json:"averageGenerationRulesExecutionTime"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 9b46aa755f..5012121d37 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -18,14 +18,13 @@ import ( ) //Generate apply generation rules on a resource -func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) EngineResponse { - var response EngineResponse +func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) (response EngineResponse) { startTime := time.Now() glog.V(4).Infof("started applying generation rules of policy %q (%v)", policy.Name, startTime) defer func() { response.ExecutionTime = time.Since(startTime) glog.V(4).Infof("Finished applying generation rules policy %q (%v)", policy.Name, response.ExecutionTime) - glog.V(4).Infof("Mutation Rules appplied succesfully count %q for policy %q", response.RulesAppliedCount, policy.Name) + glog.V(4).Infof("Generation Rules appplied count %q for policy %q", response.RulesAppliedCount, policy.Name) }() incrementAppliedRuleCount := func() { // rules applied succesfully count diff --git a/pkg/namespace/controller.go b/pkg/namespace/controller.go index 78e6340a3f..38547fdb15 100644 --- a/pkg/namespace/controller.go +++ b/pkg/namespace/controller.go @@ -8,6 +8,7 @@ import ( "github.com/golang/glog" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" + "github.com/nirmata/kyverno/pkg/policy" "k8s.io/apimachinery/pkg/api/errors" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" @@ -44,7 +45,8 @@ type NamespaceController struct { pvListerSynced cache.InformerSynced // pvLister can list/get policy violation from the shared informer's store pvLister kyvernolister.PolicyViolationLister - + // API to send policy stats for aggregation + policyStatus policy.PolicyStatusInterface // eventGen provides interface to generate evenets eventGen event.Interface // Namespaces that need to be synced @@ -59,6 +61,7 @@ func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset, nsInformer v1Informer.NamespaceInformer, pInformer kyvernoinformer.PolicyInformer, pvInformer kyvernoinformer.PolicyViolationInformer, + policyStatus policy.PolicyStatusInterface, eventGen event.Interface) *NamespaceController { //TODO: do we need to event recorder for this controller? // create the controller @@ -83,6 +86,7 @@ func NewNamespaceController(kyvernoClient *kyvernoclient.Clientset, nsc.pLister = pInformer.Lister() nsc.pvListerSynced = pInformer.Informer().HasSynced nsc.pvLister = pvInformer.Lister() + nsc.policyStatus = policyStatus // resource manager // rebuild after 300 seconds/ 5 mins diff --git a/pkg/namespace/generation.go b/pkg/namespace/generation.go index ca715e20ce..d36bc7e24a 100644 --- a/pkg/namespace/generation.go +++ b/pkg/namespace/generation.go @@ -6,6 +6,7 @@ import ( client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/engine" + "github.com/nirmata/kyverno/pkg/policy" "github.com/golang/glog" @@ -13,7 +14,9 @@ import ( kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" + policyctr "github.com/nirmata/kyverno/pkg/policy" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -108,7 +111,7 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i glog.V(4).Infof("policy %s with resource version %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion()) continue } - policyInfo := applyPolicy(nsc.client, ns, *policy) + policyInfo := applyPolicy(nsc.client, ns, *policy, nsc.policyStatus) policyInfos = append(policyInfos, policyInfo) // post-processing, register the resource as processed nsc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion()) @@ -141,7 +144,20 @@ func listpolicies(ns unstructured.Unstructured, pLister kyvernolister.PolicyList return filteredpolicies } -func applyPolicy(client *client.Client, resource unstructured.Unstructured, policy kyverno.Policy) info.PolicyInfo { +func applyPolicy(client *client.Client, resource unstructured.Unstructured, policy kyverno.Policy, policyStatus policy.PolicyStatusInterface) info.PolicyInfo { + var ps policyctr.PolicyStat + gatherStat := func(policyName string, er engine.EngineResponse) { + // ps := policyctr.PolicyStat{} + ps.PolicyName = policyName + ps.Stats.GenerationExecutionTime = er.ExecutionTime + ps.Stats.RulesAppliedCount = er.RulesAppliedCount + } + // send stats for aggregation + sendStat := func(blocked bool) { + //SEND + policyStatus.SendStat(ps) + } + startTime := time.Now() glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime) defer func() { @@ -150,6 +166,10 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, poli policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) engineResponse := engine.Generate(client, policy, resource) policyInfo.AddRuleInfos(engineResponse.RuleInfos) + // gather stats + gatherStat(policy.Name, engineResponse) + //send stats + sendStat(false) return policyInfo } diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index fccbed13c5..40a80cefee 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -447,6 +447,7 @@ func (pc *PolicyController) calculateStatus(policyName string, pvList []*kyverno status.ResourcesBlockedCount = stats.ResourceBlocked status.AvgExecutionTimeMutation = stats.MutationExecutionTime.String() status.AvgExecutionTimeValidation = stats.ValidationExecutionTime.String() + status.AvgExecutionTimeGeneration = stats.GenerationExecutionTime.String() } return status } diff --git a/pkg/policy/status.go b/pkg/policy/status.go index 60a6a6dbf0..133300320b 100644 --- a/pkg/policy/status.go +++ b/pkg/policy/status.go @@ -56,9 +56,6 @@ func (psa *PolicyStatusAggregator) process() { // mutation & validation rules seperately for r := range psa.ch { glog.V(4).Infof("recieved policy stats %v", r) - // if err := psa.updateStats(r); err != nil { - // glog.Infof("Failed to update stats for policy %s: %v", r.PolicyName, err) - // } psa.aggregate(r) } } @@ -96,6 +93,12 @@ func (psa *PolicyStatusAggregator) aggregate(ps PolicyStat) { } else { info.ValidationExecutionTime = ps.Stats.ValidationExecutionTime } + if info.GenerationExecutionTime != zeroDuration { + info.GenerationExecutionTime = (info.GenerationExecutionTime + ps.Stats.GenerationExecutionTime) / 2 + glog.V(4).Infof("updated avg generation time %v", info.GenerationExecutionTime) + } else { + info.GenerationExecutionTime = ps.Stats.GenerationExecutionTime + } // update psa.policyData[ps.PolicyName] = info glog.V(4).Infof("updated stats for policy %s", ps.PolicyName) @@ -144,6 +147,7 @@ type PolicyStat struct { type PolicyStatInfo struct { MutationExecutionTime time.Duration ValidationExecutionTime time.Duration + GenerationExecutionTime time.Duration RulesAppliedCount int ResourceBlocked int } From a4310a38dd0fc766d3c96b8e9aa868cb5fb109af Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 17:35:55 -0700 Subject: [PATCH 56/66] add missing files --- main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 2c2a950354..6d61e72d9a 100644 --- a/main.go +++ b/main.go @@ -67,7 +67,8 @@ func main() { // POLICY CONTROLLER // - reconciliation policy and policy violation // - process policy on existing resources - // - status: violation count + // - status aggregator: recieves stats when a policy is applied + // : updates the policy status pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) if err != nil { @@ -92,7 +93,7 @@ func main() { // GENERATE CONTROLLER // - watches for Namespace resource and generates resource based on the policy generate rule - nsc := namespace.NewNamespaceController(pclient, client, kubeInformer.Core().V1().Namespaces(), pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) + nsc := namespace.NewNamespaceController(pclient, client, kubeInformer.Core().V1().Namespaces(), pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), pc.GetPolicyStatusAggregator(), egen) tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { From e7082153102a4c24b66bf6de9d20433a356bbed3 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 23:43:30 -0700 Subject: [PATCH 57/66] add Policy kind as known Kind --- pkg/api/kyverno/v1alpha1/register.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/api/kyverno/v1alpha1/register.go b/pkg/api/kyverno/v1alpha1/register.go index 86f19d5512..11e0a7e1db 100644 --- a/pkg/api/kyverno/v1alpha1/register.go +++ b/pkg/api/kyverno/v1alpha1/register.go @@ -29,6 +29,8 @@ var ( // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, + &Policy{}, + &PolicyList{}, &PolicyViolation{}, &PolicyViolationList{}, ) From ed9c88cd07f5c3e9592968a435d75d94bcb3956f Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 23:51:34 -0700 Subject: [PATCH 58/66] update comments --- main.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index 6d61e72d9a..f7aedf3bc1 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ var ( func main() { defer glog.Flush() printVersionInfo() + // profile cpu and memory consuption prof = enableProfiling(cpu, memory) // CLIENT CONFIG @@ -47,6 +48,7 @@ func main() { if err != nil { glog.Fatalf("Error creating client: %v\n", err) } + // DYNAMIC CLIENT // - client for all registered resources client, err := client.NewClient(clientConfig) @@ -60,15 +62,16 @@ func main() { // - PolicyVolation // - cache resync time: 10 seconds pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10*time.Second) + // EVENT GENERATOR - // - generate event with retry + // - generate event with retry mechanism egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies()) // POLICY CONTROLLER // - reconciliation policy and policy violation // - process policy on existing resources // - status aggregator: recieves stats when a policy is applied - // : updates the policy status + // & updates the policy status pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) if err != nil { @@ -76,6 +79,7 @@ func main() { } // POLICY VIOLATION CONTROLLER + // policy violation cleanup if the corresponding resource is deleted // status: lastUpdatTime pvc, err := policyviolation.NewPolicyViolationController(client, pclient, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations()) if err != nil { @@ -100,17 +104,25 @@ func main() { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - // WEBHOOK REGISTRATION - // -- validationwebhookconfiguration (Policy) - // -- mutatingwebhookconfiguration (All resources) webhookRegistrationClient, err := webhooks.NewWebhookRegistrationClient(clientConfig, client, serverIP, int32(webhookTimeout)) if err != nil { glog.Fatalf("Unable to register admission webhooks on cluster: %v\n", err) } - + // WEBHOOK REGISTRATION + // - validationwebhookconfiguration (Policy) + // - mutatingwebhookconfiguration (All resources) + // webhook confgiuration is also generated dynamically in the policy controller + // based on the policy resources created if err = webhookRegistrationClient.Register(); err != nil { glog.Fatalf("Failed registering Admission Webhooks: %v\n", err) } + + // WEBHOOOK + // - https server to provide endpoints called based on rules defined in Mutating & Validation webhook configuration + // - reports the results based on the response from the policy engine: + // -- annotations on resources with update details on mutation JSON patches + // -- generate policy violation resource + // -- generate events on policy and resource server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, pc.GetPolicyStatusAggregator(), filterK8Resources) if err != nil { glog.Fatalf("Unable to create webhook server: %v\n", err) @@ -118,10 +130,7 @@ func main() { stopCh := signals.SetupSignalHandler() - if err = webhookRegistrationClient.Register(); err != nil { - glog.Fatalf("Failed registering Admission Webhooks: %v\n", err) - } - + // Start the components pInformer.Start(stopCh) kubeInformer.Start(stopCh) go pc.Run(1, stopCh) From f1960876a28a4532c48d1591fb390f72bd0b83b3 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Tue, 20 Aug 2019 23:56:47 -0700 Subject: [PATCH 59/66] update comments --- main.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index f7aedf3bc1..b7db798d13 100644 --- a/main.go +++ b/main.go @@ -56,6 +56,12 @@ func main() { glog.Fatalf("Error creating client: %v\n", err) } + // KUBERNETES CLIENT + kubeClient, err := utils.NewKubeClient(clientConfig) + if err != nil { + glog.Fatalf("Error creating kubernetes client: %v\n", err) + } + // KYVERNO CRD INFORMER // watches CRD resources: // - Policy @@ -72,7 +78,6 @@ func main() { // - process policy on existing resources // - status aggregator: recieves stats when a policy is applied // & updates the policy status - pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen) if err != nil { glog.Fatalf("error creating policy controller: %v\n", err) @@ -86,24 +91,22 @@ func main() { glog.Fatalf("error creating policy violation controller: %v\n", err) } - // NAMESPACE INFORMER + // KUBERNETES RESOURCES INFORMER // watches namespace resource // - cache resync time: 10 seconds - kubeClient, err := utils.NewKubeClient(clientConfig) - if err != nil { - glog.Fatalf("Error creating kubernetes client: %v\n", err) - } kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Second) // GENERATE CONTROLLER // - watches for Namespace resource and generates resource based on the policy generate rule nsc := namespace.NewNamespaceController(pclient, client, kubeInformer.Core().V1().Namespaces(), pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), pc.GetPolicyStatusAggregator(), egen) + // CONFIGURE CERTIFICATES tlsPair, err := initTLSPemPair(clientConfig, client) if err != nil { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } + // WERBHOOK REGISTRATION CLIENT webhookRegistrationClient, err := webhooks.NewWebhookRegistrationClient(clientConfig, client, serverIP, int32(webhookTimeout)) if err != nil { glog.Fatalf("Unable to register admission webhooks on cluster: %v\n", err) From d8c315e3391c4371941f6858cec3bf417f9a286b Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 21 Aug 2019 01:07:32 -0700 Subject: [PATCH 60/66] fix import cylce after merge + seperate webhookconfig client --- main.go | 17 +++++---- pkg/namespace/generation.go | 28 ++++++-------- pkg/policy/controller.go | 37 ++++++++++++++----- .../registration.go | 2 +- .../registration_test.go | 2 +- pkg/webhooks/server.go | 5 ++- 6 files changed, 53 insertions(+), 38 deletions(-) rename pkg/{webhooks => webhookconfig}/registration.go (99%) rename pkg/{webhooks => webhookconfig}/registration_test.go (99%) diff --git a/main.go b/main.go index 0cb3132940..c95261538d 100644 --- a/main.go +++ b/main.go @@ -14,8 +14,9 @@ import ( "github.com/nirmata/kyverno/pkg/policy" "github.com/nirmata/kyverno/pkg/policyviolation" "github.com/nirmata/kyverno/pkg/utils" + "github.com/nirmata/kyverno/pkg/webhookconfig" "github.com/nirmata/kyverno/pkg/webhooks" - kubeinformer "k8s.io/client-go/informers" + kubeinformers "k8s.io/client-go/informers" "k8s.io/sample-controller/pkg/signals" ) @@ -65,6 +66,12 @@ func main() { glog.Fatalf("Error creating kubernetes client: %v\n", err) } + // WERBHOOK REGISTRATION CLIENT + webhookRegistrationClient, err := webhookconfig.NewWebhookRegistrationClient(clientConfig, client, serverIP, int32(webhookTimeout)) + if err != nil { + glog.Fatalf("Unable to register admission webhooks on cluster: %v\n", err) + } + // KYVERNO CRD INFORMER // watches CRD resources: // - Policy @@ -72,7 +79,7 @@ func main() { // - cache resync time: 10 seconds pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10*time.Second) - // KUBERNETES RESOURCES INFORMER + // KUBERNETES RESOURCES INFORMER // watches namespace resource // - cache resync time: 10 seconds kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Second) @@ -109,12 +116,6 @@ func main() { glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - // WERBHOOK REGISTRATION CLIENT - webhookRegistrationClient, err := webhooks.NewWebhookRegistrationClient(clientConfig, client, serverIP, int32(webhookTimeout)) - if err != nil { - glog.Fatalf("Unable to register admission webhooks on cluster: %v\n", err) - } - // WEBHOOK REGISTRATION // - validationwebhookconfiguration (Policy) // - mutatingwebhookconfiguration (All resources) diff --git a/pkg/namespace/generation.go b/pkg/namespace/generation.go index d36bc7e24a..997795eac8 100644 --- a/pkg/namespace/generation.go +++ b/pkg/namespace/generation.go @@ -4,19 +4,14 @@ import ( "sync" "time" + "github.com/golang/glog" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/engine" - "github.com/nirmata/kyverno/pkg/policy" - - "github.com/golang/glog" - - kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" - - kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" - policyctr "github.com/nirmata/kyverno/pkg/policy" + "github.com/nirmata/kyverno/pkg/policy" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -144,10 +139,9 @@ func listpolicies(ns unstructured.Unstructured, pLister kyvernolister.PolicyList return filteredpolicies } -func applyPolicy(client *client.Client, resource unstructured.Unstructured, policy kyverno.Policy, policyStatus policy.PolicyStatusInterface) info.PolicyInfo { - var ps policyctr.PolicyStat +func applyPolicy(client *client.Client, resource unstructured.Unstructured, p kyverno.Policy, policyStatus policy.PolicyStatusInterface) info.PolicyInfo { + var ps policy.PolicyStat gatherStat := func(policyName string, er engine.EngineResponse) { - // ps := policyctr.PolicyStat{} ps.PolicyName = policyName ps.Stats.GenerationExecutionTime = er.ExecutionTime ps.Stats.RulesAppliedCount = er.RulesAppliedCount @@ -159,15 +153,15 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, poli } startTime := time.Now() - glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime) + glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", p.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime) defer func() { - glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime)) + glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", p.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime)) }() - policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction) - engineResponse := engine.Generate(client, policy, resource) + policyInfo := info.NewPolicyInfo(p.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), p.Spec.ValidationFailureAction) + engineResponse := engine.Generate(client, p, resource) policyInfo.AddRuleInfos(engineResponse.RuleInfos) // gather stats - gatherStat(policy.Name, engineResponse) + gatherStat(p.Name, engineResponse) //send stats sendStat(false) diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index a9277ff12f..76d31796ec 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -17,7 +17,7 @@ import ( client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/event" "github.com/nirmata/kyverno/pkg/utils" - "github.com/nirmata/kyverno/pkg/webhooks" + "github.com/nirmata/kyverno/pkg/webhookconfig" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -71,7 +71,7 @@ type PolicyController struct { // mutationwebhookLister can list/get mutatingwebhookconfigurations mutationwebhookLister webhooklister.MutatingWebhookConfigurationLister // WebhookRegistrationClient - webhookRegistrationClient *webhooks.WebhookRegistrationClient + webhookRegistrationClient *webhookconfig.WebhookRegistrationClient // Resource manager, manages the mapping for already processed resource rm resourceManager // filter the resources defined in the list @@ -82,7 +82,7 @@ type PolicyController struct { // NewPolicyController create a new PolicyController func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, client *client.Client, pInformer kyvernoinformer.PolicyInformer, pvInformer kyvernoinformer.PolicyViolationInformer, - eventGen event.Interface, webhookInformer webhookinformer.MutatingWebhookConfigurationInformer, webhookRegistrationClient *webhooks.WebhookRegistrationClient) (*PolicyController, error) { + eventGen event.Interface, webhookInformer webhookinformer.MutatingWebhookConfigurationInformer, webhookRegistrationClient *webhookconfig.WebhookRegistrationClient) (*PolicyController, error) { // Event broad caster eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) @@ -405,11 +405,11 @@ func (pc *PolicyController) syncPolicy(key string) error { policy, err := pc.pLister.Get(key) if errors.IsNotFound(err) { glog.V(2).Infof("Policy %v has been deleted", key) - - // remove the recorded stats for the policy + + // remove the recorded stats for the policy pc.statusAggregator.RemovePolicyStats(key) - // remove webhook configurations if there are not policies - if err := pc.handleWebhookRegistration(true, nil); err != nil { + // remove webhook configurations if there are not policies + if err := pc.handleWebhookRegistration(true, nil); err != nil { glog.Errorln(err) } return nil @@ -466,14 +466,14 @@ func (pc *PolicyController) handleWebhookRegistration(delete bool, policy *kyver if policies == nil { glog.V(3).Infoln("No policy found in the cluster, deregistering webhook") pc.webhookRegistrationClient.DeregisterMutatingWebhook() - } else if !webhooks.HasMutateOrValidatePolicies(policies) { + } else if !HasMutateOrValidatePolicies(policies) { glog.V(3).Infoln("No muatate/validate policy found in the cluster, deregistering webhook") pc.webhookRegistrationClient.DeregisterMutatingWebhook() } return nil } - if webhookList == nil && webhooks.HasMutateOrValidate(*policy) { + if webhookList == nil && HasMutateOrValidate(*policy) { glog.V(3).Infoln("Found policy without mutatingwebhook, registering webhook") pc.webhookRegistrationClient.RegisterMutatingWebhook() } @@ -934,3 +934,22 @@ func joinPatches(patches ...[]byte) []byte { result = append(result, []byte("\n]")...) return result } + +func HasMutateOrValidatePolicies(policies []*kyverno.Policy) bool { + for _, policy := range policies { + if HasMutateOrValidate(*policy) { + return true + } + } + return false +} + +func HasMutateOrValidate(policy kyverno.Policy) bool { + for _, rule := range policy.Spec.Rules { + if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) || !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) { + glog.Infoln(rule.Name) + return true + } + } + return false +} diff --git a/pkg/webhooks/registration.go b/pkg/webhookconfig/registration.go similarity index 99% rename from pkg/webhooks/registration.go rename to pkg/webhookconfig/registration.go index bbdbf94afb..f4cdfa8154 100644 --- a/pkg/webhooks/registration.go +++ b/pkg/webhookconfig/registration.go @@ -1,4 +1,4 @@ -package webhooks +package webhookconfig import ( "errors" diff --git a/pkg/webhooks/registration_test.go b/pkg/webhookconfig/registration_test.go similarity index 99% rename from pkg/webhooks/registration_test.go rename to pkg/webhookconfig/registration_test.go index eb3cad3f78..2188007f44 100644 --- a/pkg/webhooks/registration_test.go +++ b/pkg/webhookconfig/registration_test.go @@ -1,4 +1,4 @@ -package webhooks +package webhookconfig import ( "bytes" diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index af6ae8a880..28483b47b2 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -22,6 +22,7 @@ import ( "github.com/nirmata/kyverno/pkg/policy" tlsutils "github.com/nirmata/kyverno/pkg/tls" "github.com/nirmata/kyverno/pkg/utils" + "github.com/nirmata/kyverno/pkg/webhookconfig" v1beta1 "k8s.io/api/admission/v1beta1" "k8s.io/client-go/tools/cache" ) @@ -37,7 +38,7 @@ type WebhookServer struct { pListerSynced cache.InformerSynced pvListerSynced cache.InformerSynced eventGen event.Interface - webhookRegistrationClient *WebhookRegistrationClient + webhookRegistrationClient *webhookconfig.WebhookRegistrationClient // API to send policy stats for aggregation policyStatus policy.PolicyStatusInterface filterK8Resources []utils.K8Resource @@ -52,7 +53,7 @@ func NewWebhookServer( pInformer kyvernoinformer.PolicyInformer, pvInormer kyvernoinformer.PolicyViolationInformer, eventGen event.Interface, - webhookRegistrationClient *WebhookRegistrationClient, + webhookRegistrationClient *webhookconfig.WebhookRegistrationClient, policyStatus policy.PolicyStatusInterface, filterK8Resources string) (*WebhookServer, error) { From 042bc645497ce6713bfca286f8bacd73ef7387b6 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 21 Aug 2019 12:03:53 -0700 Subject: [PATCH 61/66] fix test build errors + skip testrunner --- pkg/engine/generation.go | 8 +- pkg/engine/utils_test.go | 942 +++++++----------- pkg/testrunner/test.go | 62 +- pkg/testrunner/testcase.go | 6 +- pkg/testrunner/testrunner_test.go | 2 + pkg/testrunner/utils.go | 10 + .../resources/CAFile | 0 7 files changed, 384 insertions(+), 646 deletions(-) rename pkg/{webhooks => webhookconfig}/resources/CAFile (100%) diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 5012121d37..90d6a9c3bf 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -41,10 +41,10 @@ func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unst err := applyRuleGenerator(client, ns, rule.Generation, policy.GetCreationTimestamp()) if err != nil { ri.Fail() - ri.Addf("Failed to apply rule generator, err %v.", rule.Name, err) + ri.Addf("Failed to apply rule %s generator, err %v.", rule.Name, err) glog.Infof("failed to apply policy %s rule %s on resource %s/%s/%s: %v", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName(), err) } else { - ri.Addf("Generation succesfully.", rule.Name) + ri.Addf("Generation succesfully for rule %s", rule.Name) glog.Infof("succesfully applied policy %s rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName()) } ris = append(ris, ri) @@ -101,10 +101,10 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen // 2> If clone already exists return resource, err = client.GetResource(gen.Kind, gen.Clone.Namespace, gen.Clone.Name) if err != nil { - glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s not present: %v", gen.Kind, gen.Kind, gen.Clone.Namespace, gen.Clone.Name, err) + glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s not present: %v", gen.Kind, gen.Clone.Namespace, gen.Clone.Name, err) return err } - glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", gen.Kind, gen.Kind, gen.Clone.Namespace, gen.Clone.Name) + glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", gen.Kind, gen.Clone.Namespace, gen.Clone.Name) rdata = resource.UnstructuredContent() } if processExisting { diff --git a/pkg/engine/utils_test.go b/pkg/engine/utils_test.go index 81379246c5..66b192b0f8 100644 --- a/pkg/engine/utils_test.go +++ b/pkg/engine/utils_test.go @@ -3,105 +3,232 @@ package engine import ( "testing" - types "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "gotest.tools/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestResourceMeetsDescription_Kind(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, +// Match multiple kinds +func TestResourceDescriptionMatch_MultipleKind(t *testing.T) { + 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 + } + ] + } + ] + } + } + } + }`) + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + t.Errorf("unable to convert raw resource to unstructured: %v", err) + + } + resourceDescription := kyverno.ResourceDescription{ + Kinds: []string{"Deployment", "Pods"}, Selector: &metav1.LabelSelector{ MatchLabels: nil, MatchExpressions: nil, }, } - excludeResourcesResourceDesc := types.ResourceDescription{} - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} + rule := kyverno.Rule{MatchResources: kyverno.MatchResources{resourceDescription}} - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription.Kinds[0] = "Deployment" - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription.Kinds[0] = "ConfigMap" - groupVersionKind.Kind = "Deployment" - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) + assert.Assert(t, MatchesResourceDescription(*resource, rule)) } -func TestResourceMeetsDescription_Name(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, +// Match resource name +func TestResourceDescriptionMatch_Name(t *testing.T) { + 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 + } + ] + } + ] + } + } + } + }`) + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + t.Errorf("unable to convert raw resource to unstructured: %v", err) + + } + resourceDescription := kyverno.ResourceDescription{ + Kinds: []string{"Deployment"}, + Name: "nginx-deployment", Selector: &metav1.LabelSelector{ MatchLabels: nil, MatchExpressions: nil, }, } - excludeResourcesResourceDesc := types.ResourceDescription{} + rule := kyverno.Rule{MatchResources: kyverno.MatchResources{resourceDescription}} - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - resourceDescription.Name = "test-config-map-new" - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map-new", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - rawResource = []byte(`{ - "metadata":{ - "name":"", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) + assert.Assert(t, MatchesResourceDescription(*resource, rule)) } -func TestResourceMeetsDescription_MatchExpressions(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, +// Match resource regex +func TestResourceDescriptionMatch_Name_Regex(t *testing.T) { + 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 + } + ] + } + ] + } + } + } + }`) + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + t.Errorf("unable to convert raw resource to unstructured: %v", err) + + } + resourceDescription := kyverno.ResourceDescription{ + Kinds: []string{"Deployment"}, + Name: "nginx-*", + Selector: &metav1.LabelSelector{ + MatchLabels: nil, + MatchExpressions: nil, + }, + } + rule := kyverno.Rule{MatchResources: kyverno.MatchResources{resourceDescription}} + + assert.Assert(t, MatchesResourceDescription(*resource, rule)) +} + +// Match expressions for labels to not match +func TestResourceDescriptionMatch_Label_Expression_NotMatch(t *testing.T) { + 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 + } + ] + } + ] + } + } + } + }`) + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + t.Errorf("unable to convert raw resource to unstructured: %v", err) + + } + resourceDescription := kyverno.ResourceDescription{ + Kinds: []string{"Deployment"}, + Name: "nginx-*", Selector: &metav1.LabelSelector{ MatchLabels: nil, MatchExpressions: []metav1.LabelSelectorRequirement{ @@ -112,561 +239,158 @@ func TestResourceMeetsDescription_MatchExpressions(t *testing.T) { "sometest1", }, }, - metav1.LabelSelectorRequirement{ - Key: "label1", - Operator: "In", - Values: []string{ - "test1", - "test8", - "test201", - }, - }, - metav1.LabelSelectorRequirement{ - Key: "label3", - Operator: "DoesNotExist", - Values: nil, - }, - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "test2", - }, - }, }, }, } - excludeResourcesResourceDesc := types.ResourceDescription{} + rule := kyverno.Rule{MatchResources: kyverno.MatchResources{resourceDescription}} - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1234567890", - "label2":"test2" - } - } - }`) - - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) + assert.Assert(t, MatchesResourceDescription(*resource, rule)) } -func TestResourceMeetsDescription_MatchLabels(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - "label2": "test2", - }, - MatchExpressions: nil, - }, - } - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - excludeResourcesResourceDesc := types.ResourceDescription{} - +// Match label expression in matching set +func TestResourceDescriptionMatch_Label_Expression_Match(t *testing.T) { rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label3":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label3": "test1", - "label2": "test2", - }, - MatchExpressions: nil, + "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 + } + ] + } + ] + } + } + } + }`) + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + t.Errorf("unable to convert raw resource to unstructured: %v", err) + } - - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -} - -func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { - resourceName := "test-config-map" - resourceDescription := types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, + resourceDescription := kyverno.ResourceDescription{ + Kinds: []string{"Deployment"}, + Name: "nginx-*", Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - }, + MatchLabels: nil, MatchExpressions: []metav1.LabelSelectorRequirement{ metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "test2", - }, - }, - }, - }, - } - groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} - excludeResourcesResourceDesc := types.ResourceDescription{} - - rawResource := []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", + Key: "app", Operator: "NotIn", Values: []string{ - "sometest1", + "nginx1", + "nginx2", }, }, }, }, } + rule := kyverno.Rule{MatchResources: kyverno.MatchResources{resourceDescription}} - rawResource = []byte(`{ - "metadata":{ - "name":"test-config-map", - "namespace":"default", - "creationTimestamp":null, - "labels":{ - "label1":"test1", - "label2":"test2" - } - } - }`) - assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "sometest1", - }, - }, - }, - }, - } - - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - - resourceDescription = types.ResourceDescription{ - Kinds: []string{"ConfigMap"}, - Name: resourceName, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "label1": "test1", - "label3": "test3", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - metav1.LabelSelectorRequirement{ - Key: "label2", - Operator: "In", - Values: []string{ - "test2", - }, - }, - }, - }, - } - - assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) + assert.Assert(t, MatchesResourceDescription(*resource, rule)) } -// func TestResourceMeetsDescription_Kind(t *testing.T) { -// resourceName := "test-config-map" -// resourceDescription := types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: nil, -// MatchExpressions: nil, -// }, -// } -// excludeResourcesResourceDesc := types.ResourceDescription{} -// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} -// rawResource := []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) +// check for exclude conditions +func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) { + rawResource := []byte(`{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "name": "nginx-deployment", + "labels": { + "app": "nginx", + "block": "true" + } + }, + "spec": { + "replicas": 3, + "selector": { + "matchLabels": { + "app": "nginx" + } + }, + "template": { + "metadata": { + "labels": { + "app": "nginx" + } + }, + "spec": { + "containers": [ + { + "name": "nginx", + "image": "nginx:1.7.9", + "ports": [ + { + "containerPort": 80 + } + ] + } + ] + } + } + } + }`) + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + t.Errorf("unable to convert raw resource to unstructured: %v", err) -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// resourceDescription.Kinds[0] = "Deployment" -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// resourceDescription.Kinds[0] = "ConfigMap" -// groupVersionKind.Kind = "Deployment" -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// } + } + resourceDescription := kyverno.ResourceDescription{ + Kinds: []string{"Deployment"}, + Name: "nginx-*", + Selector: &metav1.LabelSelector{ + MatchLabels: nil, + MatchExpressions: []metav1.LabelSelectorRequirement{ + metav1.LabelSelectorRequirement{ + Key: "app", + Operator: "NotIn", + Values: []string{ + "nginx1", + "nginx2", + }, + }, + }, + }, + } -// func TestResourceMeetsDescription_Name(t *testing.T) { -// resourceName := "test-config-map" -// resourceDescription := types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: nil, -// MatchExpressions: nil, -// }, -// } -// excludeResourcesResourceDesc := types.ResourceDescription{} + resourceDescriptionExclude := kyverno.ResourceDescription{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "block": "true", + }, + }, + } -// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} + rule := kyverno.Rule{MatchResources: kyverno.MatchResources{resourceDescription}, + ExcludeResources: kyverno.ExcludeResources{resourceDescriptionExclude}} -// rawResource := []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) - -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// resourceName = "test-config-map-new" -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// rawResource = []byte(`{ -// "metadata":{ -// "name":"test-config-map-new", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// rawResource = []byte(`{ -// "metadata":{ -// "name":"", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// } - -// func TestResourceMeetsDescription_MatchExpressions(t *testing.T) { -// resourceName := "test-config-map" -// resourceDescription := types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: nil, -// MatchExpressions: []metav1.LabelSelectorRequirement{ -// metav1.LabelSelectorRequirement{ -// Key: "label2", -// Operator: "NotIn", -// Values: []string{ -// "sometest1", -// }, -// }, -// metav1.LabelSelectorRequirement{ -// Key: "label1", -// Operator: "In", -// Values: []string{ -// "test1", -// "test8", -// "test201", -// }, -// }, -// metav1.LabelSelectorRequirement{ -// Key: "label3", -// Operator: "DoesNotExist", -// Values: nil, -// }, -// metav1.LabelSelectorRequirement{ -// Key: "label2", -// Operator: "In", -// Values: []string{ -// "test2", -// }, -// }, -// }, -// }, -// } -// excludeResourcesResourceDesc := types.ResourceDescription{} - -// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} -// rawResource := []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) - -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// rawResource = []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1234567890", -// "label2":"test2" -// } -// } -// }`) - -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// } - -// func TestResourceMeetsDescription_MatchLabels(t *testing.T) { -// resourceName := "test-config-map" -// resourceDescription := types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: map[string]string{ -// "label1": "test1", -// "label2": "test2", -// }, -// MatchExpressions: nil, -// }, -// } -// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} -// excludeResourcesResourceDesc := types.ResourceDescription{} - -// rawResource := []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// rawResource = []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label3":"test1", -// "label2":"test2" -// } -// } -// }`) -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// resourceDescription = types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: map[string]string{ -// "label3": "test1", -// "label2": "test2", -// }, -// MatchExpressions: nil, -// }, -// } - -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// } - -// func TestResourceMeetsDescription_MatchLabelsAndMatchExpressions(t *testing.T) { -// resourceName := "test-config-map" -// resourceDescription := types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: map[string]string{ -// "label1": "test1", -// }, -// MatchExpressions: []metav1.LabelSelectorRequirement{ -// metav1.LabelSelectorRequirement{ -// Key: "label2", -// Operator: "In", -// Values: []string{ -// "test2", -// }, -// }, -// }, -// }, -// } -// groupVersionKind := metav1.GroupVersionKind{Kind: "ConfigMap"} -// excludeResourcesResourceDesc := types.ResourceDescription{} - -// rawResource := []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) - -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// resourceDescription = types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: map[string]string{ -// "label1": "test1", -// }, -// MatchExpressions: []metav1.LabelSelectorRequirement{ -// metav1.LabelSelectorRequirement{ -// Key: "label2", -// Operator: "NotIn", -// Values: []string{ -// "sometest1", -// }, -// }, -// }, -// }, -// } - -// rawResource = []byte(`{ -// "metadata":{ -// "name":"test-config-map", -// "namespace":"default", -// "creationTimestamp":null, -// "labels":{ -// "label1":"test1", -// "label2":"test2" -// } -// } -// }`) -// assert.Assert(t, ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// resourceDescription = types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: map[string]string{ -// "label1": "test1", -// }, -// MatchExpressions: []metav1.LabelSelectorRequirement{ -// metav1.LabelSelectorRequirement{ -// Key: "label2", -// Operator: "In", -// Values: []string{ -// "sometest1", -// }, -// }, -// }, -// }, -// } - -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) - -// resourceDescription = types.ResourceDescription{ -// Kinds: []string{"ConfigMap"}, -// Name: &resourceName, -// Selector: &metav1.LabelSelector{ -// MatchLabels: map[string]string{ -// "label1": "test1", -// "label3": "test3", -// }, -// MatchExpressions: []metav1.LabelSelectorRequirement{ -// metav1.LabelSelectorRequirement{ -// Key: "label2", -// Operator: "In", -// Values: []string{ -// "test2", -// }, -// }, -// }, -// }, -// } - -// assert.Assert(t, false == ResourceMeetsDescription(rawResource, resourceDescription, excludeResourcesResourceDesc, groupVersionKind)) -// } + assert.Assert(t, !MatchesResourceDescription(*resource, rule)) +} func TestWrappedWithParentheses_StringIsWrappedWithParentheses(t *testing.T) { str := "(something)" diff --git a/pkg/testrunner/test.go b/pkg/testrunner/test.go index bd2104f97b..b1cc4e0144 100644 --- a/pkg/testrunner/test.go +++ b/pkg/testrunner/test.go @@ -2,15 +2,14 @@ package testrunner import ( "fmt" + "reflect" "strconv" "testing" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - ospath "path" "github.com/golang/glog" - pt "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" @@ -22,7 +21,7 @@ type test struct { t *testing.T testCase *testCase // input - policy *pt.Policy + policy *kyverno.Policy tResource *resourceInfo loadResources []*resourceInfo // expected @@ -64,7 +63,7 @@ func (t *test) run() { t.checkGenerationResult(client, policyInfo) } -func (t *test) checkMutationResult(pr *resourceInfo, policyInfo *info.PolicyInfo) { +func (t *test) checkMutationResult(pr *resourceInfo, policyInfo info.PolicyInfo) { if t.testCase.Expected.Mutation == nil { glog.Info("No Mutation check defined") return @@ -91,12 +90,12 @@ func (t *test) overAllPass(result bool, expected string) { } } -func (t *test) compareRules(ruleInfos []*info.RuleInfo, rules []tRules) { +func (t *test) compareRules(ruleInfos []info.RuleInfo, rules []tRules) { // Compare the rules specified in the expected against the actual rule info returned by the apply policy for _, eRule := range rules { // Look-up the rule from the policy info rule := lookUpRule(eRule.Name, ruleInfos) - if rule == nil { + if reflect.DeepEqual(rule, info.RuleInfo{}) { t.t.Errorf("Rule with name %s not found", eRule.Name) continue } @@ -118,16 +117,17 @@ func (t *test) compareRules(ruleInfos []*info.RuleInfo, rules []tRules) { } } -func lookUpRule(name string, ruleInfos []*info.RuleInfo) *info.RuleInfo { +func lookUpRule(name string, ruleInfos []info.RuleInfo) info.RuleInfo { + for _, r := range ruleInfos { if r.Name == name { return r } } - return nil + return info.RuleInfo{} } -func (t *test) checkValidationResult(policyInfo *info.PolicyInfo) { +func (t *test) checkValidationResult(policyInfo info.PolicyInfo) { if t.testCase.Expected.Validation == nil { glog.Info("No Validation check defined") return @@ -137,7 +137,7 @@ func (t *test) checkValidationResult(policyInfo *info.PolicyInfo) { t.compareRules(policyInfo.Rules, t.testCase.Expected.Validation.Rules) } -func (t *test) checkGenerationResult(client *client.Client, policyInfo *info.PolicyInfo) { +func (t *test) checkGenerationResult(client *client.Client, policyInfo info.PolicyInfo) { if t.testCase.Expected.Generation == nil { glog.Info("No Generate check defined") return @@ -162,11 +162,12 @@ func (t *test) checkGenerationResult(client *client.Client, policyInfo *info.Pol } } -func (t *test) applyPolicy(policy *pt.Policy, +func (t *test) applyPolicy(policy *kyverno.Policy, tresource *resourceInfo, - client *client.Client) (*resourceInfo, *info.PolicyInfo, error) { + client *client.Client) (*resourceInfo, info.PolicyInfo, error) { // apply policy on the trigger resource // Mutate + var zeroPolicyInfo info.PolicyInfo var err error rawResource := tresource.rawResource rname := engine.ParseNameFromObject(rawResource) @@ -177,42 +178,43 @@ func (t *test) applyPolicy(policy *pt.Policy, rname, rns, policy.Spec.ValidationFailureAction) + + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + return nil, zeroPolicyInfo, err + } + // Apply Mutation Rules - patches, ruleInfos := engine.Mutate(*policy, rawResource, *tresource.gvk) - policyInfo.AddRuleInfos(ruleInfos) + engineResponse := engine.Mutate(*policy, *resource) + // patches, ruleInfos := engine.Mutate(*policy, rawResource, *tresource.gvk) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) // TODO: only validate if there are no errors in mutate, why? if policyInfo.IsSuccessful() { - if len(patches) != 0 { - rawResource, err = engine.ApplyPatches(rawResource, patches) + if len(engineResponse.Patches) != 0 { + rawResource, err = engine.ApplyPatches(rawResource, engineResponse.Patches) if err != nil { - return nil, nil, err + return nil, zeroPolicyInfo, err } } } // Validate - ruleInfos, err = engine.Validate(*policy, rawResource, *tresource.gvk) - policyInfo.AddRuleInfos(ruleInfos) + engineResponse = engine.Validate(*policy, *resource) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) if err != nil { - return nil, nil, err + return nil, zeroPolicyInfo, err } if rkind == "Namespace" { if client != nil { - // convert []byte to unstructured - unstr := unstructured.Unstructured{} - err := unstr.UnmarshalJSON(rawResource) - if err != nil { - glog.Error(err) - } - ruleInfos := engine.Generate(client, policy, unstr) - policyInfo.AddRuleInfos(ruleInfos) + engineResponse := engine.Generate(client, *policy, *resource) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) } } // Generate // transform the patched Resource into resource Info ri, err := extractResourceRaw(rawResource) if err != nil { - return nil, nil, err + return nil, zeroPolicyInfo, err } // return the results return ri, policyInfo, nil diff --git a/pkg/testrunner/testcase.go b/pkg/testrunner/testcase.go index 80363c7b1e..99ba30c733 100644 --- a/pkg/testrunner/testcase.go +++ b/pkg/testrunner/testcase.go @@ -7,7 +7,7 @@ import ( ospath "path" "github.com/golang/glog" - pt "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" yaml "k8s.io/apimachinery/pkg/util/yaml" @@ -117,8 +117,8 @@ func (tc *testCase) loadTriggerResource(ap string) (*resourceInfo, error) { } // Loads a single policy -func (tc *testCase) loadPolicy(file string) (*pt.Policy, error) { - p := &pt.Policy{} +func (tc *testCase) loadPolicy(file string) (*kyverno.Policy, error) { + p := &kyverno.Policy{} data, err := LoadFile(file) if err != nil { return nil, err diff --git a/pkg/testrunner/testrunner_test.go b/pkg/testrunner/testrunner_test.go index 9d1155e0c1..25ab75111f 100644 --- a/pkg/testrunner/testrunner_test.go +++ b/pkg/testrunner/testrunner_test.go @@ -3,5 +3,7 @@ package testrunner import "testing" func TestCLI(t *testing.T) { + //https://github.com/nirmata/kyverno/issues/301 + t.Skip("skipping testrunner as this needs a re-design") runner(t, "/test/scenarios/cli") } diff --git a/pkg/testrunner/utils.go b/pkg/testrunner/utils.go index eadd4c3f42..947a4b5ad3 100644 --- a/pkg/testrunner/utils.go +++ b/pkg/testrunner/utils.go @@ -121,3 +121,13 @@ func ParseNamespaceFromObject(bytes []byte) string { } return "" } + +func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) { + resource := &unstructured.Unstructured{} + err := resource.UnmarshalJSON(data) + if err != nil { + glog.V(4).Infof("failed to unmarshall resource: %v", err) + return nil, err + } + return resource, nil +} diff --git a/pkg/webhooks/resources/CAFile b/pkg/webhookconfig/resources/CAFile similarity index 100% rename from pkg/webhooks/resources/CAFile rename to pkg/webhookconfig/resources/CAFile From 57f770241e9d6ab91529afed4d2454b3bf7fe2a0 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 21 Aug 2019 14:18:36 -0700 Subject: [PATCH 62/66] remove commented code --- pkg/policy/controller.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 76d31796ec..4ff81722a0 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -601,10 +601,6 @@ func (m *PolicyViolationControllerRefManager) adoptPolicyViolation(pv *kyverno.P glog.Errorf("failed to add owner reference %v for PolicyViolation %s: %v", pOwnerRef, pv.Name, err) return err } - // addControllerPatch := fmt.Sprintf( - // `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`, - // m.controllerKind.GroupVersion(), m.controllerKind.Kind, - // m.Controller.GetName(), m.Controller.GetUID(), pv.UID) return m.pvControl.PatchPolicyViolation(pv.Name, addControllerPatch) } From fe5e9b0bb1d6825da7ca04744c97c0e8c1b7b546 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 21 Aug 2019 14:18:44 -0700 Subject: [PATCH 63/66] update documentation --- documentation/writing-policies.md | 48 +++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/documentation/writing-policies.md b/documentation/writing-policies.md index 72a80dbae3..0a1552be17 100644 --- a/documentation/writing-policies.md +++ b/documentation/writing-policies.md @@ -14,26 +14,44 @@ spec : rules: # Rules must have a unique name - name: "check-pod-controller-labels" - # Each rule matches specific resource described by "resource" field. - resource: - kinds: - - Deployment - - StatefulSet - - DaemonSet - # A resource name is optional. Name supports wildcards * and ? - name: "*" - # A resoucre selector is optional. Selector values support wildcards * and ? - selector: - matchLabels: - app: mongodb - matchExpressions: - - {key: tier, operator: In, values: [database]} - + # Each rule matches specific resource described by "match" field. + match: + resources: + kinds: + - Deployment + - StatefulSet + - DaemonSet + # A resource name is optional. Name supports wildcards * and ? + name: "*" + # A resoucre selector is optional. Selector values support wildcards * and ? + selector: + matchLabels: + app: mongodb + matchExpressions: + - {key: tier, operator: In, values: [database]} + # Resources that need to be excluded + # exclude: + # resources: + # kinds: + # - Deployment + # # A resource name is optional. Name supports wildcards * and ? + # name: "*" + # # A resoucre selector is optional. Selector values support wildcards * and ? + # selector: + # matchLabels: + # app: mongodb + # matchExpressions: + # - {key: tier, operator: In, values: [database]} # Each rule can contain a single validate, mutate, or generate directive ... ```` Each rule can validate, mutate, or generate configurations of matching resources. A rule definition can contain only a single **mutate**, **validate**, or **generate** child node. These actions are applied to the resource in described order: mutation, validation and then generation. +**Resource description:** +* ```match``` is a required key that defines the parameters which identify the resources that need to matched + +* ```exclude``` is an option key to exclude resources from the application of the rule + --- *Read Next >> [Validate](/documentation/writing-policies-validate.md)* \ No newline at end of file From ff03744958164212c8f9674cd156d3072f204ce6 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 21 Aug 2019 15:49:34 -0700 Subject: [PATCH 64/66] update documentation --- documentation/writing-policies-generate.md | 35 ++++++++-------- documentation/writing-policies-mutate.md | 48 ++++++++++++---------- documentation/writing-policies-validate.md | 38 +++++++++-------- documentation/writing-policies.md | 37 +++++++++-------- 4 files changed, 85 insertions(+), 73 deletions(-) diff --git a/documentation/writing-policies-generate.md b/documentation/writing-policies-generate.md index 9d80a70871..e99f539796 100644 --- a/documentation/writing-policies-generate.md +++ b/documentation/writing-policies-generate.md @@ -14,12 +14,13 @@ metadata: spec: rules: - name: "Basic config generator for all namespaces" - resource: - kinds: - - Namespace - selector: - matchLabels: - LabelForSelector : "namespace2" + match: + resources: + kinds: + - Namespace + selector: + matchLabels: + LabelForSelector : "namespace2" generate: kind: ConfigMap name: default-config @@ -27,12 +28,13 @@ spec: namespace: default name: config-template - name: "Basic config generator for all namespaces" - resource: - kinds: - - Namespace - selector: - matchLabels: - LabelForSelector : "namespace2" + match: + resources: + kinds: + - Namespace + selector: + matchLabels: + LabelForSelector : "namespace2" generate: kind: Secret name: mongo-creds @@ -59,10 +61,11 @@ metadata: spec: rules: - name: "deny-all-traffic" - resource: - kinds: - - Namespace - name: "*" + match: + resources: + kinds: + - Namespace + name: "*" generate: kind: NetworkPolicy name: deny-all-traffic diff --git a/documentation/writing-policies-mutate.md b/documentation/writing-policies-mutate.md index 923fcdeae1..cac31b55d6 100644 --- a/documentation/writing-policies-mutate.md +++ b/documentation/writing-policies-mutate.md @@ -18,9 +18,10 @@ metadata : spec : rules: - name: "add-init-secrets" - resource: - kinds: - - Deployment + match: + resources: + kinds: + - Deployment mutate: patches: - path: "/spec/template/spec/initContainers/0/" @@ -46,9 +47,10 @@ metadata : spec : rules: - name: "Remove unwanted label" - resource: - kinds: - - Secret + match: + resources: + kinds: + - Secret mutate: patches: - path: "/metadata/labels/purpose" @@ -71,12 +73,13 @@ metadata : spec : rules: - name: "Set hard memory limit to 2Gi" - resource: - kinds: - - Pod - selector: - matchLabels: - memory: high + match: + resources: + kinds: + - Pod + selector: + matchLabels: + memory: high mutate: overlay: spec: @@ -103,9 +106,10 @@ metadata: spec: rules: - name: "Add IP to subsets" - resource: - kinds : - - Endpoints + match: + resources: + kinds : + - Endpoints mutate: overlay: subsets: @@ -128,9 +132,10 @@ metadata : spec : rules: - name: "Set port" - resource: - kinds : - - Endpoints + match: + resources: + kinds : + - Endpoints mutate: overlay: subsets: @@ -158,9 +163,10 @@ metadata : spec : rules: - name: "Set port" - resource: - kinds : - - Endpoints + match: + resources: + kinds : + - Endpoints mutate: overlay: subsets: diff --git a/documentation/writing-policies-validate.md b/documentation/writing-policies-validate.md index f0ac4c7d97..9fd1d31234 100644 --- a/documentation/writing-policies-validate.md +++ b/documentation/writing-policies-validate.md @@ -44,16 +44,17 @@ metadata : spec : rules: - name: check-label - resource: - # Kind specifies one or more resource types to match - kinds: - - Deployment - - StatefuleSet - - DaemonSet - # Name is optional and can use wildcards - name: "*" - # Selector is optional - selector: + match: + resources: + # Kind specifies one or more resource types to match + kinds: + - Deployment + - StatefuleSet + - DaemonSet + # Name is optional and can use wildcards + name: "*" + # Selector is optional + selector: validate: # Message is optional message: "The label app is required" @@ -79,14 +80,15 @@ metadata : spec : rules: - name: check-memory_requests_link_in_yaml_relative - resource: - # Kind specifies one or more resource types to match - kinds: - - Deployment - # Name is optional and can use wildcards - name: "*" - # Selector is optional - selector: + match: + resources: + # Kind specifies one or more resource types to match + kinds: + - Deployment + # Name is optional and can use wildcards + name: "*" + # Selector is optional + selector: validate: pattern: spec: diff --git a/documentation/writing-policies.md b/documentation/writing-policies.md index 0a1552be17..0b5476b9c7 100644 --- a/documentation/writing-policies.md +++ b/documentation/writing-policies.md @@ -17,31 +17,32 @@ spec : # Each rule matches specific resource described by "match" field. match: resources: - kinds: + kinds: # Required, list of kinds - Deployment - StatefulSet - - DaemonSet - # A resource name is optional. Name supports wildcards * and ? - name: "*" - # A resoucre selector is optional. Selector values support wildcards * and ? - selector: + name: "mongo*" # Optional, a resource name is optional. Name supports wildcards * and ? + namespaces: # Optional, list of namespaces + - devtest2 + - devtest1 + selector: # Optional, a resource selector is optional. Selector values support wildcards * and ? matchLabels: app: mongodb matchExpressions: - {key: tier, operator: In, values: [database]} # Resources that need to be excluded - # exclude: - # resources: - # kinds: - # - Deployment - # # A resource name is optional. Name supports wildcards * and ? - # name: "*" - # # A resoucre selector is optional. Selector values support wildcards * and ? - # selector: - # matchLabels: - # app: mongodb - # matchExpressions: - # - {key: tier, operator: In, values: [database]} + exclude: # Optional, resources to be excluded from evaulation + resources: + kinds: + - Daemonsets + name: "*" + namespaces: + - devtest2 + selector: + matchLabels: + app: mongodb + matchExpressions: + - {key: tier, operator: In, values: [database]} + # Each rule can contain a single validate, mutate, or generate directive ... ```` From 35e0c7ca49b56b7214970c60386ebc737e518ff9 Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 21 Aug 2019 16:34:17 -0700 Subject: [PATCH 65/66] fix build errors + add example for exclude policy --- examples/policy_mutate_imagePullPolicy.yaml | 4 ++ pkg/kyverno/apply/apply.go | 43 ++++++++++----------- pkg/kyverno/apply/util.go | 10 +++++ test/generate-resource/main.go | 4 +- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/examples/policy_mutate_imagePullPolicy.yaml b/examples/policy_mutate_imagePullPolicy.yaml index 64fb71a910..83a9aad033 100644 --- a/examples/policy_mutate_imagePullPolicy.yaml +++ b/examples/policy_mutate_imagePullPolicy.yaml @@ -12,6 +12,10 @@ spec: selector: matchLabels: app : nginxlatest + exclude: + resources: + kinds: + - DaemonSet mutate: overlay: spec: diff --git a/pkg/kyverno/apply/apply.go b/pkg/kyverno/apply/apply.go index eb30c5dc26..e8b9ad2316 100644 --- a/pkg/kyverno/apply/apply.go +++ b/pkg/kyverno/apply/apply.go @@ -8,7 +8,7 @@ import ( "os" "github.com/golang/glog" - kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/engine" "github.com/nirmata/kyverno/pkg/info" "github.com/spf13/cobra" @@ -51,7 +51,7 @@ func NewCmdApply(in io.Reader, out, errout io.Writer) *cobra.Command { return cmd } -func complete(kubeconfig string, args []string) (*kubepolicy.Policy, []*resourceInfo) { +func complete(kubeconfig string, args []string) (*kyverno.Policy, []*resourceInfo) { policyDir, resourceDir, err := validateDir(args) if err != nil { glog.Errorf("Failed to parse file path, err: %v\n", err) @@ -75,7 +75,7 @@ func complete(kubeconfig string, args []string) (*kubepolicy.Policy, []*resource return policy, resources } -func applyPolicy(policy *kubepolicy.Policy, resources []*resourceInfo) (output string) { +func applyPolicy(policy *kyverno.Policy, resources []*resourceInfo) (output string) { for _, resource := range resources { patchedDocument, err := applyPolicyOnRaw(policy, resource.rawResource, resource.gvk) if err != nil { @@ -94,7 +94,7 @@ func applyPolicy(policy *kubepolicy.Policy, resources []*resourceInfo) (output s return } -func applyPolicyOnRaw(policy *kubepolicy.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]byte, error) { +func applyPolicyOnRaw(policy *kyverno.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]byte, error) { patchedResource := rawResource var err error @@ -106,45 +106,44 @@ func applyPolicyOnRaw(policy *kubepolicy.Policy, rawResource []byte, gvk *metav1 rns, policy.Spec.ValidationFailureAction) + resource, err := ConvertToUnstructured(rawResource) + if err != nil { + return nil, err + } + //TODO check if the kind information is present resource // Process Mutation - patches, ruleInfos := engine.Mutate(*policy, rawResource, *gvk) - policyInfo.AddRuleInfos(ruleInfos) + engineResponse := engine.Mutate(*policy, *resource) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) if !policyInfo.IsSuccessful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range ruleInfos { + for _, r := range engineResponse.RuleInfos { glog.Warning(r.Msgs) } - } else if len(patches) > 0 { + } else if len(engineResponse.Patches) > 0 { glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns) - patchedResource, err = engine.ApplyPatches(rawResource, patches) + patchedResource, err = engine.ApplyPatches(rawResource, engineResponse.Patches) if err != nil { return nil, fmt.Errorf("Unable to apply mutation patches:\n%v", err) } // Process Validation - ruleInfos, err := engine.Validate(*policy, patchedResource, *gvk) - if err != nil { - // This is not policy error - // but if unable to parse request raw resource - // TODO : create event ? dont think so - glog.Error(err) - return patchedResource, err - } - policyInfo.AddRuleInfos(ruleInfos) + engineResponse := engine.Validate(*policy, *resource) + + policyInfo.AddRuleInfos(engineResponse.RuleInfos) if !policyInfo.IsSuccessful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range ruleInfos { + for _, r := range engineResponse.RuleInfos { glog.Warning(r.Msgs) } return patchedResource, fmt.Errorf("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - } else if len(ruleInfos) > 0 { + } else if len(engineResponse.RuleInfos) > 0 { glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns) } } return patchedResource, nil } -func extractPolicy(fileDir string) (*kubepolicy.Policy, error) { - policy := &kubepolicy.Policy{} +func extractPolicy(fileDir string) (*kyverno.Policy, error) { + policy := &kyverno.Policy{} file, err := loadFile(fileDir) if err != nil { diff --git a/pkg/kyverno/apply/util.go b/pkg/kyverno/apply/util.go index bb71715ac3..26e0390300 100644 --- a/pkg/kyverno/apply/util.go +++ b/pkg/kyverno/apply/util.go @@ -9,6 +9,7 @@ import ( "github.com/golang/glog" yamlv2 "gopkg.in/yaml.v2" + unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" rest "k8s.io/client-go/rest" clientcmd "k8s.io/client-go/tools/clientcmd" ) @@ -93,3 +94,12 @@ func scanDir(dir string) ([]string, error) { return res[1:], nil } +func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) { + resource := &unstructured.Unstructured{} + err := resource.UnmarshalJSON(data) + if err != nil { + glog.V(4).Infof("failed to unmarshall resource: %v", err) + return nil, err + } + return resource, nil +} diff --git a/test/generate-resource/main.go b/test/generate-resource/main.go index ee5a66b579..6a8d52bd59 100644 --- a/test/generate-resource/main.go +++ b/test/generate-resource/main.go @@ -10,7 +10,7 @@ import ( "path/filepath" "strconv" - kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" yaml "k8s.io/apimachinery/pkg/util/yaml" ) @@ -21,7 +21,7 @@ func main() { } func generatePolicies() error { - var policy *kubepolicy.Policy + var policy *kyverno.Policy file, err := ioutil.ReadFile(policyPath) if err != nil { From fdfa074954650daef734aa540b60f956156b500f Mon Sep 17 00:00:00 2001 From: shivkumar dudhani Date: Wed, 21 Aug 2019 16:42:42 -0700 Subject: [PATCH 66/66] return on delete request --- pkg/webhooks/policyvalidation.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/webhooks/policyvalidation.go b/pkg/webhooks/policyvalidation.go index abeca02c76..8d815907d8 100644 --- a/pkg/webhooks/policyvalidation.go +++ b/pkg/webhooks/policyvalidation.go @@ -17,19 +17,18 @@ func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionReques admissionResp := &v1beta1.AdmissionResponse{ Allowed: true, } + // nothing to do on DELETE + if request.Operation == v1beta1.Delete { + return admissionResp + } raw := request.Object.Raw - if request.Operation == v1beta1.Delete { - raw = request.OldObject.Raw - } if err := json.Unmarshal(raw, &policy); err != nil { glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err) return &v1beta1.AdmissionResponse{Allowed: false} } - - if request.Operation != v1beta1.Delete { - admissionResp = ws.validateUniqueRuleName(policy) - } + // check for uniqueness of rule names while CREATE/DELET + admissionResp = ws.validateUniqueRuleName(policy) if admissionResp.Allowed { ws.manageWebhookConfigurations(*policy, request.Operation)