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:
commit
ead99660f0
26 changed files with 891 additions and 733 deletions
87
main.go
87
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"
|
||||
)
|
||||
|
||||
|
@ -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)
|
||||
|
|
|
@ -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{},
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
165
pkg/policy/status.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package webhooks
|
||||
package webhookconfig
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package webhooks
|
||||
package webhookconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue