1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

Merge commit '042bc645497ce6713bfca286f8bacd73ef7387b6' into 285_allow_OR_across_overlay_patterns

# Conflicts:
#	pkg/engine/validation.go
This commit is contained in:
Shuting Zhao 2019-08-21 14:13:22 -07:00
commit ead99660f0
26 changed files with 891 additions and 733 deletions

87
main.go
View file

@ -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"
)
@ -34,6 +35,7 @@ const defaultReSyncTime = 10 * time.Second
func main() {
defer glog.Flush()
printVersionInfo()
// profile cpu and memory consuption
prof = enableProfiling(cpu, memory)
// CLIENT CONFIG
@ -58,55 +60,46 @@ func main() {
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, defaultReSyncTime)
// EVENT GENERATOR
// - generate event with retry
egen := event.NewEventGenerator(client, pInformer.Kyverno().V1alpha1().Policies())
// KUBERNETES CLIENT
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 {
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))
// 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)
}
if err = webhookRegistrationClient.Register(); err != nil {
glog.Fatalf("Failed registering Admission Webhooks: %v\n", err)
}
// KYVERNO CRD INFORMER
// watches CRD resources:
// - Policy
// - PolicyVolation
// - cache resync time: 10 seconds
pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, 10*time.Second)
// KUBERNETES RESOURCES INFORMER
// watches namespace resource
// - cache resync time: 10 seconds
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 10*time.Second)
// EVENT GENERATOR
// - 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: violation count
pc, err := policy.NewPolicyController(pclient, client, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, mutatingWebhookConfigurationInformer, webhookRegistrationClient)
// - 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, kubeInformer.Admissionregistration().V1beta1().MutatingWebhookConfigurations(), webhookRegistrationClient)
if err != nil {
glog.Fatalf("error creating policy controller: %v\n", err)
}
// 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 {
@ -115,19 +108,37 @@ 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)
server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, filterK8Resources)
// CONFIGURE CERTIFICATES
tlsPair, err := initTLSPemPair(clientConfig, client)
if err != nil {
glog.Fatalf("Failed to initialize TLS key/certificate pair: %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)
}
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)

View file

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

View file

@ -90,7 +90,17 @@ 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 string `json:"averageMutationRulesExecutionTime"`
// average time required to process the policy Validation rules on a resource
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

View file

@ -3,6 +3,8 @@ package engine
import (
"encoding/json"
"errors"
"time"
"fmt"
"github.com/golang/glog"
@ -16,7 +18,19 @@ 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) (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("Generation Rules appplied count %q for policy %q", response.RulesAppliedCount, policy.Name)
}()
incrementAppliedRuleCount := func() {
// rules applied succesfully count
response.RulesAppliedCount++
}
ris := []info.RuleInfo{}
for _, rule := range policy.Spec.Rules {
if rule.Generation == (kyverno.Generation{}) {
@ -27,15 +41,17 @@ 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)
incrementAppliedRuleCount()
}
return ris
response.RuleInfos = ris
return response
}
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen kyverno.Generation, policyCreationTime metav1.Time) error {
@ -85,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 {

View file

@ -2,6 +2,7 @@ package engine
import (
"reflect"
"time"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
@ -10,11 +11,24 @@ import (
)
// Mutate performs mutation. Overlay first and then mutation patches
func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) 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{}
startTime := time.Now()
glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime)
defer func() {
response.ExecutionTime = time.Since(startTime)
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)
}()
incrementAppliedRuleCount := func() {
// rules applied succesfully count
response.RulesAppliedCount++
}
patchedDocument, err := resource.MarshalJSON()
if err != nil {
@ -23,7 +37,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 {
@ -66,6 +81,7 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes
ruleInfo.Fail()
ruleInfo.Addf("failed to apply overlay: %v", err)
}
incrementAppliedRuleCount()
}
// Process Patches
@ -84,6 +100,7 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) EngineRes
ruleInfo.Patches = rulePatches
allPatches = append(allPatches, rulePatches...)
}
incrementAppliedRuleCount()
}
patchedDocument, err = ApplyPatches(patchedDocument, rulePatches)
@ -97,12 +114,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
}

View file

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

View file

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

View file

@ -1,12 +1,14 @@
package engine
import (
"encoding/json"
"errors"
"fmt"
"path/filepath"
"reflect"
"strconv"
"strings"
"time"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
@ -16,7 +18,34 @@ 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 {
// func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineResponse {
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() {
response.ExecutionTime = time.Since(startTime)
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)
}()
incrementAppliedRuleCount := func() {
// rules applied succesfully count
response.RulesAppliedCount++
}
resourceRaw, err := resource.MarshalJSON()
if err != nil {
glog.V(4).Infof("Skip processing validating rule, unable to marshal resource : %v\n", err)
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)
response.PatchedResource = resource
return response
}
var ruleInfos []info.RuleInfo
for _, rule := range policy.Spec.Rules {
@ -34,10 +63,11 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) EngineR
}
ruleInfo := validatePatterns(resource, rule.Validation, rule.Name)
incrementAppliedRuleCount()
ruleInfos = append(ruleInfos, ruleInfo)
}
return EngineResponse{RuleInfos: ruleInfos}
response.RuleInfos = ruleInfos
return response
}
// validatePatterns validate pattern and anyPattern

View file

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

View file

@ -4,15 +4,13 @@ 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/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"
"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"
@ -108,7 +106,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,15 +139,31 @@ 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, p kyverno.Policy, policyStatus policy.PolicyStatusInterface) info.PolicyInfo {
var ps policy.PolicyStat
gatherStat := func(policyName string, er engine.EngineResponse) {
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)
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)
ruleInfos := engine.Generate(client, policy, resource)
policyInfo.AddRuleInfos(ruleInfos)
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(p.Name, engineResponse)
//send stats
sendStat(false)
return policyInfo
}

View file

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

View file

@ -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,16 +71,18 @@ 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
filterK8Resources []utils.K8Resource
// recieves stats and aggregates details
statusAggregator *PolicyStatusAggregator
}
// 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)
@ -128,6 +130,10 @@ 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, pInformer)
pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient)
return &pc, nil
}
@ -347,6 +353,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
}
@ -396,10 +405,14 @@ 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)
// remove webhook configurations if there are not policies
if err := pc.handleWebhookRegistration(true, nil); err != nil {
glog.Errorln(err)
}
return err
return nil
}
if err != nil {
@ -422,6 +435,8 @@ func (pc *PolicyController) syncPolicy(key string) error {
policyInfos := pc.processExistingResources(*p)
// 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)
}
@ -451,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()
}
@ -470,7 +485,7 @@ func (pc *PolicyController) handleWebhookRegistration(delete bool, policy *kyver
// 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
@ -482,10 +497,19 @@ func (pc *PolicyController) syncStatusOnly(p *kyverno.Policy, pvList []*kyverno.
return err
}
func calculateStatus(pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus {
func (pc *PolicyController) calculateStatus(policyName string, pvList []*kyverno.PolicyViolation) kyverno.PolicyStatus {
violationCount := len(pvList)
status := kyverno.PolicyStatus{
Violations: violationCount,
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()
status.AvgExecutionTimeGeneration = stats.GenerationExecutionTime.String()
}
return status
}
@ -910,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
}

View file

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

165
pkg/policy/status.go Normal file
View file

@ -0,0 +1,165 @@
package policy
import (
"sync"
"time"
"github.com/golang/glog"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
)
//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
//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 {
psa := PolicyStatusAggregator{
startTime: time.Now(),
ch: make(chan PolicyStat),
policyData: map[string]PolicyStatInfo{},
}
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")
defer func() {
glog.V(4).Info("Shutting down aggregator for policy status stats")
}()
for i := 0; i < workers; i++ {
go wait.Until(psa.process, time.Second, stopCh)
}
}
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
// 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)
psa.aggregate(r)
}
}
func (psa *PolicyStatusAggregator) aggregate(ps PolicyStat) {
func() {
glog.V(4).Infof("write lock update policy %s", ps.PolicyName)
psa.mux.Lock()
}()
defer func() {
glog.V(4).Infof("write Unlock update policy %s", ps.PolicyName)
psa.mux.Unlock()
}()
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
}
// aggregate
info.RulesAppliedCount = info.RulesAppliedCount + ps.Stats.RulesAppliedCount
if ps.Stats.ResourceBlocked == 1 {
info.ResourceBlocked++
}
var zeroDuration time.Duration
if info.MutationExecutionTime != zeroDuration {
info.MutationExecutionTime = (info.MutationExecutionTime + ps.Stats.MutationExecutionTime) / 2
glog.V(4).Infof("updated avg mutation time %v", info.MutationExecutionTime)
} else {
info.MutationExecutionTime = ps.Stats.MutationExecutionTime
}
if info.ValidationExecutionTime != zeroDuration {
info.ValidationExecutionTime = (info.ValidationExecutionTime + ps.Stats.ValidationExecutionTime) / 2
glog.V(4).Infof("updated avg validation time %v", info.ValidationExecutionTime)
} 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)
}
//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
}
//PolicyStat stored stats for policy
type PolicyStat struct {
PolicyName string
Stats PolicyStatInfo
}
type PolicyStatInfo struct {
MutationExecutionTime time.Duration
ValidationExecutionTime time.Duration
GenerationExecutionTime time.Duration
RulesAppliedCount int
ResourceBlocked int
}
//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
}
//GetPolicyStatusAggregator returns interface to send policy status stats
func (pc *PolicyController) GetPolicyStatusAggregator() PolicyStatusInterface {
return pc.statusAggregator
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
package webhooks
package webhookconfig
import (
"errors"

View file

@ -1,4 +1,4 @@
package webhooks
package webhookconfig
import (
"bytes"

View file

@ -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.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.Stats.ResourceBlocked = utils.Btoi(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
}

View file

@ -19,8 +19,10 @@ 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"
"github.com/nirmata/kyverno/pkg/webhookconfig"
v1beta1 "k8s.io/api/admission/v1beta1"
"k8s.io/client-go/tools/cache"
)
@ -36,8 +38,10 @@ type WebhookServer struct {
pListerSynced cache.InformerSynced
pvListerSynced cache.InformerSynced
eventGen event.Interface
webhookRegistrationClient *WebhookRegistrationClient
filterK8Resources []utils.K8Resource
webhookRegistrationClient *webhookconfig.WebhookRegistrationClient
// API to send policy stats for aggregation
policyStatus policy.PolicyStatusInterface
filterK8Resources []utils.K8Resource
}
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
@ -49,7 +53,8 @@ func NewWebhookServer(
pInformer kyvernoinformer.PolicyInformer,
pvInormer kyvernoinformer.PolicyViolationInformer,
eventGen event.Interface,
webhookRegistrationClient *WebhookRegistrationClient,
webhookRegistrationClient *webhookconfig.WebhookRegistrationClient,
policyStatus policy.PolicyStatusInterface,
filterK8Resources string) (*WebhookServer, error) {
if tlsPair == nil {
@ -73,6 +78,7 @@ func NewWebhookServer(
pvListerSynced: pInformer.Informer().HasSynced,
eventGen: eventGen,
webhookRegistrationClient: webhookRegistrationClient,
policyStatus: policyStatus,
filterK8Resources: utils.ParseKinds(filterK8Resources),
}
mux := http.NewServeMux()

View file

@ -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.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.Stats.ResourceBlocked = utils.Btoi(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,
}