diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go index 46725b8cc2..be48530ae7 100644 --- a/pkg/annotations/annotations.go +++ b/pkg/annotations/annotations.go @@ -48,7 +48,7 @@ func (p *Policy) getOverAllStatus() string { return "Success" } -func getRules(rules []*pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule { +func getRules(rules []pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule { if len(rules) == 0 { return nil } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 3befc6f3bf..93ca34e1b5 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -1,107 +1,105 @@ package engine import ( - jsonpatch "github.com/evanphx/json-patch" - "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // ProcessExisting checks for mutation and validation violations of existing resources func ProcessExisting(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) []info.PolicyInfo { - glog.Infof("Applying policy %s on existing resources", policy.Name) - // key uid - resourceMap := ListResourcesThatApplyToPolicy(client, policy, filterK8Resources) - policyInfos := []info.PolicyInfo{} - // for the filtered resource apply policy - for _, v := range resourceMap { + return nil + // glog.Infof("Applying policy %s on existing resources", policy.Name) + // // key uid + // resourceMap := ListResourcesThatApplyToPolicy(client, policy, filterK8Resources) + // policyInfos := []info.PolicyInfo{} + // // for the filtered resource apply policy + // for _, v := range resourceMap { - policyInfo, err := applyPolicy(client, policy, v) - if err != nil { - glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.Resource.GetName(), v.Resource.GetNamespace()) - glog.Error(err) - continue - } - policyInfos = append(policyInfos, policyInfo) - } + // policyInfo, err := applyPolicy(client, policy, v) + // if err != nil { + // glog.Errorf("unable to apply policy %s on resource %s/%s", policy.Name, v.Resource.GetName(), v.Resource.GetNamespace()) + // glog.Error(err) + // continue + // } + // policyInfos = append(policyInfos, policyInfo) + // } - return policyInfos + // return policyInfos } -func applyPolicy(client *client.Client, policy *kyverno.Policy, res resourceInfo) (info.PolicyInfo, error) { - var policyInfo info.PolicyInfo - glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - rawResource, err := res.Resource.MarshalJSON() - if err != nil { - return policyInfo, err - } - // Mutate - mruleInfos, err := mutation(policy, rawResource, res.Gvk) - policyInfo.AddRuleInfos(mruleInfos) - if err != nil { - return policyInfo, err - } - // Validation - //TODO check by value or pointer - vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) - if err != nil { - return policyInfo, err - } - policyInfo.AddRuleInfos(vruleInfos) - policyInfo = info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction) +// func applyPolicy(client *client.Client, policy *kyverno.Policy, res resourceInfo) (info.PolicyInfo, error) { +// var policyInfo info.PolicyInfo +// glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) +// rawResource, err := res.Resource.MarshalJSON() +// if err != nil { +// return policyInfo, err +// } +// // Mutate +// mruleInfos, err := mutation(policy, rawResource, res.Gvk) +// policyInfo.AddRuleInfos(mruleInfos) +// if err != nil { +// return policyInfo, err +// } +// // Validation +// //TODO check by value or pointer +// vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) +// if err != nil { +// return policyInfo, err +// } +// policyInfo.AddRuleInfos(vruleInfos) +// policyInfo = info.NewPolicyInfo(policy.Name, res.Gvk.Kind, res.Resource.GetName(), res.Resource.GetNamespace(), policy.Spec.ValidationFailureAction) - if res.Gvk.Kind == "Namespace" { +// if res.Gvk.Kind == "Namespace" { - // Generation - gruleInfos := Generate(client, policy, res.Resource) - policyInfo.AddRuleInfos(gruleInfos) - } +// // Generation +// gruleInfos := Generate(client, policy, res.Resource) +// policyInfo.AddRuleInfos(gruleInfos) +// } - return policyInfo, nil -} +// return policyInfo, nil +// } -func mutation(p *kyverno.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { - patches, ruleInfos := Mutate(*p, rawResource, *gvk) - if len(ruleInfos) == 0 { - // no rules were processed - return nil, nil - } - // if there are any errors return - for _, r := range ruleInfos { - if !r.IsSuccessful() { - return ruleInfos, nil - } - } - // if there are no patches // for overlay - if len(patches) == 0 { - return ruleInfos, nil - } - // option 2: (original Resource + patch) compare with (original resource) - mergePatches := JoinPatches(patches) - // merge the patches - patch, err := jsonpatch.DecodePatch(mergePatches) - if err != nil { - return nil, err - } - // apply the patches returned by mutate to the original resource - patchedResource, err := patch.Apply(rawResource) - if err != nil { - return nil, err - } - // compare (original Resource + patch) vs (original resource) - // to verify if they are equal - ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation) - if !jsonpatch.Equal(patchedResource, rawResource) { - //resource does not match so there was a mutation rule violated - // TODO : check the rule name "mutation rules" - ruleInfo.Fail() - ruleInfo.Add("resource does not satisfy mutation rules") - } else { - ruleInfo.Add("resource satisfys the mutation rule") - } - ruleInfos = append(ruleInfos, ruleInfo) - return ruleInfos, nil -} +// func mutation(p *kyverno.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +// patches, ruleInfos := Mutate(*p, rawResource, *gvk) +// if len(ruleInfos) == 0 { +// // no rules were processed +// return nil, nil +// } +// // if there are any errors return +// for _, r := range ruleInfos { +// if !r.IsSuccessful() { +// return ruleInfos, nil +// } +// } +// // if there are no patches // for overlay +// if len(patches) == 0 { +// return ruleInfos, nil +// } +// // option 2: (original Resource + patch) compare with (original resource) +// mergePatches := JoinPatches(patches) +// // merge the patches +// patch, err := jsonpatch.DecodePatch(mergePatches) +// if err != nil { +// return nil, err +// } +// // apply the patches returned by mutate to the original resource +// patchedResource, err := patch.Apply(rawResource) +// if err != nil { +// return nil, err +// } +// // compare (original Resource + patch) vs (original resource) +// // to verify if they are equal +// ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation) +// if !jsonpatch.Equal(patchedResource, rawResource) { +// //resource does not match so there was a mutation rule violated +// // TODO : check the rule name "mutation rules" +// ruleInfo.Fail() +// ruleInfo.Add("resource does not satisfy mutation rules") +// } else { +// ruleInfo.Add("resource satisfys the mutation rule") +// } +// ruleInfos = append(ruleInfos, ruleInfo) +// return ruleInfos, nil +// } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 891682be3e..52076832b7 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -14,8 +14,8 @@ import ( ) //Generate apply generation rules on a resource -func Generate(client *client.Client, policy *kyverno.Policy, ns unstructured.Unstructured) []*info.RuleInfo { - ris := []*info.RuleInfo{} +func Generate(client *client.Client, policy *kyverno.Policy, ns unstructured.Unstructured) []info.RuleInfo { + ris := []info.RuleInfo{} for _, rule := range policy.Spec.Rules { if rule.Generation == (kyverno.Generation{}) { continue diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 8445bd8b97..392913ee61 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -6,20 +6,15 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Mutate performs mutation. Overlay first and then mutation patches //TODO: check if gvk needs to be passed or can be set in resource -func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []*info.RuleInfo) { +func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) ([][]byte, []info.RuleInfo) { //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information - //TODO: pass unstructured instead of rawResource ? - resource, err := convertToUnstructured(rawResource) - if err != nil { - glog.Errorf("unable to convert raw resource to unstructured: %v", err) - } var patches [][]byte - var ruleInfos []*info.RuleInfo + var ruleInfos []info.RuleInfo for _, rule := range policy.Spec.Rules { if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) { @@ -29,7 +24,7 @@ func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKi // check if the resource satisfies the filter conditions defined in the rule //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that // dont statisfy a policy rule resource description - ok := MatchesResourceDescription(resource, rule, gvk) + ok := MatchesResourceDescription(resource, rule) if !ok { glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName()) continue @@ -39,7 +34,7 @@ func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKi // Process Overlay if rule.Mutation.Overlay != nil { - oPatches, err := processOverlay(resource, rule, rawResource) + oPatches, err := processOverlay(resource, rule) if err == nil { if len(oPatches) == 0 { // if array elements dont match then we skip(nil patch, no error) @@ -66,7 +61,7 @@ func Mutate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKi // Process Patches if len(rule.Mutation.Patches) != 0 { - jsonPatches, errs := processPatches(rule, rawResource) + jsonPatches, errs := processPatches(resource, rule) if len(errs) > 0 { ruleInfo.Fail() for _, err := range errs { diff --git a/pkg/engine/overlay.go b/pkg/engine/overlay.go index 27118f5530..1ae274bb8c 100644 --- a/pkg/engine/overlay.go +++ b/pkg/engine/overlay.go @@ -17,7 +17,7 @@ import ( // ProcessOverlay handles validating admission request // Checks the target resources for rules defined in the policy -func processOverlay(resourceUnstr *unstructured.Unstructured, rule kyverno.Rule, rawResource []byte) ([][]byte, error) { +func processOverlay(resourceUnstr unstructured.Unstructured, rule kyverno.Rule) ([][]byte, error) { //TODO check if there is better solution resourceRaw, err := resourceUnstr.MarshalJSON() diff --git a/pkg/engine/patches.go b/pkg/engine/patches.go index b13c0971a3..0debccdfe1 100644 --- a/pkg/engine/patches.go +++ b/pkg/engine/patches.go @@ -3,9 +3,11 @@ package engine import ( "encoding/json" "errors" + "fmt" "reflect" "github.com/golang/glog" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" jsonpatch "github.com/evanphx/json-patch" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" @@ -13,7 +15,15 @@ import ( // ProcessPatches Returns array from separate patches that can be applied to the document // Returns error ONLY in case when creation of resource should be denied. -func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, errs []error) { +func processPatches(resourceUnstr unstructured.Unstructured, rule kyverno.Rule) (allPatches [][]byte, errs []error) { + //TODO check if there is better solution + resource, err := resourceUnstr.MarshalJSON() + if err != nil { + glog.V(4).Infof("unable to marshal resource : %v", err) + errs = append(errs, fmt.Errorf("unable to marshal resource : %v", err)) + return nil, errs + } + if len(resource) == 0 { errs = append(errs, errors.New("Source document for patching is empty")) return nil, errs diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index cf80c5b40c..6e18124d8f 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -150,11 +150,11 @@ func excludeNamespaces(namespaces []string, excludeNs string) []string { } //MatchesResourceDescription checks if the resource matches resource desription of the rule or not -func MatchesResourceDescription(resource *unstructured.Unstructured, rule kyverno.Rule, gvk metav1.GroupVersionKind) bool { +func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno.Rule) bool { matches := rule.MatchResources.ResourceDescription exclude := rule.ExcludeResources.ResourceDescription - if !findKind(matches.Kinds, gvk.Kind) { + if !findKind(matches.Kinds, resource.GetKind()) { return false } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index b3edc32712..d850820e3c 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -12,30 +12,30 @@ import ( "github.com/golang/glog" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" "github.com/nirmata/kyverno/pkg/info" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Validate handles validating admission request // Checks the target resources for rules defined in the policy -func Validate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +func Validate(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) { //TODO: convert rawResource to unstructured to avoid unmarhalling all the time for get some resource information //TODO: pass unstructured instead of rawResource ? - resourceUnstr, err := convertToUnstructured(rawResource) - if err != nil { - glog.Errorf("unable to convert raw resource to unstructured: %v", err) - } - resourceRaw, err := resourceUnstr.MarshalJSON() + // resourceUnstr, err := convertToUnstructured(rawResource) + // if err != nil { + // glog.Errorf("unable to convert raw resource to unstructured: %v", err) + // } + resourceRaw, err := resource.MarshalJSON() if err != nil { glog.V(4).Infof("unable to marshal resource : %v", err) return nil, err } - var resource interface{} - if err := json.Unmarshal(resourceRaw, &resource); err != nil { + var resourceInt interface{} + if err := json.Unmarshal(resourceRaw, &resourceInt); err != nil { glog.V(4).Infof("unable to unmarshal resource : %v", err) return nil, err } - var ruleInfos []*info.RuleInfo + var ruleInfos []info.RuleInfo for _, rule := range policy.Spec.Rules { if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) { @@ -45,21 +45,21 @@ func Validate(policy kyverno.Policy, rawResource []byte, gvk metav1.GroupVersion // check if the resource satisfies the filter conditions defined in the rule //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that // dont statisfy a policy rule resource description - ok := MatchesResourceDescription(resourceUnstr, rule, gvk) + ok := MatchesResourceDescription(resource, rule) if !ok { - glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resourceUnstr.GetNamespace(), resourceUnstr.GetName()) + glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName()) continue } ruleInfo := info.NewRuleInfo(rule.Name, info.Validation) - err := validateResourceWithPattern(resource, rule.Validation.Pattern) + err := validateResourceWithPattern(resourceInt, rule.Validation.Pattern) if err != nil { ruleInfo.Fail() ruleInfo.Addf("Failed to apply pattern: %v.", err) } else { ruleInfo.Add("Pattern succesfully validated") - glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resourceUnstr.GetNamespace(), resourceUnstr.GetName()) + glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName()) } ruleInfos = append(ruleInfos, ruleInfo) } diff --git a/pkg/info/info.go b/pkg/info/info.go index 65f43167ac..1118aa4c4e 100644 --- a/pkg/info/info.go +++ b/pkg/info/info.go @@ -20,7 +20,7 @@ type PolicyInfo struct { RNamespace string //TODO: add check/enum for types ValidationFailureAction string // BlockChanges, ReportViolation - Rules []*RuleInfo + Rules []RuleInfo success bool } @@ -135,8 +135,8 @@ func (ri *RuleInfo) GetErrorString() string { } //NewRuleInfo creates a new RuleInfo -func NewRuleInfo(ruleName string, ruleType RuleType) *RuleInfo { - return &RuleInfo{ +func NewRuleInfo(ruleName string, ruleType RuleType) RuleInfo { + return RuleInfo{ Name: ruleName, Msgs: []string{}, RuleType: ruleType, @@ -165,7 +165,7 @@ func (ri *RuleInfo) Addf(msg string, args ...interface{}) { } //RulesSuccesfuly check if the any rule has failed or not -func RulesSuccesfuly(rules []*RuleInfo) bool { +func RulesSuccesfuly(rules []RuleInfo) bool { for _, r := range rules { if !r.success { return false @@ -175,7 +175,7 @@ func RulesSuccesfuly(rules []*RuleInfo) bool { } //AddRuleInfos sets the rule information -func (pi *PolicyInfo) AddRuleInfos(rules []*RuleInfo) { +func (pi *PolicyInfo) AddRuleInfos(rules []RuleInfo) { if rules == nil { return } diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go new file mode 100644 index 0000000000..c07358d46c --- /dev/null +++ b/pkg/policy/apply.go @@ -0,0 +1,93 @@ +package policy + +import ( + "time" + + jsonpatch "github.com/evanphx/json-patch" + "github.com/golang/glog" + kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" + "github.com/nirmata/kyverno/pkg/engine" + "github.com/nirmata/kyverno/pkg/info" + "github.com/nirmata/kyverno/pkg/utils" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// applyPolicy applies policy on a resource +//TODO: generation rules +func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured) (info.PolicyInfo, error) { + startTime := time.Now() + glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime) + defer func() { + glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime)) + }() + // glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + var policyInfo info.PolicyInfo + //MUTATION + mruleInfos, err := mutation(policy, resource) + policyInfo.AddRuleInfos(mruleInfos) + if err != nil { + return policyInfo, err + } + + //VALIDATION + vruleInfos, err := engine.Validate(policy, resource) + policyInfo.AddRuleInfos(vruleInfos) + if err != nil { + return policyInfo, err + } + + //TODO: GENERATION + return policyInfo, nil +} + +func mutation(policy kyverno.Policy, resource unstructured.Unstructured) ([]info.RuleInfo, error) { + patches, ruleInfos := engine.Mutate(policy, resource) + if len(ruleInfos) == 0 { + //no rules processed + return nil, nil + } + + for _, r := range ruleInfos { + if !r.IsSuccessful() { + // no failures while processing rule + return ruleInfos, nil + } + } + if len(patches) == 0 { + // no patches for the resources + // either there were failures or the overlay already was satisfied + return ruleInfos, nil + } + + // (original resource + patch) == (original resource) + mergePatches := utils.JoinPatches(patches) + patch, err := jsonpatch.DecodePatch(mergePatches) + if err != nil { + return nil, err + } + rawResource, err := resource.MarshalJSON() + if err != nil { + glog.V(4).Infof("unable to marshal resource : %v", err) + return nil, err + } + + // apply the patches returned by mutate to the original resource + patchedResource, err := patch.Apply(rawResource) + if err != nil { + return nil, err + } + //TODO: this will be removed after the support for patching for each rule + ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation) + + if !jsonpatch.Equal(patchedResource, rawResource) { + //resource does not match so there was a mutation rule violated + // TODO : check the rule name "mutation rules" + ruleInfo.Fail() + ruleInfo.Add("resource does not satisfy mutation rules") + } else { + ruleInfo.Add("resource satisfys the mutation rule") + } + + ruleInfos = append(ruleInfos, ruleInfo) + return ruleInfos, nil +} diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 5f5df4f107..8cee3e168d 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -397,7 +397,7 @@ func (pc *PolicyController) syncPolicy(key string) error { return err } // process policies on existing resources - pc.processExistingResources(p) + pc.processExistingResources(*p) return pc.syncStatusOnly(p, pvList) } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 59dfc68e9a..0879a306da 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -8,12 +8,13 @@ import ( "github.com/minio/minio/pkg/wildcard" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func (pc *PolicyController) processExistingResources(policy *kyverno.Policy) { +func (pc *PolicyController) processExistingResources(policy kyverno.Policy) { // Parse through all the resources // drops the cache after configured rebuild time pc.rm.Drop() @@ -23,17 +24,27 @@ func (pc *PolicyController) processExistingResources(policy *kyverno.Policy) { for _, resource := range resourceMap { // pre-processing, check if the policy and resource version has been processed before if !pc.rm.ProcessResource(policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) { - glog.V(4).Infof("policy %s with resource versio %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + glog.V(4).Infof("policy %s with resource version %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) continue } // apply the policy on each glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) + applyPolicyOnResource(policy, resource) // post-processing, register the resource as processed pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion()) } } -func listResources(client *client.Client, policy *kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]unstructured.Unstructured { +func applyPolicyOnResource(policy kyverno.Policy, resource unstructured.Unstructured) *info.PolicyInfo { + policyInfo, err := applyPolicy(policy, resource) + if err != nil { + glog.V(4).Infof("failed to process policy %s on resource %s/%s/%s: %v", policy.GetName(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) + return nil + } + return &policyInfo +} + +func listResources(client *client.Client, policy kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]unstructured.Unstructured { // key uid resourceMap := map[string]unstructured.Unstructured{} diff --git a/pkg/utils/json.go b/pkg/utils/json.go index c098307737..5542d2275c 100644 --- a/pkg/utils/json.go +++ b/pkg/utils/json.go @@ -187,3 +187,21 @@ func subsetSlice(a, b []interface{}) bool { } return true } + +// JoinPatches joins array of serialized JSON patches to the single JSONPatch array +func JoinPatches(patches [][]byte) []byte { + var result []byte + if len(patches) == 0 { + return result + } + + result = append(result, []byte("[\n")...) + for index, patch := range patches { + result = append(result, patch...) + if index != len(patches)-1 { + result = append(result, []byte(",\n")...) + } + } + result = append(result, []byte("\n]")...) + return result +} diff --git a/pkg/utils/util.go b/pkg/utils/util.go index fe6c95e538..19b728babd 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -4,8 +4,11 @@ import ( "regexp" "strings" + "github.com/golang/glog" + "github.com/minio/minio/pkg/wildcard" "k8s.io/api/admission/v1beta1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func Contains(list []string, element string) bool { @@ -74,3 +77,14 @@ func ParseKinds(list string) []K8Resource { } return resources } + +//ConvertToUnstructured coverts a raw resource into unstructured struct +func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) { + resource := &unstructured.Unstructured{} + err := resource.UnmarshalJSON(data) + if err != nil { + glog.V(4).Infof("failed to unmarshall resource: %v", err) + return nil, err + } + return resource, nil +} diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 99447f85e6..9fc6f83540 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -51,8 +51,17 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation) glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) + // resource, err := utils.ConvertToUnstructured(request.Object.Raw) + // if err != nil { + // glog.Errorf("unable to process policy %s resource %v: %v", policy.GetName(), request.Resource, err) + // continue + // } + //TODO: check if the GVK information is present in the request of we set it explicity here ? + glog.V(4).Infof("GVK is %v", resource.GroupVersionKind()) + // resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind}) //TODO: passing policy value as we dont wont to modify the policy - policyPatches, ruleInfos := engine.Mutate(*policy, request.Object.Raw, request.Kind) + + policyPatches, ruleInfos := engine.Mutate(*policy, *resource) policyInfo.AddRuleInfos(ruleInfos) policyInfos = append(policyInfos, policyInfo) if !policyInfo.IsSuccessful() { diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index 34d628e82d..51514c8740 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -52,7 +52,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1 glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - ruleInfos, err := engine.Validate(*policy, request.Object.Raw, request.Kind) + ruleInfos, err := engine.Validate(*policy, *resource) if err != nil { // This is not policy error // but if unable to parse request raw resource