mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
replace policyInfo with engineResponse
This commit is contained in:
parent
b062d70e29
commit
5b80da32ba
19 changed files with 721 additions and 532 deletions
|
@ -2,7 +2,6 @@ package engine
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
|
@ -10,7 +9,6 @@ import (
|
|||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -18,43 +16,49 @@ import (
|
|||
)
|
||||
|
||||
//Generate apply generation rules on a resource
|
||||
func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) (response EngineResponse) {
|
||||
func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) (response EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
// policy information
|
||||
func() {
|
||||
// set policy information
|
||||
response.PolicyResponse.Policy = policy.Name
|
||||
// resource details
|
||||
response.PolicyResponse.Resource.Name = ns.GetName()
|
||||
response.PolicyResponse.Resource.Kind = ns.GetKind()
|
||||
response.PolicyResponse.Resource.APIVersion = ns.GetAPIVersion()
|
||||
}()
|
||||
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)
|
||||
response.PolicyResponse.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying generation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
|
||||
glog.V(4).Infof("Generation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
|
||||
}()
|
||||
incrementAppliedRuleCount := func() {
|
||||
// rules applied succesfully count
|
||||
response.RulesAppliedCount++
|
||||
response.PolicyResponse.RulesAppliedCount++
|
||||
}
|
||||
|
||||
ris := []info.RuleInfo{}
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if rule.Generation == (kyverno.Generation{}) {
|
||||
continue
|
||||
}
|
||||
glog.V(4).Infof("applying policy %s generate rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName())
|
||||
ri := info.NewRuleInfo(rule.Name, info.Generation)
|
||||
err := applyRuleGenerator(client, ns, rule.Generation, policy.GetCreationTimestamp())
|
||||
if err != nil {
|
||||
ri.Fail()
|
||||
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 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)
|
||||
ruleResponse := applyRuleGenerator(client, ns, rule, policy.GetCreationTimestamp())
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
response.RuleInfos = ris
|
||||
return response
|
||||
}
|
||||
|
||||
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen kyverno.Generation, policyCreationTime metav1.Time) error {
|
||||
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, rule kyverno.Rule, policyCreationTime metav1.Time) (response RuleResponse) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started applying generation rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Generation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying generation rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
var err error
|
||||
resource := &unstructured.Unstructured{}
|
||||
var rdata map[string]interface{}
|
||||
|
@ -63,66 +67,84 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen
|
|||
nsCreationTime := ns.GetCreationTimestamp()
|
||||
return nsCreationTime.Before(&policyCreationTime)
|
||||
}()
|
||||
if gen.Data != nil {
|
||||
if rule.Generation.Data != nil {
|
||||
glog.V(4).Info("generate rule: creates new resource")
|
||||
// 1> Check if resource exists
|
||||
obj, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
||||
obj, err := client.GetResource(rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
if err == nil {
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present. checking if it contains the required configuration", gen.Kind, ns.GetName(), gen.Name)
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present. checking if it contains the required configuration", rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
// 2> If already exsists, then verify the content is contained
|
||||
// found the resource
|
||||
// check if the rule is create, if yes, then verify if the specified configuration is present in the resource
|
||||
ok, err := checkResource(gen.Data, obj)
|
||||
ok, err := checkResource(rule.Generation.Data, obj)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("generate rule:: unable to check if configuration %v, is present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name)
|
||||
return err
|
||||
glog.V(4).Infof("generate rule:: unable to check if configuration %v, is present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("unable to check if configuration %v, is present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
if !ok {
|
||||
glog.V(4).Infof("generate rule:: configuration %v not present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name)
|
||||
return errors.New("rule configuration not present in resource")
|
||||
glog.V(4).Infof("generate rule:: configuration %v not present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("configuration %v not present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
glog.V(4).Infof("generate rule: required configuration %v is present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name)
|
||||
return nil
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("required configuration %v is present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
rdata, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&gen.Data)
|
||||
rdata, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&rule.Generation.Data)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to parse the specified resource spec %v: %v", rule.Generation.Data, err)
|
||||
return response
|
||||
}
|
||||
}
|
||||
if gen.Clone != (kyverno.CloneFrom{}) {
|
||||
if rule.Generation.Clone != (kyverno.CloneFrom{}) {
|
||||
glog.V(4).Info("generate rule: clone resource")
|
||||
// 1> Check if resource exists
|
||||
_, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
||||
_, err := client.GetResource(rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
if err == nil {
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present", gen.Kind, ns.GetName(), gen.Name)
|
||||
return nil
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present", rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("resource %s/%s/%s already present", rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
// 2> If clone already exists return
|
||||
resource, err = client.GetResource(gen.Kind, gen.Clone.Namespace, gen.Clone.Name)
|
||||
resource, err = client.GetResource(rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
|
||||
if err != nil {
|
||||
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 not present: %v", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name, err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("clone reference resource %s/%s/%s not present: %v", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name, err)
|
||||
return response
|
||||
}
|
||||
glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", gen.Kind, gen.Clone.Namespace, gen.Clone.Name)
|
||||
glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
|
||||
rdata = resource.UnstructuredContent()
|
||||
}
|
||||
if processExisting {
|
||||
glog.V(4).Infof("resource %s not found in existing namespace %s", rule.Generation.Name, ns.GetName())
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("resource %s not found in existing namespace %s", rule.Generation.Name, ns.GetName())
|
||||
// for existing resources we generate an error which indirectly generates a policy violation
|
||||
return fmt.Errorf("resource %s not found in existing namespace %s", gen.Name, ns.GetName())
|
||||
return response
|
||||
}
|
||||
resource.SetUnstructuredContent(rdata)
|
||||
resource.SetName(gen.Name)
|
||||
resource.SetName(rule.Generation.Name)
|
||||
resource.SetNamespace(ns.GetName())
|
||||
// Reset resource version
|
||||
resource.SetResourceVersion("")
|
||||
_, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false)
|
||||
_, err = client.CreateResource(rule.Generation.Kind, ns.GetName(), resource, false)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
return err
|
||||
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", rule.Generation.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("unable to create resource %s/%s/%s: %v", rule.Generation.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
return response
|
||||
}
|
||||
glog.V(4).Infof("generate rule: created resource %s/%s/%s", gen.Kind, resource.GetNamespace(), resource.GetName())
|
||||
return nil
|
||||
glog.V(4).Infof("generate rule: created resource %s/%s/%s", rule.Generation.Kind, resource.GetNamespace(), resource.GetName())
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("created resource %s/%s/%s", rule.Generation.Kind, resource.GetNamespace(), resource.GetName())
|
||||
return response
|
||||
}
|
||||
|
||||
//checkResource checks if the config is present in th eresource
|
||||
|
|
|
@ -6,124 +6,123 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// Mutate performs mutation. Overlay first and then mutation patches
|
||||
|
||||
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++
|
||||
}
|
||||
// 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 {
|
||||
glog.Errorf("unable to marshal resource : %v\n", err)
|
||||
}
|
||||
// patchedDocument, err := resource.MarshalJSON()
|
||||
// if err != nil {
|
||||
// glog.Errorf("unable to marshal resource : %v\n", err)
|
||||
// }
|
||||
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
response.PatchedResource = resource
|
||||
return response
|
||||
}
|
||||
// if err != nil {
|
||||
// glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
// response.PatchedResource = resource
|
||||
// return response
|
||||
// }
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
|
||||
continue
|
||||
}
|
||||
// for _, rule := range policy.Spec.Rules {
|
||||
// if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// check if the resource satisfies the filter conditions defined in the rule
|
||||
//TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// dont statisfy a policy rule resource description
|
||||
ok := MatchesResourceDescription(resource, rule)
|
||||
if !ok {
|
||||
glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
continue
|
||||
}
|
||||
// // check if the resource satisfies the filter conditions defined in the rule
|
||||
// //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// // dont statisfy a policy rule resource description
|
||||
// ok := MatchesResourceDescription(resource, rule)
|
||||
// if !ok {
|
||||
// glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
// continue
|
||||
// }
|
||||
|
||||
ruleInfo := info.NewRuleInfo(rule.Name, info.Mutation)
|
||||
// ruleInfo := info.NewRuleInfo(rule.Name, info.Mutation)
|
||||
|
||||
// Process Overlay
|
||||
if rule.Mutation.Overlay != nil {
|
||||
// ruleRespone := processOverlay(rule, res)
|
||||
rulePatches, err = processOverlay(rule, patchedDocument)
|
||||
if err == nil {
|
||||
if len(rulePatches) == 0 {
|
||||
// if array elements dont match then we skip(nil patch, no error)
|
||||
// or if acnohor is defined and doenst match
|
||||
// policy is not applicable
|
||||
glog.V(4).Info("overlay does not match, so skipping applying rule")
|
||||
continue
|
||||
}
|
||||
// // Process Overlay
|
||||
// if rule.Mutation.Overlay != nil {
|
||||
// // ruleRespone := processOverlay(rule, res)
|
||||
// rulePatches, err = processOverlay(rule, patchedDocument)
|
||||
// if err == nil {
|
||||
// if len(rulePatches) == 0 {
|
||||
// // if array elements dont match then we skip(nil patch, no error)
|
||||
// // or if acnohor is defined and doenst match
|
||||
// // policy is not applicable
|
||||
// glog.V(4).Info("overlay does not match, so skipping applying rule")
|
||||
// continue
|
||||
// }
|
||||
|
||||
ruleInfo.Addf("Rule %s: Overlay succesfully applied.", rule.Name)
|
||||
// ruleInfo.Addf("Rule %s: Overlay succesfully applied.", rule.Name)
|
||||
|
||||
// strip slashes from string
|
||||
ruleInfo.Patches = rulePatches
|
||||
allPatches = append(allPatches, rulePatches...)
|
||||
// // strip slashes from string
|
||||
// ruleInfo.Patches = rulePatches
|
||||
// allPatches = append(allPatches, rulePatches...)
|
||||
|
||||
glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
} else {
|
||||
glog.V(4).Infof("failed to apply overlay: %v", err)
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Addf("failed to apply overlay: %v", err)
|
||||
}
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
// glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
// } else {
|
||||
// glog.V(4).Infof("failed to apply overlay: %v", err)
|
||||
// ruleInfo.Fail()
|
||||
// ruleInfo.Addf("failed to apply overlay: %v", err)
|
||||
// }
|
||||
// incrementAppliedRuleCount()
|
||||
// }
|
||||
|
||||
// Process Patches
|
||||
if len(rule.Mutation.Patches) != 0 {
|
||||
rulePatches, errs = processPatches(rule, patchedDocument)
|
||||
if len(errs) > 0 {
|
||||
ruleInfo.Fail()
|
||||
for _, err := range errs {
|
||||
glog.V(4).Infof("failed to apply patches: %v", err)
|
||||
ruleInfo.Addf("patches application has failed, err %v.", err)
|
||||
}
|
||||
} else {
|
||||
glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
ruleInfo.Addf("Patches succesfully applied.")
|
||||
// // Process Patches
|
||||
// if len(rule.Mutation.Patches) != 0 {
|
||||
// rulePatches, errs = processPatches(rule, patchedDocument)
|
||||
// if len(errs) > 0 {
|
||||
// ruleInfo.Fail()
|
||||
// for _, err := range errs {
|
||||
// glog.V(4).Infof("failed to apply patches: %v", err)
|
||||
// ruleInfo.Addf("patches application has failed, err %v.", err)
|
||||
// }
|
||||
// } else {
|
||||
// glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
// ruleInfo.Addf("Patches succesfully applied.")
|
||||
|
||||
ruleInfo.Patches = rulePatches
|
||||
allPatches = append(allPatches, rulePatches...)
|
||||
}
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
// ruleInfo.Patches = rulePatches
|
||||
// allPatches = append(allPatches, rulePatches...)
|
||||
// }
|
||||
// incrementAppliedRuleCount()
|
||||
// }
|
||||
|
||||
patchedDocument, err = ApplyPatches(patchedDocument, rulePatches)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to apply patches on ruleName=%s, err%v\n:", rule.Name, err)
|
||||
}
|
||||
// patchedDocument, err = ApplyPatches(patchedDocument, rulePatches)
|
||||
// if err != nil {
|
||||
// glog.Errorf("Failed to apply patches on ruleName=%s, err%v\n:", rule.Name, err)
|
||||
// }
|
||||
|
||||
ris = append(ris, ruleInfo)
|
||||
}
|
||||
// ris = append(ris, ruleInfo)
|
||||
// }
|
||||
|
||||
patchedResource, err := ConvertToUnstructured(patchedDocument)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to convert patched resource to unstructuredtype, err%v\n:", err)
|
||||
response.PatchedResource = resource
|
||||
return response
|
||||
}
|
||||
// patchedResource, err := ConvertToUnstructured(patchedDocument)
|
||||
// if err != nil {
|
||||
// glog.Errorf("Failed to convert patched resource to unstructuredtype, err%v\n:", err)
|
||||
// response.PatchedResource = resource
|
||||
// return response
|
||||
// }
|
||||
|
||||
response.Patches = allPatches
|
||||
response.PatchedResource = *patchedResource
|
||||
response.RuleInfos = ris
|
||||
return response
|
||||
}
|
||||
// response.Patches = allPatches
|
||||
// response.PatchedResource = *patchedResource
|
||||
// response.RuleInfos = ris
|
||||
// return response
|
||||
// }
|
||||
|
||||
//MutateNew ...
|
||||
func MutateNew(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponseNew) {
|
||||
|
|
|
@ -94,3 +94,23 @@ func (er EngineResponseNew) GetPatches() []byte {
|
|||
// join patches
|
||||
return JoinPatches(patches)
|
||||
}
|
||||
|
||||
//GetFailedRules returns failed rules
|
||||
func (er EngineResponseNew) GetFailedRules() []string {
|
||||
return er.getRules(false)
|
||||
}
|
||||
|
||||
//GetSuccessRules returns success rules
|
||||
func (er EngineResponseNew) GetSuccessRules() []string {
|
||||
return er.getRules(true)
|
||||
}
|
||||
|
||||
func (er EngineResponseNew) getRules(success bool) []string {
|
||||
var rules []string
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
if r.Success == success {
|
||||
rules = append(rules, r.Name)
|
||||
}
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/minio/minio/pkg/wildcard"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
|
@ -19,17 +18,17 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
//EngineResponse provides the response to the application of a policy rule set on a resource
|
||||
type EngineResponse struct {
|
||||
// JSON patches for mutation rules
|
||||
Patches [][]byte
|
||||
// Resource patched with the policy changes
|
||||
PatchedResource unstructured.Unstructured
|
||||
// Rule details
|
||||
RuleInfos []info.RuleInfo
|
||||
// PolicyS
|
||||
EngineStats
|
||||
}
|
||||
// //EngineResponse provides the response to the application of a policy rule set on a resource
|
||||
// type EngineResponse struct {
|
||||
// // JSON patches for mutation rules
|
||||
// Patches [][]byte
|
||||
// // Resource patched with the policy changes
|
||||
// PatchedResource unstructured.Unstructured
|
||||
// // Rule details
|
||||
// RuleInfos []info.RuleInfo
|
||||
// // PolicyS
|
||||
// EngineStats
|
||||
// }
|
||||
|
||||
// type EngineResponseNew struct {
|
||||
// // error while processing engine action
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
@ -12,62 +11,61 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// Validate handles validating admission request
|
||||
// Checks the target resources for rules defined in the policy
|
||||
func Validate(policy kyverno.Policy, 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
|
||||
}
|
||||
// // Validate handles validating admission request
|
||||
// // Checks the target resources for rules defined in the policy
|
||||
// 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 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
|
||||
// var ruleInfos []info.RuleInfo
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
continue
|
||||
}
|
||||
// for _, rule := range policy.Spec.Rules {
|
||||
// if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// check if the resource satisfies the filter conditions defined in the rule
|
||||
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// dont statisfy a policy rule resource description
|
||||
ok := MatchesResourceDescription(resource, rule)
|
||||
if !ok {
|
||||
glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
continue
|
||||
}
|
||||
// // check if the resource satisfies the filter conditions defined in the rule
|
||||
// // TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// // dont statisfy a policy rule resource description
|
||||
// ok := MatchesResourceDescription(resource, rule)
|
||||
// if !ok {
|
||||
// glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
// continue
|
||||
// }
|
||||
|
||||
// ruleInfo := validatePatterns(resource, rule)
|
||||
incrementAppliedRuleCount()
|
||||
// ruleInfos = append(ruleInfos, ruleInfo)
|
||||
}
|
||||
response.RuleInfos = ruleInfos
|
||||
return response
|
||||
}
|
||||
// // ruleInfo := validatePatterns(resource, rule)
|
||||
// incrementAppliedRuleCount()
|
||||
// // ruleInfos = append(ruleInfos, ruleInfo)
|
||||
// }
|
||||
// response.RuleInfos = ruleInfos
|
||||
// return response
|
||||
// }
|
||||
|
||||
// validatePatterns validate pattern and anyPattern
|
||||
func validatePatterns(resource unstructured.Unstructured, rule kyverno.Rule) (response RuleResponse) {
|
||||
|
|
|
@ -28,7 +28,7 @@ type Generator struct {
|
|||
|
||||
//Interface to generate event
|
||||
type Interface interface {
|
||||
Add(infoList ...*Info)
|
||||
Add(infoList ...Info)
|
||||
}
|
||||
|
||||
//NewEventGenerator to generate a new event controller
|
||||
|
@ -69,9 +69,9 @@ func initRecorder(client *client.Client) record.EventRecorder {
|
|||
}
|
||||
|
||||
//Add queues an event for generation
|
||||
func (gen *Generator) Add(infos ...*Info) {
|
||||
func (gen *Generator) Add(infos ...Info) {
|
||||
for _, info := range infos {
|
||||
gen.queue.Add(*info)
|
||||
gen.queue.Add(info)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,3 +180,24 @@ func NewEvent(rkind string, rnamespace string, rname string, reason Reason, mess
|
|||
Message: msgText,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEventNew(
|
||||
rkind,
|
||||
rapiVersion,
|
||||
rnamespace,
|
||||
rname,
|
||||
reason string,
|
||||
message MsgKey,
|
||||
args ...interface{}) Info {
|
||||
msgText, err := getEventMsg(message, args...)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
return Info{
|
||||
Kind: rkind,
|
||||
Name: rname,
|
||||
Namespace: rnamespace,
|
||||
Reason: reason,
|
||||
Message: msgText,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,8 +202,8 @@ func (nsc *NamespaceController) syncNamespace(key string) error {
|
|||
n := namespace.DeepCopy()
|
||||
|
||||
// process generate rules
|
||||
policyInfos := nsc.processNamespace(*n)
|
||||
engineResponses := nsc.processNamespace(*n)
|
||||
// report errors
|
||||
nsc.report(policyInfos)
|
||||
nsc.report(engineResponses)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/policy"
|
||||
policyctr "github.com/nirmata/kyverno/pkg/policy"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
@ -85,13 +85,12 @@ func buildKey(policy, pv, kind, ns, name, rv string) string {
|
|||
return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv
|
||||
}
|
||||
|
||||
func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []info.PolicyInfo {
|
||||
var policyInfos []info.PolicyInfo
|
||||
func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []engine.EngineResponseNew {
|
||||
// convert to unstructured
|
||||
unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&namespace)
|
||||
if err != nil {
|
||||
glog.Infof("unable to convert to unstructured, not processing any policies: %v", err)
|
||||
return policyInfos
|
||||
return nil
|
||||
}
|
||||
nsc.rm.Drop()
|
||||
|
||||
|
@ -100,18 +99,20 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i
|
|||
// get all the policies that have a generate rule and resource description satifies the namespace
|
||||
// apply policy on resource
|
||||
policies := listpolicies(ns, nsc.pLister)
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
for _, policy := range policies {
|
||||
// pre-processing, check if the policy and resource version has been processed before
|
||||
if !nsc.rm.ProcessResource(policy.Name, policy.ResourceVersion, ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion()) {
|
||||
glog.V(4).Infof("policy %s with resource version %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion())
|
||||
continue
|
||||
}
|
||||
policyInfo := applyPolicy(nsc.client, ns, *policy, nsc.policyStatus)
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
engineResponse := applyPolicy(nsc.client, ns, *policy, nsc.policyStatus)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
|
||||
// post-processing, register the resource as processed
|
||||
nsc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion())
|
||||
}
|
||||
return policyInfos
|
||||
return engineResponses
|
||||
}
|
||||
|
||||
func listpolicies(ns unstructured.Unstructured, pLister kyvernolister.PolicyLister) []*kyverno.Policy {
|
||||
|
@ -139,17 +140,23 @@ func listpolicies(ns unstructured.Unstructured, pLister kyvernolister.PolicyList
|
|||
return filteredpolicies
|
||||
}
|
||||
|
||||
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) {
|
||||
func applyPolicy(client *client.Client, resource unstructured.Unstructured, p kyverno.Policy, policyStatus policyctr.PolicyStatusInterface) engine.EngineResponseNew {
|
||||
var policyStats []policyctr.PolicyStat
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.GenerationExecutionTime = er.ExecutionTime
|
||||
ps.Stats.RulesAppliedCount = er.RulesAppliedCount
|
||||
ps.Stats.MutationExecutionTime = policyResponse.ProcessingTime
|
||||
ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
|
||||
policyStats = append(policyStats, ps)
|
||||
}
|
||||
// send stats for aggregation
|
||||
sendStat := func(blocked bool) {
|
||||
//SEND
|
||||
policyStatus.SendStat(ps)
|
||||
for _, stat := range policyStats {
|
||||
stat.Stats.ResourceBlocked = utils.Btoi(blocked)
|
||||
//SEND
|
||||
policyStatus.SendStat(stat)
|
||||
}
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
@ -157,13 +164,11 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, p ky
|
|||
defer func() {
|
||||
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(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)
|
||||
gatherStat(p.Name, engineResponse.PolicyResponse)
|
||||
//send stats
|
||||
sendStat(false)
|
||||
|
||||
return policyInfo
|
||||
return engineResponse
|
||||
}
|
||||
|
|
|
@ -4,56 +4,54 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/policyviolation"
|
||||
)
|
||||
|
||||
func (nsc *NamespaceController) report(policyInfos []info.PolicyInfo) {
|
||||
func (nsc *NamespaceController) report(engineResponses []engine.EngineResponseNew) {
|
||||
// generate events
|
||||
// generate policy violations
|
||||
for _, policyInfo := range policyInfos {
|
||||
for _, er := range engineResponses {
|
||||
// events
|
||||
// success - policy applied on resource
|
||||
// failure - policy/rule failed to apply on the resource
|
||||
reportEvents(policyInfo, nsc.eventGen)
|
||||
reportEvents(er, nsc.eventGen)
|
||||
// policy violations
|
||||
// failure - policy/rule failed to apply on the resource
|
||||
}
|
||||
|
||||
// generate policy violation
|
||||
policyviolation.GeneratePolicyViolations(nsc.pvListerSynced, nsc.pvLister, nsc.kyvernoClient, policyInfos)
|
||||
|
||||
policyviolation.CreatePV(nsc.pvLister, nsc.kyvernoClient, engineResponses)
|
||||
}
|
||||
|
||||
//reportEvents generates events for the failed resources
|
||||
func reportEvents(policyInfo info.PolicyInfo, eventGen event.Interface) {
|
||||
|
||||
if policyInfo.IsSuccessful() {
|
||||
func reportEvents(engineResponse engine.EngineResponseNew, eventGen event.Interface) {
|
||||
if engineResponse.IsSuccesful() {
|
||||
return
|
||||
}
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
for _, rule := range policyInfo.Rules {
|
||||
if rule.IsSuccessful() {
|
||||
continue
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", engineResponse.PolicyResponse.Policy, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
for _, rule := range engineResponse.PolicyResponse.Rules {
|
||||
if rule.Success {
|
||||
return
|
||||
}
|
||||
|
||||
// generate event on resource for each failed rule
|
||||
e := &event.Info{}
|
||||
e.Kind = policyInfo.RKind
|
||||
e.Namespace = policyInfo.RNamespace
|
||||
e.Name = policyInfo.RName
|
||||
glog.V(4).Infof("generation event on resource %s/%s/%s for policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = engineResponse.PolicyResponse.Resource.Kind
|
||||
e.Namespace = engineResponse.PolicyResponse.Resource.Namespace
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", policyInfo.Name, rule.RuleType.String(), rule.Name, rule.GetErrorString())
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", engineResponse.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
|
||||
eventGen.Add(e)
|
||||
|
||||
}
|
||||
// generate a event on policy for all failed rules
|
||||
e := &event.Info{}
|
||||
glog.V(4).Infof("generation event on policy %s", engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = "Policy"
|
||||
e.Namespace = ""
|
||||
e.Name = policyInfo.Name
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("failed to apply rules %s on resource %s/%s/%s", policyInfo.FailedRules(), policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
e.Message = fmt.Sprintf("failed to apply rules %v on resource %s/%s/%s", engineResponse.GetFailedRules(), engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
eventGen.Add(e)
|
||||
}
|
||||
|
|
|
@ -1,131 +1,117 @@
|
|||
package policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// applyPolicy applies policy on a resource
|
||||
//TODO: generation rules
|
||||
func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured, 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)
|
||||
}
|
||||
|
||||
func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (responses []engine.EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
var policyStats []PolicyStat
|
||||
glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime)
|
||||
defer func() {
|
||||
glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime))
|
||||
}()
|
||||
// glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
||||
policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.MutationExecutionTime = policyResponse.ProcessingTime
|
||||
ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
|
||||
policyStats = append(policyStats, ps)
|
||||
}
|
||||
// send stats for aggregation
|
||||
sendStat := func(blocked bool) {
|
||||
for _, stat := range policyStats {
|
||||
stat.Stats.ResourceBlocked = utils.Btoi(blocked)
|
||||
//SEND
|
||||
policyStatus.SendStat(stat)
|
||||
}
|
||||
}
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
var engineResponse engine.EngineResponseNew
|
||||
var err error
|
||||
|
||||
//MUTATION
|
||||
mruleInfos, err := mutation(policy, resource, policyStatus)
|
||||
policyInfo.AddRuleInfos(mruleInfos)
|
||||
engineResponse, err = mutation(policy, resource, policyStatus)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
if err != nil {
|
||||
return policyInfo, err
|
||||
glog.Error("unable to process mutation rules: %v", err)
|
||||
}
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
//send stats
|
||||
sendStat(false)
|
||||
|
||||
//VALIDATION
|
||||
engineResponse := engine.Validate(policy, resource)
|
||||
if len(engineResponse.RuleInfos) != 0 {
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
}
|
||||
engineResponse = engine.ValidateNew(policy, resource)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
// gather stats
|
||||
gatherStat(policy.Name, engineResponse)
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
//send stats
|
||||
sendStat(false)
|
||||
|
||||
//TODO: GENERATION
|
||||
return policyInfo, nil
|
||||
return engineResponses
|
||||
}
|
||||
func mutation(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (engine.EngineResponseNew, error) {
|
||||
engineResponse := engine.MutateNew(policy, resource)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.V(4).Infof("mutation had errors reporting them")
|
||||
return engineResponse, nil
|
||||
}
|
||||
// Verify if the JSON pathes returned by the Mutate are already applied to the resource
|
||||
if reflect.DeepEqual(resource, engineResponse.PatchedResource) {
|
||||
// resources matches
|
||||
glog.V(4).Infof("resource %s/%s/%s satisfies policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
|
||||
return engineResponse, nil
|
||||
}
|
||||
return getFailedOverallRuleInfo(resource, engineResponse)
|
||||
}
|
||||
|
||||
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 {
|
||||
//no rules processed
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, r := range ruleInfos {
|
||||
if !r.IsSuccessful() {
|
||||
// no failures while processing rule
|
||||
return ruleInfos, nil
|
||||
}
|
||||
}
|
||||
if len(patches) == 0 {
|
||||
// no patches for the resources
|
||||
// either there were failures or the overlay already was satisfied
|
||||
return ruleInfos, nil
|
||||
}
|
||||
|
||||
// (original resource + patch) == (original resource)
|
||||
mergePatches := utils.JoinPatches(patches)
|
||||
patch, err := jsonpatch.DecodePatch(mergePatches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// getFailedOverallRuleInfo gets detailed info for over-all mutation failure
|
||||
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse engine.EngineResponseNew) (engine.EngineResponseNew, error) {
|
||||
rawResource, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
return nil, err
|
||||
glog.V(4).Infof("unable to marshal resource: %v\n", err)
|
||||
return engine.EngineResponseNew{}, err
|
||||
}
|
||||
|
||||
// apply the patches returned by mutate to the original resource
|
||||
patchedResource, err := patch.Apply(rawResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//TODO: this will be removed after the support for patching for each rule
|
||||
ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation)
|
||||
// resource does not match so there was a mutation rule violated
|
||||
for index, rule := range engineResponse.PolicyResponse.Rules {
|
||||
glog.V(4).Info("veriying if policy %s rule %s was applied before to resource %s/%s/%s", engineResponse.PolicyResponse.Policy, rule.Name, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
if len(rule.Patches) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !jsonpatch.Equal(patchedResource, rawResource) {
|
||||
//resource does not match so there was a mutation rule violated
|
||||
// TODO : check the rule name "mutation rules"
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Add("resource does not satisfy mutation rules")
|
||||
} else {
|
||||
ruleInfo.Add("resource satisfys the mutation rule")
|
||||
}
|
||||
patch, err := jsonpatch.DecodePatch(utils.JoinPatches(rule.Patches))
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to decode patch %s: %v", rule.Patches, err)
|
||||
return engine.EngineResponseNew{}, err
|
||||
}
|
||||
|
||||
ruleInfos = append(ruleInfos, ruleInfo)
|
||||
return ruleInfos, nil
|
||||
// apply the patches returned by mutate to the original resource
|
||||
patchedResource, err := patch.Apply(rawResource)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to apply patch %s: %v", rule.Patches, err)
|
||||
return engine.EngineResponseNew{}, err
|
||||
}
|
||||
|
||||
if !jsonpatch.Equal(patchedResource, rawResource) {
|
||||
glog.V(4).Infof("policy %s rule %s condition not satisifed by existing resource", engineResponse.PolicyResponse.Policy, rule.Name)
|
||||
engineResponse.PolicyResponse.Rules[index].Success = false
|
||||
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("rule not satisfied by existing resource. %s", rule.Message)
|
||||
}
|
||||
}
|
||||
return engineResponse, nil
|
||||
}
|
||||
|
|
|
@ -8,17 +8,17 @@ import (
|
|||
"github.com/minio/minio/pkg/wildcard"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []info.PolicyInfo {
|
||||
func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []engine.EngineResponseNew {
|
||||
// Parse through all the resources
|
||||
// drops the cache after configured rebuild time
|
||||
pc.rm.Drop()
|
||||
var policyInfos []info.PolicyInfo
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
// get resource that are satisfy the resource description defined in the rules
|
||||
resourceMap := listResources(pc.client, policy, pc.filterK8Resources)
|
||||
for _, resource := range resourceMap {
|
||||
|
@ -29,21 +29,13 @@ 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, pc.statusAggregator)
|
||||
policyInfos = append(policyInfos, *policyInfo)
|
||||
engineResponse := applyPolicy(policy, resource, pc.statusAggregator)
|
||||
// get engine response for mutation & validation indipendently
|
||||
engineResponses = append(engineResponses, engineResponse...)
|
||||
// post-processing, register the resource as processed
|
||||
pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
||||
}
|
||||
return policyInfos
|
||||
}
|
||||
|
||||
func applyPolicyOnResource(policy kyverno.Policy, resource unstructured.Unstructured, 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
|
||||
}
|
||||
return &policyInfo
|
||||
return engineResponses
|
||||
}
|
||||
|
||||
func listResources(client *client.Client, policy kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]unstructured.Unstructured {
|
||||
|
|
|
@ -4,15 +4,15 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/policyviolation"
|
||||
)
|
||||
|
||||
func (pc *PolicyController) report(policyInfos []info.PolicyInfo) {
|
||||
func (pc *PolicyController) report(engineResponses []engine.EngineResponseNew) {
|
||||
// generate events
|
||||
// generate policy violations
|
||||
for _, policyInfo := range policyInfos {
|
||||
for _, policyInfo := range engineResponses {
|
||||
// events
|
||||
// success - policy applied on resource
|
||||
// failure - policy/rule failed to apply on the resource
|
||||
|
@ -22,37 +22,39 @@ func (pc *PolicyController) report(policyInfos []info.PolicyInfo) {
|
|||
}
|
||||
|
||||
// generate policy violation
|
||||
policyviolation.GeneratePolicyViolations(pc.pvListerSynced, pc.pvLister, pc.kyvernoClient, policyInfos)
|
||||
policyviolation.CreatePV(pc.pvLister, pc.kyvernoClient, engineResponses)
|
||||
}
|
||||
|
||||
//reportEvents generates events for the failed resources
|
||||
func reportEvents(policyInfo info.PolicyInfo, eventGen event.Interface) {
|
||||
|
||||
if policyInfo.IsSuccessful() {
|
||||
func reportEvents(engineResponse engine.EngineResponseNew, eventGen event.Interface) {
|
||||
if engineResponse.IsSuccesful() {
|
||||
return
|
||||
}
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
for _, rule := range policyInfo.Rules {
|
||||
if rule.IsSuccessful() {
|
||||
continue
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", engineResponse.PolicyResponse.Policy, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
for _, rule := range engineResponse.PolicyResponse.Rules {
|
||||
if rule.Success {
|
||||
return
|
||||
}
|
||||
|
||||
// generate event on resource for each failed rule
|
||||
e := &event.Info{}
|
||||
e.Kind = policyInfo.RKind
|
||||
e.Namespace = policyInfo.RNamespace
|
||||
e.Name = policyInfo.RName
|
||||
glog.V(4).Infof("generation event on resource %s/%s/%s for policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = engineResponse.PolicyResponse.Resource.Kind
|
||||
e.Namespace = engineResponse.PolicyResponse.Resource.Namespace
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", policyInfo.Name, rule.RuleType.String(), rule.Name, rule.GetErrorString())
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", engineResponse.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
|
||||
eventGen.Add(e)
|
||||
|
||||
}
|
||||
// generate a event on policy for all failed rules
|
||||
e := &event.Info{}
|
||||
glog.V(4).Infof("generation event on policy %s", engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = "Policy"
|
||||
e.Namespace = ""
|
||||
e.Name = policyInfo.Name
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("failed to apply rules %s on resource %s/%s/%s", policyInfo.FailedRules(), policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
e.Message = fmt.Sprintf("failed to apply rules %v on resource %s/%s/%s", engineResponse.GetFailedRules(), engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
eventGen.Add(e)
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
@ -52,7 +53,80 @@ func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation
|
|||
return pv
|
||||
}
|
||||
|
||||
//generatePolicyViolations generate policyViolation resources for the rules that failed
|
||||
func buildPVForPolicy(er engine.EngineResponseNew) kyverno.PolicyViolation {
|
||||
var violatedRules []kyverno.ViolatedRule
|
||||
glog.V(4).Infof("building policy violation for engine response %v", er)
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
// filter failed/violated rules
|
||||
if !r.Success {
|
||||
vrule := kyverno.ViolatedRule{
|
||||
Name: r.Name,
|
||||
Message: r.Message,
|
||||
Type: r.Type,
|
||||
}
|
||||
violatedRules = append(violatedRules, vrule)
|
||||
}
|
||||
}
|
||||
pv := BuildPolicyViolation(er.PolicyResponse.Policy,
|
||||
kyverno.ResourceSpec{
|
||||
Kind: er.PolicyResponse.Resource.Kind,
|
||||
Namespace: er.PolicyResponse.Resource.Namespace,
|
||||
Name: er.PolicyResponse.Resource.Name,
|
||||
},
|
||||
violatedRules,
|
||||
)
|
||||
return pv
|
||||
}
|
||||
|
||||
//CreatePV creates policy violation resource based on the engine responses
|
||||
func CreatePV(pvLister kyvernolister.PolicyViolationLister, client *kyvernoclient.Clientset, engineResponses []engine.EngineResponseNew) {
|
||||
var pvs []kyverno.PolicyViolation
|
||||
for _, er := range engineResponses {
|
||||
if !er.IsSuccesful() {
|
||||
if pv := buildPVForPolicy(er); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) {
|
||||
pvs = append(pvs, pv)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(pvs) == 0 {
|
||||
return
|
||||
}
|
||||
for _, newPv := range pvs {
|
||||
glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name)
|
||||
// check if there was a previous policy voilation for policy & resource combination
|
||||
curPv, err := getExistingPolicyViolationIfAny(nil, pvLister, newPv)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
if curPv == nil {
|
||||
glog.V(4).Infof("creating new policy violation for policy %s & resource %s/%s/%s", curPv.Spec.Policy, curPv.Spec.ResourceSpec.Kind, curPv.Spec.ResourceSpec.Namespace, curPv.Spec.ResourceSpec.Name)
|
||||
// no existing policy violation, create a new one
|
||||
_, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// compare the policyviolation spec for existing resource if present else
|
||||
if reflect.DeepEqual(curPv.Spec, newPv.Spec) {
|
||||
// if they are equal there has been no change so dont update the polivy violation
|
||||
glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec)
|
||||
continue
|
||||
}
|
||||
// spec changed so update the policyviolation
|
||||
glog.V(4).Infof("creating new policy violation for policy %s & resource %s/%s/%s", curPv.Spec.Policy, curPv.Spec.ResourceSpec.Kind, curPv.Spec.ResourceSpec.Namespace, curPv.Spec.ResourceSpec.Name)
|
||||
//TODO: using a generic name, but would it be helpful to have naming convention for policy violations
|
||||
// as we can only have one policy violation for each (policy + resource) combination
|
||||
_, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//GeneratePolicyViolations generate policyViolation resources for the rules that failed
|
||||
//TODO: check if pvListerSynced is needed
|
||||
func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister kyvernolister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) {
|
||||
var pvs []kyverno.PolicyViolation
|
||||
|
@ -102,6 +176,7 @@ func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister kyve
|
|||
//TODO: change the name
|
||||
func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister kyvernolister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) {
|
||||
// TODO: check for existing ov using label selectors on resource and policy
|
||||
// TODO: there can be duplicates, as the labels have not been assigned to the policy violation yet
|
||||
labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()}
|
||||
ls := &metav1.LabelSelector{}
|
||||
err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil)
|
||||
|
|
|
@ -5,11 +5,8 @@ import (
|
|||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -40,7 +37,7 @@ func generateAnnotationPatches(annotations map[string]string, policyResponse eng
|
|||
annotations = map[string]string{}
|
||||
}
|
||||
var patchResponse response
|
||||
value := generateAnnotationsFromPolicyInfo(policyResponse)
|
||||
value := generateAnnotationsFromPolicyResponse(policyResponse)
|
||||
if value == nil {
|
||||
// no patches or error while processing patches
|
||||
return nil
|
||||
|
@ -71,56 +68,56 @@ func generateAnnotationPatches(annotations map[string]string, policyResponse eng
|
|||
return patchByte
|
||||
}
|
||||
|
||||
func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos []info.PolicyInfo) []byte {
|
||||
annots := resource.GetAnnotations()
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
}
|
||||
// func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos []info.PolicyInfo) []byte {
|
||||
// annots := resource.GetAnnotations()
|
||||
// if annots == nil {
|
||||
// annots = map[string]string{}
|
||||
// }
|
||||
|
||||
var patchResponse response
|
||||
value := annotationFromPolicies(policyInfos)
|
||||
if _, ok := annots[policyAnnotation]; ok {
|
||||
// create update patch string
|
||||
patchResponse = response{
|
||||
Op: "replace",
|
||||
Path: "/metadata/annotations/" + policyAnnotation,
|
||||
Value: string(value),
|
||||
}
|
||||
} else {
|
||||
patchResponse = response{
|
||||
Op: "add",
|
||||
Path: "/metadata/annotations",
|
||||
Value: map[string]string{policyAnnotation: string(value)},
|
||||
}
|
||||
}
|
||||
// var patchResponse response
|
||||
// value := annotationFromPolicies(policyInfos)
|
||||
// if _, ok := annots[policyAnnotation]; ok {
|
||||
// // create update patch string
|
||||
// patchResponse = response{
|
||||
// Op: "replace",
|
||||
// Path: "/metadata/annotations/" + policyAnnotation,
|
||||
// Value: string(value),
|
||||
// }
|
||||
// } else {
|
||||
// patchResponse = response{
|
||||
// Op: "add",
|
||||
// Path: "/metadata/annotations",
|
||||
// Value: map[string]string{policyAnnotation: string(value)},
|
||||
// }
|
||||
// }
|
||||
|
||||
patchByte, _ := json.Marshal(patchResponse)
|
||||
// patchByte, _ := json.Marshal(patchResponse)
|
||||
|
||||
// check the patch
|
||||
_, err := jsonpatch.DecodePatch([]byte("[" + string(patchByte) + "]"))
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to make patch from annotation'%s', err: %v\n ", string(patchByte), err)
|
||||
}
|
||||
// // check the patch
|
||||
// _, err := jsonpatch.DecodePatch([]byte("[" + string(patchByte) + "]"))
|
||||
// if err != nil {
|
||||
// glog.Errorf("Failed to make patch from annotation'%s', err: %v\n ", string(patchByte), err)
|
||||
// }
|
||||
|
||||
return patchByte
|
||||
}
|
||||
// return patchByte
|
||||
// }
|
||||
|
||||
func annotationFromPolicies(policyInfos []info.PolicyInfo) []byte {
|
||||
var policyPatches []policyPatch
|
||||
for _, policyInfo := range policyInfos {
|
||||
var pp policyPatch
|
||||
// func annotationFromPolicies(policyInfos []info.PolicyInfo) []byte {
|
||||
// var policyPatches []policyPatch
|
||||
// for _, policyInfo := range policyInfos {
|
||||
// var pp policyPatch
|
||||
|
||||
pp.PolicyName = policyInfo.Name
|
||||
pp.RulePatches = annotationFromPolicy(policyInfo)
|
||||
policyPatches = append(policyPatches, pp)
|
||||
}
|
||||
// pp.PolicyName = policyInfo.Name
|
||||
// pp.RulePatches = annotationFromPolicy(policyInfo)
|
||||
// policyPatches = append(policyPatches, pp)
|
||||
// }
|
||||
|
||||
result, _ := json.Marshal(policyPatches)
|
||||
// result, _ := json.Marshal(policyPatches)
|
||||
|
||||
return result
|
||||
}
|
||||
// return result
|
||||
// }
|
||||
|
||||
func generateAnnotationsFromPolicyInfo(policyResponse engine.PolicyResponse) []byte {
|
||||
func generateAnnotationsFromPolicyResponse(policyResponse engine.PolicyResponse) []byte {
|
||||
var rulePatches []rulePatch
|
||||
// generate annotation for each mutation JSON patch to be applied on the resource
|
||||
for _, rule := range policyResponse.Rules {
|
||||
|
@ -148,32 +145,32 @@ func generateAnnotationsFromPolicyInfo(policyResponse engine.PolicyResponse) []b
|
|||
return patch
|
||||
}
|
||||
|
||||
func annotationFromPolicy(policyInfo info.PolicyInfo) []rulePatch {
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.V(2).Infof("Policy %s failed, skip preparing annotation\n", policyInfo.Name)
|
||||
return nil
|
||||
}
|
||||
// func annotationFromPolicy(policyInfo info.PolicyInfo) []rulePatch {
|
||||
// if !policyInfo.IsSuccessful() {
|
||||
// glog.V(2).Infof("Policy %s failed, skip preparing annotation\n", policyInfo.Name)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
var rulePatches []rulePatch
|
||||
for _, ruleInfo := range policyInfo.Rules {
|
||||
// var rulePatches []rulePatch
|
||||
// for _, ruleInfo := range policyInfo.Rules {
|
||||
|
||||
for _, patch := range ruleInfo.Patches {
|
||||
var patchmap map[string]string
|
||||
// for _, patch := range ruleInfo.Patches {
|
||||
// var patchmap map[string]string
|
||||
|
||||
if err := json.Unmarshal(patch, &patchmap); err != nil {
|
||||
glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
|
||||
continue
|
||||
}
|
||||
// if err := json.Unmarshal(patch, &patchmap); err != nil {
|
||||
// glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
|
||||
// continue
|
||||
// }
|
||||
|
||||
rp := rulePatch{
|
||||
RuleName: ruleInfo.Name,
|
||||
Op: patchmap["op"],
|
||||
Path: patchmap["path"]}
|
||||
// rp := rulePatch{
|
||||
// RuleName: ruleInfo.Name,
|
||||
// Op: patchmap["op"],
|
||||
// Path: patchmap["path"]}
|
||||
|
||||
rulePatches = append(rulePatches, rp)
|
||||
glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
|
||||
}
|
||||
}
|
||||
// rulePatches = append(rulePatches, rp)
|
||||
// glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
|
||||
// }
|
||||
// }
|
||||
|
||||
return rulePatches
|
||||
}
|
||||
// return rulePatches
|
||||
// }
|
||||
|
|
|
@ -62,11 +62,9 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool
|
|||
if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
continue
|
||||
}
|
||||
// policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
|
||||
glog.V(4).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
|
||||
// glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
// TODO: this can be
|
||||
engineResponse := engine.MutateNew(*policy, *resource)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
|
@ -86,13 +84,11 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool
|
|||
//TODO: check if there is an order to policy application on resource
|
||||
// resource = &engineResponse.PatchedResource
|
||||
}
|
||||
// combine rule patches & annotations
|
||||
|
||||
// ADD EVENTS
|
||||
// if len(patches) > 0 {
|
||||
// eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation)
|
||||
// ws.eventGen.Add(eventsInfo...)
|
||||
// }
|
||||
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
|
||||
ws.eventGen.Add(events...)
|
||||
|
||||
if isResponseSuccesful(engineResponses) {
|
||||
sendStat(false)
|
||||
patch := engine.JoinPatches(patches)
|
||||
|
|
|
@ -3,51 +3,138 @@ package webhooks
|
|||
import (
|
||||
"strings"
|
||||
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
)
|
||||
|
||||
//TODO: change validation from bool -> enum(validation, mutation)
|
||||
func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, ruleType info.RuleType) []*event.Info {
|
||||
var eventsInfo []*event.Info
|
||||
ok, msg := isAdmSuccesful(policyInfoList)
|
||||
// Some policies failed to apply succesfully
|
||||
if !ok {
|
||||
for _, pi := range policyInfoList {
|
||||
if pi.IsSuccessful() {
|
||||
//generateEvents generates event info for the engine responses
|
||||
func generateEvents(engineResponses []engine.EngineResponseNew, onUpdate bool) []event.Info {
|
||||
var events []event.Info
|
||||
if !isResponseSuccesful(engineResponses) {
|
||||
for _, er := range engineResponses {
|
||||
if er.IsSuccesful() {
|
||||
// dont create events on success
|
||||
continue
|
||||
}
|
||||
rules := pi.FailedRules()
|
||||
ruleNames := strings.Join(rules, ";")
|
||||
if !onUpdate {
|
||||
// CREATE
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyApplyBlockCreate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
|
||||
glog.V(3).Infof("Rule(s) %s of policy %s blocked resource creation, error: %s\n", ruleNames, pi.Name, msg)
|
||||
} else {
|
||||
failedRules := er.GetFailedRules()
|
||||
filedRulesStr := strings.Join(failedRules, ";")
|
||||
if onUpdate {
|
||||
var e event.Info
|
||||
// UPDATE
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.RequestBlocked, event.FPolicyApplyBlockUpdate, ruleNames, pi.Name))
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyBlockResourceUpdate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
glog.V(3).Infof("Request blocked events info has prepared for %s/%s and %s/%s\n", policyKind, pi.Name, pi.RKind, pi.RName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !onUpdate {
|
||||
// All policies were applied succesfully
|
||||
// CREATE
|
||||
for _, pi := range policyInfoList {
|
||||
rules := pi.SuccessfulRules()
|
||||
ruleNames := strings.Join(rules, ";")
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.PolicyApplied, event.SRulesApply, ruleNames, pi.Name))
|
||||
// event on resource
|
||||
e = event.NewEventNew(
|
||||
er.PolicyResponse.Resource.Kind,
|
||||
er.PolicyResponse.Resource.APIVersion,
|
||||
er.PolicyResponse.Resource.Namespace,
|
||||
er.PolicyResponse.Resource.Name,
|
||||
event.RequestBlocked.String(),
|
||||
event.FPolicyApplyBlockUpdate,
|
||||
filedRulesStr,
|
||||
er.PolicyResponse.Policy,
|
||||
)
|
||||
glog.V(4).Infof("UPDATE event on resource %s/%s/%s with policy %s", er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name, er.PolicyResponse.Policy)
|
||||
events = append(events, e)
|
||||
|
||||
glog.V(3).Infof("Success event info has prepared for %s/%s\n", pi.RKind, pi.RName)
|
||||
// event on policy
|
||||
e = event.NewEventNew(
|
||||
"Policy",
|
||||
kyverno.SchemeGroupVersion.String(),
|
||||
"",
|
||||
er.PolicyResponse.Policy,
|
||||
event.RequestBlocked.String(),
|
||||
event.FPolicyBlockResourceUpdate,
|
||||
er.PolicyResponse.Resource.Namespace+"/"+er.PolicyResponse.Resource.Name,
|
||||
filedRulesStr,
|
||||
)
|
||||
glog.V(4).Infof("UPDATE event on policy %s", er.PolicyResponse.Policy)
|
||||
events = append(events, e)
|
||||
|
||||
} else {
|
||||
// CREATE
|
||||
// event on policy
|
||||
e := event.NewEventNew(
|
||||
"Policy",
|
||||
kyverno.SchemeGroupVersion.String(),
|
||||
"",
|
||||
er.PolicyResponse.Policy,
|
||||
event.RequestBlocked.String(),
|
||||
event.FPolicyApplyBlockCreate,
|
||||
er.PolicyResponse.Resource.Namespace+"/"+er.PolicyResponse.Resource.Name,
|
||||
filedRulesStr,
|
||||
)
|
||||
glog.V(4).Infof("CREATE event on policy %s", er.PolicyResponse.Policy)
|
||||
events = append(events, e)
|
||||
}
|
||||
}
|
||||
return events
|
||||
}
|
||||
return eventsInfo
|
||||
if !onUpdate {
|
||||
// All policies were applied succesfully
|
||||
// CREATE
|
||||
for _, er := range engineResponses {
|
||||
successRules := er.GetSuccessRules()
|
||||
successRulesStr := strings.Join(successRules, ";")
|
||||
// event on resource
|
||||
e := event.NewEventNew(
|
||||
er.PolicyResponse.Resource.Kind,
|
||||
er.PolicyResponse.Resource.APIVersion,
|
||||
er.PolicyResponse.Resource.Namespace,
|
||||
er.PolicyResponse.Resource.Name,
|
||||
event.PolicyApplied.String(),
|
||||
event.SRulesApply,
|
||||
successRulesStr,
|
||||
er.PolicyResponse.Policy,
|
||||
)
|
||||
events = append(events, e)
|
||||
}
|
||||
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
// //TODO: change validation from bool -> enum(validation, mutation)
|
||||
// func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, ruleType info.RuleType) []*event.Info {
|
||||
// var eventsInfo []*event.Info
|
||||
// ok, msg := isAdmSuccesful(policyInfoList)
|
||||
// // Some policies failed to apply succesfully
|
||||
// if !ok {
|
||||
// for _, pi := range policyInfoList {
|
||||
// if pi.IsSuccessful() {
|
||||
// continue
|
||||
// }
|
||||
// rules := pi.FailedRules()
|
||||
// ruleNames := strings.Join(rules, ";")
|
||||
// if !onUpdate {
|
||||
// // CREATE
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyApplyBlockCreate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
|
||||
// glog.V(3).Infof("Rule(s) %s of policy %s blocked resource creation, error: %s\n", ruleNames, pi.Name, msg)
|
||||
// } else {
|
||||
// // UPDATE
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.RequestBlocked, event.FPolicyApplyBlockUpdate, ruleNames, pi.Name))
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyBlockResourceUpdate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
// glog.V(3).Infof("Request blocked events info has prepared for %s/%s and %s/%s\n", policyKind, pi.Name, pi.RKind, pi.RName)
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if !onUpdate {
|
||||
// // All policies were applied succesfully
|
||||
// // CREATE
|
||||
// for _, pi := range policyInfoList {
|
||||
// rules := pi.SuccessfulRules()
|
||||
// ruleNames := strings.Join(rules, ";")
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.PolicyApplied, event.SRulesApply, ruleNames, pi.Name))
|
||||
|
||||
// glog.V(3).Infof("Success event info has prepared for %s/%s\n", pi.RKind, pi.RName)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return eventsInfo
|
||||
// }
|
||||
|
|
|
@ -50,7 +50,7 @@ func NewWebhookServer(
|
|||
client *client.Client,
|
||||
tlsPair *tlsutils.TlsPemPair,
|
||||
pInformer kyvernoinformer.PolicyInformer,
|
||||
pvInormer kyvernoinformer.PolicyViolationInformer,
|
||||
pvInformer kyvernoinformer.PolicyViolationInformer,
|
||||
eventGen event.Interface,
|
||||
webhookRegistrationClient *webhookconfig.WebhookRegistrationClient,
|
||||
policyStatus policy.PolicyStatusInterface,
|
||||
|
@ -72,8 +72,8 @@ func NewWebhookServer(
|
|||
client: client,
|
||||
kyvernoClient: kyvernoClient,
|
||||
pLister: pInformer.Lister(),
|
||||
pvLister: pvInormer.Lister(),
|
||||
pListerSynced: pInformer.Informer().HasSynced,
|
||||
pvLister: pvInformer.Lister(),
|
||||
pListerSynced: pvInformer.Informer().HasSynced,
|
||||
pvListerSynced: pInformer.Informer().HasSynced,
|
||||
eventGen: eventGen,
|
||||
webhookRegistrationClient: webhookRegistrationClient,
|
||||
|
|
|
@ -7,25 +7,24 @@ import (
|
|||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
)
|
||||
|
||||
const policyKind = "Policy"
|
||||
|
||||
func isAdmSuccesful(policyInfos []info.PolicyInfo) (bool, string) {
|
||||
var admSuccess = true
|
||||
var errMsgs []string
|
||||
for _, pi := range policyInfos {
|
||||
if !pi.IsSuccessful() {
|
||||
admSuccess = false
|
||||
errMsgs = append(errMsgs, fmt.Sprintf("\nPolicy %s failed with following rules", pi.Name))
|
||||
// Get the error rules
|
||||
errorRules := pi.ErrorRules()
|
||||
errMsgs = append(errMsgs, errorRules)
|
||||
}
|
||||
}
|
||||
return admSuccess, strings.Join(errMsgs, ";")
|
||||
}
|
||||
// func isAdmSuccesful(policyInfos []info.PolicyInfo) (bool, string) {
|
||||
// var admSuccess = true
|
||||
// var errMsgs []string
|
||||
// for _, pi := range policyInfos {
|
||||
// if !pi.IsSuccessful() {
|
||||
// admSuccess = false
|
||||
// errMsgs = append(errMsgs, fmt.Sprintf("\nPolicy %s failed with following rules", pi.Name))
|
||||
// // Get the error rules
|
||||
// errorRules := pi.ErrorRules()
|
||||
// errMsgs = append(errMsgs, errorRules)
|
||||
// }
|
||||
// }
|
||||
// return admSuccess, strings.Join(errMsgs, ";")
|
||||
// }
|
||||
|
||||
func isResponseSuccesful(engineReponses []engine.EngineResponseNew) bool {
|
||||
for _, er := range engineReponses {
|
||||
|
@ -113,18 +112,18 @@ const (
|
|||
ReportViolation = "audit"
|
||||
)
|
||||
|
||||
// returns true -> if there is even one policy that blocks resource requst
|
||||
// returns false -> if all the policies are meant to report only, we dont block resource request
|
||||
func toBlock(pis []info.PolicyInfo) bool {
|
||||
for _, pi := range pis {
|
||||
if pi.ValidationFailureAction != ReportViolation {
|
||||
glog.V(3).Infoln("ValidationFailureAction set to enforce, blocking resource ceation")
|
||||
return true
|
||||
}
|
||||
}
|
||||
glog.V(3).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation")
|
||||
return false
|
||||
}
|
||||
// // returns true -> if there is even one policy that blocks resource requst
|
||||
// // returns false -> if all the policies are meant to report only, we dont block resource request
|
||||
// func toBlock(pis []info.PolicyInfo) bool {
|
||||
// for _, pi := range pis {
|
||||
// if pi.ValidationFailureAction != ReportViolation {
|
||||
// glog.V(3).Infoln("ValidationFailureAction set to enforce, blocking resource ceation")
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// glog.V(3).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation")
|
||||
// return false
|
||||
// }
|
||||
|
||||
func processResourceWithPatches(patch []byte, resource []byte) []byte {
|
||||
if patch == nil {
|
||||
|
|
|
@ -3,7 +3,6 @@ package webhooks
|
|||
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"
|
||||
|
@ -19,7 +18,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pat
|
|||
glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
|
||||
|
||||
var policyInfos []info.PolicyInfo
|
||||
var policyStats []policyctr.PolicyStat
|
||||
|
||||
// gather stats from the engine response
|
||||
|
@ -87,14 +85,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pat
|
|||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// ADD EVENTS
|
||||
// if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 {
|
||||
// eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Validation)
|
||||
// // If the validationFailureAction flag is set "audit",
|
||||
// // then we dont block the request and report the violations
|
||||
// ws.eventGen.Add(eventsInfo...)
|
||||
// }
|
||||
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
|
||||
ws.eventGen.Add(events...)
|
||||
|
||||
// If Validation fails then reject the request
|
||||
// violations are created if "audit" flag is set
|
||||
|
@ -106,7 +99,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pat
|
|||
}
|
||||
|
||||
// ADD POLICY VIOLATIONS
|
||||
policyviolation.GeneratePolicyViolations(ws.pvListerSynced, ws.pvLister, ws.kyvernoClient, policyInfos)
|
||||
policyviolation.CreatePV(ws.pvLister, ws.kyvernoClient, engineResponses)
|
||||
|
||||
sendStat(false)
|
||||
return true, ""
|
||||
|
|
Loading…
Add table
Reference in a new issue