mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-05 15:37:19 +00:00
initial redesign
This commit is contained in:
parent
3191713e76
commit
b062d70e29
12 changed files with 687 additions and 150 deletions
|
@ -59,6 +59,7 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) (response
|
|||
|
||||
// Process Overlay
|
||||
if rule.Mutation.Overlay != nil {
|
||||
// ruleRespone := processOverlay(rule, res)
|
||||
rulePatches, err = processOverlay(rule, patchedDocument)
|
||||
if err == nil {
|
||||
if len(rulePatches) == 0 {
|
||||
|
@ -123,3 +124,67 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) (response
|
|||
response.RuleInfos = ris
|
||||
return response
|
||||
}
|
||||
|
||||
//MutateNew ...
|
||||
func MutateNew(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
// policy information
|
||||
func() {
|
||||
// set policy information
|
||||
response.PolicyResponse.Policy = policy.Name
|
||||
// resource details
|
||||
response.PolicyResponse.Resource.Name = resource.GetName()
|
||||
response.PolicyResponse.Resource.Namespace = resource.GetNamespace()
|
||||
response.PolicyResponse.Resource.Kind = resource.GetKind()
|
||||
response.PolicyResponse.Resource.APIVersion = resource.GetAPIVersion()
|
||||
}()
|
||||
glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime)
|
||||
defer func() {
|
||||
response.PolicyResponse.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying mutation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
|
||||
glog.V(4).Infof("Mutation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
|
||||
}()
|
||||
incrementAppliedRuleCount := func() {
|
||||
// rules applied succesfully count
|
||||
response.PolicyResponse.RulesAppliedCount++
|
||||
}
|
||||
|
||||
var patchedResource unstructured.Unstructured
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
//TODO: to be checked before calling the resources as well
|
||||
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
|
||||
}
|
||||
// Process Overlay
|
||||
if rule.Mutation.Overlay != nil {
|
||||
var ruleResponse RuleResponse
|
||||
ruleResponse, patchedResource = processOverlayNew(rule, resource)
|
||||
if reflect.DeepEqual(ruleResponse, (RuleResponse{})) {
|
||||
// overlay pattern does not match the resource conditions
|
||||
continue
|
||||
}
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
|
||||
// Process Patches
|
||||
if rule.Mutation.Patches != nil {
|
||||
var ruleResponse RuleResponse
|
||||
ruleResponse, patchedResource = processPatchesNew(rule, resource)
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
}
|
||||
// send the patched resource
|
||||
response.PatchedResource = patchedResource
|
||||
return response
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
|
@ -38,6 +40,60 @@ func processOverlay(rule kyverno.Rule, rawResource []byte) ([][]byte, error) {
|
|||
return patches, err
|
||||
}
|
||||
|
||||
// rawResource handles validating admission request
|
||||
// Checks the target resources for rules defined in the policy
|
||||
// TODO: pass in the unstructured object in stead of raw byte?
|
||||
func processOverlayNew(rule kyverno.Rule, resource unstructured.Unstructured) (response RuleResponse, patchedResource unstructured.Unstructured) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started applying overlay rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Mutation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying overlay rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
patches, err := processOverlayPatches(resource.UnstructuredContent(), rule.Mutation.Overlay)
|
||||
// resource does not satisfy the overlay pattern, we dont apply this rule
|
||||
if err != nil && strings.Contains(err.Error(), "Conditions are not met") {
|
||||
glog.V(4).Infof("Resource %s/%s/%s does not meet the conditions in the rule %s with overlay pattern %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), rule.Name, rule.Mutation.Overlay)
|
||||
//TODO: send zero response and not consider this as applied?
|
||||
return RuleResponse{}, resource
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// rule application failed
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process overlay: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
// convert to RAW
|
||||
resourceRaw, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
glog.Infof("unable to marshall resource: %v", err)
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
joinedPatches := JoinPatches(patches)
|
||||
var patchResource []byte
|
||||
patchResource, err = ApplyPatchNew(resourceRaw, joinedPatches)
|
||||
err = patchedResource.UnmarshalJSON(patchResource)
|
||||
if err != nil {
|
||||
glog.Infof("failed to unmarshall resource to undstructured: %v", err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
// rule application succesfuly
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("succesfully process overlay")
|
||||
response.Patches = patches
|
||||
// apply the patches to the resource
|
||||
return response, patchedResource
|
||||
}
|
||||
func processOverlayPatches(resource, overlay interface{}) ([][]byte, error) {
|
||||
|
||||
if !meetConditions(resource, overlay) {
|
||||
|
@ -65,6 +121,7 @@ func applyOverlay(resource, overlay interface{}, path string) ([][]byte, error)
|
|||
}
|
||||
|
||||
appliedPatches = append(appliedPatches, patch)
|
||||
//TODO : check if return is needed ?
|
||||
}
|
||||
return applyOverlayForSameTypes(resource, overlay, path)
|
||||
}
|
||||
|
|
|
@ -3,9 +3,13 @@ package engine
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
|
@ -84,3 +88,88 @@ func ApplyPatches(resource []byte, patches [][]byte) ([]byte, error) {
|
|||
}
|
||||
return patchedDocument, err
|
||||
}
|
||||
|
||||
//ApplyPatchNew ...
|
||||
func ApplyPatchNew(resource, patch []byte) ([]byte, error) {
|
||||
jsonpatch, err := jsonpatch.DecodePatch(patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedResource, err := jsonpatch.Apply(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return patchedResource, err
|
||||
|
||||
}
|
||||
|
||||
func processPatchesNew(rule kyverno.Rule, resource unstructured.Unstructured) (response RuleResponse, patchedResource unstructured.Unstructured) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Mutation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished JSON patch rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
// convert to RAW
|
||||
resourceRaw, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
glog.Infof("unable to marshall resource: %v", err)
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var patches [][]byte
|
||||
for _, patch := range rule.Mutation.Patches {
|
||||
// JSON patch
|
||||
patchRaw, err := json.Marshal(patch)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to marshall JSON patch %v: %v", patch, err)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
patchResource, err := ApplyPatchNew(resourceRaw, patchRaw)
|
||||
// TODO: continue on error if one of the patches fails, will add the failure event in such case
|
||||
if err != nil && patch.Operation == "remove" {
|
||||
glog.Info(err)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
resourceRaw = patchResource
|
||||
patches = append(patches, patchRaw)
|
||||
}
|
||||
|
||||
// error while processing JSON patches
|
||||
if len(errs) > 0 {
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprint("failed to process JSON patches: %v", func() string {
|
||||
var str []string
|
||||
for _, err := range errs {
|
||||
str = append(str, err.Error())
|
||||
}
|
||||
return strings.Join(str, ";")
|
||||
}())
|
||||
return response, resource
|
||||
}
|
||||
err = patchedResource.UnmarshalJSON(resourceRaw)
|
||||
if err != nil {
|
||||
glog.Info("failed to unmarshall resource to undstructured: %v", err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
// JSON patches processed succesfully
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("succesfully process JSON patches")
|
||||
response.Patches = patches
|
||||
return response, patchedResource
|
||||
}
|
||||
|
|
96
pkg/engine/response.go
Normal file
96
pkg/engine/response.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
//EngineResponseNew engine response to the action
|
||||
type EngineResponseNew struct {
|
||||
// Resource patched with the engine action changes
|
||||
PatchedResource unstructured.Unstructured
|
||||
// Policy Response
|
||||
PolicyResponse PolicyResponse
|
||||
}
|
||||
|
||||
//PolicyResponse policy application response
|
||||
type PolicyResponse struct {
|
||||
// policy name
|
||||
Policy string
|
||||
// resource details
|
||||
Resource ResourceSpec
|
||||
// policy statistics
|
||||
PolicyStats
|
||||
// rule response
|
||||
Rules []RuleResponse
|
||||
// ValidationFailureAction: audit,enforce(default)
|
||||
ValidationFailureAction string
|
||||
}
|
||||
|
||||
//ResourceSpec resource action applied on
|
||||
type ResourceSpec struct {
|
||||
//TODO: support ApiVersion
|
||||
Kind string
|
||||
APIVersion string
|
||||
Namespace string
|
||||
Name string
|
||||
}
|
||||
|
||||
//PolicyStats stores statistics for the single policy application
|
||||
type PolicyStats struct {
|
||||
// time required to process the policy rules on a resource
|
||||
ProcessingTime time.Duration
|
||||
// Count of rules that were applied succesfully
|
||||
RulesAppliedCount int
|
||||
}
|
||||
|
||||
//RuleResponse details for each rule applicatino
|
||||
type RuleResponse struct {
|
||||
// rule name specified in policy
|
||||
Name string
|
||||
// rule type (Mutation,Generation,Validation) for Kyverno Policy
|
||||
Type string
|
||||
// message response from the rule application
|
||||
Message string
|
||||
// JSON patches, for mutation rules
|
||||
Patches [][]byte
|
||||
// success/fail
|
||||
Success bool
|
||||
// statistics
|
||||
RuleStats
|
||||
}
|
||||
|
||||
//ToString ...
|
||||
func (rr RuleResponse) ToString() string {
|
||||
return fmt.Sprintf("rule %s (%s): %v", rr.Name, rr.Type, rr.Message)
|
||||
}
|
||||
|
||||
//RuleStats stores the statisctis for the single rule application
|
||||
type RuleStats struct {
|
||||
// time required to appliy the rule on the resource
|
||||
ProcessingTime time.Duration
|
||||
}
|
||||
|
||||
//IsSuccesful checks if any rule has failed or not
|
||||
func (er EngineResponseNew) IsSuccesful() bool {
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
if !r.Success {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//GetPatches returns all the patches joined
|
||||
func (er EngineResponseNew) GetPatches() []byte {
|
||||
var patches [][]byte
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
if r.Patches != nil {
|
||||
patches = append(patches, r.Patches...)
|
||||
}
|
||||
}
|
||||
// join patches
|
||||
return JoinPatches(patches)
|
||||
}
|
|
@ -21,12 +21,34 @@ import (
|
|||
|
||||
//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
|
||||
// Err error
|
||||
// // Resource patched with the engine action changes
|
||||
// PatchedResource unstructured.Unstructured
|
||||
// // Policy Response
|
||||
// PolicyRespone PolicyResponse
|
||||
// }
|
||||
|
||||
// type PolicyResponse struct {
|
||||
// // policy name
|
||||
// Policy string
|
||||
// // resource details
|
||||
// Resource kyverno.ResourceSpec
|
||||
// }
|
||||
|
||||
// type PolicyStatus
|
||||
|
||||
//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
|
||||
|
@ -518,3 +540,21 @@ func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
|||
}
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
type RuleType int
|
||||
|
||||
const (
|
||||
Mutation RuleType = iota
|
||||
Validation
|
||||
Generation
|
||||
All
|
||||
)
|
||||
|
||||
func (ri RuleType) String() string {
|
||||
return [...]string{
|
||||
"Mutation",
|
||||
"Validation",
|
||||
"Generation",
|
||||
"All",
|
||||
}[ri]
|
||||
}
|
||||
|
|
|
@ -61,53 +61,67 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) (respon
|
|||
continue
|
||||
}
|
||||
|
||||
ruleInfo := validatePatterns(resource, rule.Validation, rule.Name)
|
||||
// ruleInfo := validatePatterns(resource, rule)
|
||||
incrementAppliedRuleCount()
|
||||
ruleInfos = append(ruleInfos, ruleInfo)
|
||||
// ruleInfos = append(ruleInfos, ruleInfo)
|
||||
}
|
||||
response.RuleInfos = ruleInfos
|
||||
return response
|
||||
}
|
||||
|
||||
// validatePatterns validate pattern and anyPattern
|
||||
func validatePatterns(resource unstructured.Unstructured, validation kyverno.Validation, ruleName string) info.RuleInfo {
|
||||
var errs []error
|
||||
ruleInfo := info.NewRuleInfo(ruleName, info.Validation)
|
||||
func validatePatterns(resource unstructured.Unstructured, rule kyverno.Rule) (response RuleResponse) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started applying validation rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Validation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying validation rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
if validation.Pattern != nil {
|
||||
err := validateResourceWithPattern(resource.Object, validation.Pattern)
|
||||
// either pattern or anyPattern can be specified in Validation rule
|
||||
if rule.Validation.Pattern != nil {
|
||||
err := validateResourceWithPattern(resource.Object, rule.Validation.Pattern)
|
||||
if err != nil {
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Addf("Failed to apply pattern: %v", err)
|
||||
} else {
|
||||
ruleInfo.Add("Pattern succesfully validated")
|
||||
glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
// rule application failed
|
||||
glog.V(4).Infof("failed to apply validation for rule %s on resource %s/%s/%s, pattern %v ", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), rule.Validation.Pattern)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to apply pattern: %v", err)
|
||||
return response
|
||||
}
|
||||
return ruleInfo
|
||||
// rule application succesful
|
||||
glog.V(4).Infof("rule %s pattern validated succesfully on resource %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("validation pattern succesfully validated")
|
||||
return response
|
||||
}
|
||||
|
||||
if validation.AnyPattern != nil {
|
||||
for _, pattern := range validation.AnyPattern {
|
||||
//TODO: add comments to explain the flow
|
||||
if rule.Validation.AnyPattern != nil {
|
||||
var errs []error
|
||||
for _, pattern := range rule.Validation.AnyPattern {
|
||||
if err := validateResourceWithPattern(resource.Object, pattern); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
failedPattern := len(errs)
|
||||
patterns := len(validation.AnyPattern)
|
||||
|
||||
// all pattern fail
|
||||
patterns := len(rule.Validation.AnyPattern)
|
||||
// all patterns fail
|
||||
if failedPattern == patterns {
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Addf("None of anyPattern succeed: %v", errs)
|
||||
} else {
|
||||
ruleInfo.Addf("%d/%d patterns succesfully validated", patterns-failedPattern, patterns)
|
||||
// any Pattern application failed
|
||||
glog.V(4).Infof("none of anyPattern were processed: %v", errs)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("None of anyPattern succeed: %v", errs)
|
||||
return response
|
||||
}
|
||||
// any Pattern application succesful
|
||||
glog.V(4).Infof("%d/%d patterns validated succesfully on resource %s/%s", patterns-failedPattern, patterns, resource.GetNamespace(), resource.GetName())
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("%d/%d patterns succesfully validated", patterns-failedPattern, patterns)
|
||||
return response
|
||||
}
|
||||
return ruleInfo
|
||||
}
|
||||
|
||||
return info.RuleInfo{}
|
||||
return RuleResponse{}
|
||||
}
|
||||
|
||||
// validateResourceWithPattern is a start of element-by-element validation process
|
||||
|
@ -328,3 +342,51 @@ func validateArrayOfMaps(resourceMapArray []interface{}, patternMap map[string]i
|
|||
handler := CreateAnchorHandler(anchor, pattern, path)
|
||||
return handler.Handle(resourceMapArray, patternMap, originPattern)
|
||||
}
|
||||
|
||||
//ValidateNew ...
|
||||
func ValidateNew(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
// policy information
|
||||
func() {
|
||||
// set policy information
|
||||
response.PolicyResponse.Policy = policy.Name
|
||||
// resource details
|
||||
response.PolicyResponse.Resource.Name = resource.GetName()
|
||||
response.PolicyResponse.Resource.Namespace = resource.GetNamespace()
|
||||
response.PolicyResponse.Resource.Kind = resource.GetKind()
|
||||
response.PolicyResponse.Resource.APIVersion = resource.GetAPIVersion()
|
||||
response.PolicyResponse.ValidationFailureAction = policy.Spec.ValidationFailureAction
|
||||
}()
|
||||
|
||||
glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime)
|
||||
defer func() {
|
||||
response.PolicyResponse.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("Finished applying validation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
|
||||
glog.V(4).Infof("Validation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
|
||||
}()
|
||||
incrementAppliedRuleCount := func() {
|
||||
// rules applied succesfully count
|
||||
response.PolicyResponse.RulesAppliedCount++
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if rule.Validation.Pattern != nil {
|
||||
ruleResponse := validatePatterns(resource, rule)
|
||||
incrementAppliedRuleCount()
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
|
17
pkg/policy/utils.go
Normal file
17
pkg/policy/utils.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package policy
|
||||
|
||||
import kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
|
||||
// reEvaulatePolicy checks if the policy needs to be re-evaulated
|
||||
// during re-evaulation we remove all the old policy violations and re-create new ones
|
||||
// - Rule count changes
|
||||
// - Rule resource description changes
|
||||
// - Rule operation changes
|
||||
// - Rule name changed
|
||||
func reEvaulatePolicy(curP, oldP *kyverno.Policy) bool {
|
||||
// count of rules changed
|
||||
if len(curP.Spec.Rules) != len(curP.Spec.Rules) {
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -3,6 +3,8 @@ package webhooks
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
|
@ -33,6 +35,42 @@ type response struct {
|
|||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func generateAnnotationPatches(annotations map[string]string, policyResponse engine.PolicyResponse) []byte {
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
var patchResponse response
|
||||
value := generateAnnotationsFromPolicyInfo(policyResponse)
|
||||
if value == nil {
|
||||
// no patches or error while processing patches
|
||||
return nil
|
||||
}
|
||||
if _, ok := annotations[policyAnnotation]; ok {
|
||||
// create update patch string
|
||||
patchResponse = response{
|
||||
Op: "replace",
|
||||
Path: "/metadata/annotations/" + policyAnnotation,
|
||||
Value: string(value),
|
||||
}
|
||||
} else {
|
||||
patchResponse = response{
|
||||
Op: "add",
|
||||
Path: "/metadata/annotations",
|
||||
Value: map[string]string{policyAnnotation: string(value)},
|
||||
}
|
||||
}
|
||||
|
||||
patchByte, _ := json.Marshal(patchResponse)
|
||||
|
||||
// check the patch
|
||||
_, err := jsonpatch.DecodePatch([]byte("[" + string(patchByte) + "]"))
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to make patch from annotation'%s', err: %v\n ", string(patchByte), err)
|
||||
}
|
||||
|
||||
return patchByte
|
||||
}
|
||||
|
||||
func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos []info.PolicyInfo) []byte {
|
||||
annots := resource.GetAnnotations()
|
||||
if annots == nil {
|
||||
|
@ -82,6 +120,34 @@ func annotationFromPolicies(policyInfos []info.PolicyInfo) []byte {
|
|||
return result
|
||||
}
|
||||
|
||||
func generateAnnotationsFromPolicyInfo(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 {
|
||||
var patchmap map[string]string
|
||||
patch := engine.JoinPatches(rule.Patches)
|
||||
if err := json.Unmarshal(patch, &patchmap); err != nil {
|
||||
glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rp := rulePatch{
|
||||
RuleName: rule.Name,
|
||||
Op: patchmap["op"],
|
||||
Path: patchmap["path"]}
|
||||
|
||||
rulePatches = append(rulePatches, rp)
|
||||
glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
|
||||
|
||||
}
|
||||
patch, err := json.Marshal(rulePatches)
|
||||
if err != nil {
|
||||
glog.Info("failed to marshall: %v", err)
|
||||
return nil
|
||||
}
|
||||
return patch
|
||||
}
|
||||
|
||||
func annotationFromPolicy(policyInfo info.PolicyInfo) []rulePatch {
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.V(2).Infof("Policy %s failed, skip preparing annotation\n", policyInfo.Name)
|
||||
|
|
|
@ -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/utils"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
|
@ -12,16 +11,19 @@ import (
|
|||
)
|
||||
|
||||
// HandleMutation handles mutating webhook admission request
|
||||
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, engine.EngineResponse) {
|
||||
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, []byte, string) {
|
||||
glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
|
||||
|
||||
var patches [][]byte
|
||||
var policyInfos []info.PolicyInfo
|
||||
var policyStats []policyctr.PolicyStat
|
||||
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, er engine.EngineResponse) {
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.MutationExecutionTime = 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
|
||||
|
@ -32,85 +34,72 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool
|
|||
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)
|
||||
|
||||
// convert RAW to unstructured
|
||||
resource, err := engine.ConvertToUnstructured(request.Object.Raw)
|
||||
if err != nil {
|
||||
//TODO: skip applying the amiddions control ?
|
||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||
return true, nil, ""
|
||||
}
|
||||
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
|
||||
engineResponse := engine.EngineResponse{PatchedResource: *resource}
|
||||
|
||||
policies, err := ws.pLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
//TODO check if the CRD is created ?
|
||||
// Unable to connect to policy Lister to access policies
|
||||
glog.Errorln("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied")
|
||||
glog.Warning(err)
|
||||
return true, engineResponse
|
||||
return true, nil, ""
|
||||
}
|
||||
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
for _, policy := range policies {
|
||||
|
||||
// check if policy has a rule for the admission request kind
|
||||
if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
// policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
|
||||
glog.V(4).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
|
||||
glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
|
||||
engineResponse = engine.Mutate(*policy, *resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
// 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)
|
||||
// Gather policy application statistics
|
||||
gatherStat(policy.Name, engineResponse)
|
||||
|
||||
// ps := policyctr.NewPolicyStat(policy.Name, engineResponse.ExecutionTime, nil, engineResponse.RulesAppliedCount)
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
glog.V(4).Info("Failed rule details")
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.V(4).Infof("%s: %s\n", r.Name, r.Msgs)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
patches = append(patches, engineResponse.Patches...)
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
// gather patches
|
||||
patches = append(patches, engineResponse.GetPatches())
|
||||
// generate annotations
|
||||
if annPatches := generateAnnotationPatches(resource.GetAnnotations(), engineResponse.PolicyResponse); annPatches != nil {
|
||||
patches = append(patches, annPatches)
|
||||
}
|
||||
glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
||||
|
||||
//TODO: check if there is an order to policy application on resource
|
||||
// resource = &engineResponse.PatchedResource
|
||||
}
|
||||
// combine rule patches & annotations
|
||||
|
||||
// ADD ANNOTATIONS
|
||||
// ADD EVENTS
|
||||
if len(patches) > 0 {
|
||||
eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation)
|
||||
ws.eventGen.Add(eventsInfo...)
|
||||
|
||||
annotation := prepareAnnotationPatches(resource, policyInfos)
|
||||
patches = append(patches, annotation)
|
||||
}
|
||||
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
// Send policy engine Stats
|
||||
if ok {
|
||||
// if len(patches) > 0 {
|
||||
// eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation)
|
||||
// ws.eventGen.Add(eventsInfo...)
|
||||
// }
|
||||
if isResponseSuccesful(engineResponses) {
|
||||
sendStat(false)
|
||||
engineResponse.Patches = patches
|
||||
return true, engineResponse
|
||||
patch := engine.JoinPatches(patches)
|
||||
return true, patch, ""
|
||||
}
|
||||
|
||||
sendStat(true)
|
||||
glog.Errorf("Failed to mutate the resource: %s\n", msg)
|
||||
return false, engineResponse
|
||||
glog.Errorf("Failed to mutate the resource\n")
|
||||
return false, nil, getErrorMsg(engineResponses)
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1"
|
||||
|
@ -24,6 +22,7 @@ import (
|
|||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"github.com/nirmata/kyverno/pkg/webhookconfig"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
|
@ -135,24 +134,43 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
var response *v1beta1.AdmissionResponse
|
||||
|
||||
allowed, engineResponse := ws.HandleMutation(request)
|
||||
if !allowed {
|
||||
// TODO: add failure message to response
|
||||
// MUTATION
|
||||
ok, patches, msg := ws.HandleMutation(request)
|
||||
if !ok {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Status: "Failure",
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
response = ws.HandleValidation(request, engineResponse.PatchedResource)
|
||||
if response.Allowed && len(engineResponse.Patches) > 0 {
|
||||
// patch the resource with patches before handling validation rules
|
||||
patchedResource := processResourceWithPatches(patches, request.Object.Raw)
|
||||
|
||||
// VALIDATION
|
||||
ok, msg = ws.HandleValidation(request, patchedResource)
|
||||
if !ok {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Status: "Failure",
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Succesfful processing of mutation & validation rules in policy
|
||||
patchType := v1beta1.PatchTypeJSONPatch
|
||||
response.Patch = engine.JoinPatches(engineResponse.Patches)
|
||||
response.PatchType = &patchType
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Status: "Success",
|
||||
},
|
||||
Patch: patches,
|
||||
PatchType: &patchType,
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// RunAsync TLS server in separate thread and returns control immediately
|
||||
|
|
|
@ -6,6 +6,7 @@ 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"
|
||||
)
|
||||
|
||||
|
@ -26,6 +27,43 @@ func isAdmSuccesful(policyInfos []info.PolicyInfo) (bool, string) {
|
|||
return admSuccess, strings.Join(errMsgs, ";")
|
||||
}
|
||||
|
||||
func isResponseSuccesful(engineReponses []engine.EngineResponseNew) bool {
|
||||
for _, er := range engineReponses {
|
||||
if !er.IsSuccesful() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 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 toBlockResource(engineReponses []engine.EngineResponseNew) bool {
|
||||
for _, er := range engineReponses {
|
||||
if er.PolicyResponse.ValidationFailureAction != ReportViolation {
|
||||
glog.V(4).Infof("ValidationFailureAction set to enforce for policy %s , blocking resource ceation", er.PolicyResponse.Policy)
|
||||
return true
|
||||
}
|
||||
}
|
||||
glog.V(4).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation")
|
||||
return false
|
||||
}
|
||||
|
||||
func getErrorMsg(engineReponses []engine.EngineResponseNew) string {
|
||||
var str []string
|
||||
for _, er := range engineReponses {
|
||||
if !er.IsSuccesful() {
|
||||
str = append(str, fmt.Sprintf("failed policy %s"), er.PolicyResponse.Policy)
|
||||
for _, rule := range er.PolicyResponse.Rules {
|
||||
if !rule.Success {
|
||||
str = append(str, rule.ToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(str, "\n")
|
||||
}
|
||||
|
||||
//ArrayFlags to store filterkinds
|
||||
type ArrayFlags []string
|
||||
|
||||
|
@ -87,3 +125,15 @@ func toBlock(pis []info.PolicyInfo) bool {
|
|||
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 {
|
||||
return nil
|
||||
}
|
||||
resource, err := engine.ApplyPatchNew(resource, patch)
|
||||
if err != nil {
|
||||
glog.Error("failed to patch resource: %v", err)
|
||||
return nil
|
||||
}
|
||||
return resource
|
||||
}
|
||||
|
|
|
@ -8,23 +8,26 @@ import (
|
|||
"github.com/nirmata/kyverno/pkg/policyviolation"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// HandleValidation handles validating webhook admission request
|
||||
// If there are no errors in validating rule we apply generation rules
|
||||
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, resource unstructured.Unstructured) *v1beta1.AdmissionResponse {
|
||||
// patchedResource is the (resource + patches) after applying mutation rules
|
||||
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, patchedResource []byte) (bool, string) {
|
||||
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
|
||||
gatherStat := func(policyName string, er engine.EngineResponse) {
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.ValidationExecutionTime = er.ExecutionTime
|
||||
ps.Stats.RulesAppliedCount = er.RulesAppliedCount
|
||||
ps.Stats.ValidationExecutionTime = policyResponse.ProcessingTime
|
||||
ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
|
||||
policyStats = append(policyStats, ps)
|
||||
}
|
||||
// send stats for aggregation
|
||||
|
@ -36,8 +39,23 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, res
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
resourceRaw := request.Object.Raw
|
||||
if patchedResource != nil {
|
||||
glog.V(4).Info("using patched resource from mutation to process validation rules")
|
||||
resourceRaw = patchedResource
|
||||
}
|
||||
// convert RAW to unstructured
|
||||
resource, err := engine.ConvertToUnstructured(resourceRaw)
|
||||
if err != nil {
|
||||
//TODO: skip applying the amiddions control ?
|
||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||
return true, ""
|
||||
}
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
// all the patches to be applied on the resource
|
||||
|
||||
policies, err := ws.pLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
|
@ -45,81 +63,51 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, res
|
|||
// Unable to connect to policy Lister to access policies
|
||||
glog.Error("Unable to connect to policy controller to access policies. Validation Rules are NOT being applied")
|
||||
glog.Warning(err)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
// all the patches to be applied on the resource
|
||||
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
for _, policy := range policies {
|
||||
|
||||
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 validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
|
||||
|
||||
glog.V(4).Infof("Validating resource %s/%s/%s with policy %s with %d rules\n", resource.GetKind(), resource.GetNamespace(), resource.GetName(), policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
// glog.V(4).Infof("Validating resource %s/%s/%s with policy %s with %d rules\n", resource.GetKind(), resource.GetNamespace(), resource.GetName(), policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
|
||||
engineResponse := engine.Validate(*policy, resource)
|
||||
if len(engineResponse.RuleInfos) == 0 {
|
||||
engineResponse := engine.ValidateNew(*policy, *resource)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
// Gather policy application statistics
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
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())
|
||||
}
|
||||
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.Warningf("%s: %s\n", r.Name, r.Msgs)
|
||||
}
|
||||
}
|
||||
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
||||
// 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...)
|
||||
// }
|
||||
|
||||
// If Validation fails then reject the request
|
||||
// violations are created if "audit" flag is set
|
||||
// and if there are any then we dont block the resource creation
|
||||
// Even if one the policy being applied
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
if !ok && toBlock(policyInfos) {
|
||||
if !isResponseSuccesful(engineResponses) && toBlockResource(engineResponses) {
|
||||
sendStat(true)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
return false, getErrorMsg(engineResponses)
|
||||
}
|
||||
|
||||
// ADD POLICY VIOLATIONS
|
||||
policyviolation.GeneratePolicyViolations(ws.pvListerSynced, ws.pvLister, ws.kyvernoClient, policyInfos)
|
||||
|
||||
sendStat(false)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue