1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/policy/apply.go

130 lines
4.9 KiB
Go
Raw Normal View History

package policy
import (
2019-08-26 13:34:42 -07:00
"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/utils"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// applyPolicy applies policy on a resource
//TODO: generation rules
func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (responses []engine.EngineResponseNew) {
startTime := time.Now()
2019-08-26 13:34:42 -07:00
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))
}()
2019-08-26 13:34:42 -07:00
// 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
2019-09-03 17:43:36 -07:00
// capture rule level stats
for _, rule := range policyResponse.Rules {
rs := RuleStatinfo{}
rs.RuleName = rule.Name
rs.ExecutionTime = rule.RuleStats.ProcessingTime
if rule.Success {
rs.RuleAppliedCount++
} else {
rs.RulesFailedCount++
}
ps.Stats.Rules = append(ps.Stats.Rules, rs)
}
2019-08-26 13:34:42 -07:00
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
2019-08-13 17:24:05 -07:00
//MUTATION
2019-08-26 13:34:42 -07:00
engineResponse, err = mutation(policy, resource, policyStatus)
engineResponses = append(engineResponses, engineResponse)
if err != nil {
2019-08-30 01:08:54 -07:00
glog.Errorf("unable to process mutation rules: %v", err)
}
2019-08-26 13:34:42 -07:00
gatherStat(policy.Name, engineResponse.PolicyResponse)
//send stats
sendStat(false)
//VALIDATION
2019-09-03 15:48:13 -07:00
engineResponse = engine.Validate(policy, resource)
2019-08-26 13:34:42 -07:00
engineResponses = append(engineResponses, engineResponse)
2019-08-20 16:57:19 -07:00
// gather stats
2019-08-26 13:34:42 -07:00
gatherStat(policy.Name, engineResponse.PolicyResponse)
2019-08-20 16:57:19 -07:00
//send stats
sendStat(false)
//TODO: GENERATION
2019-08-26 13:34:42 -07:00
return engineResponses
}
func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (engine.EngineResponseNew, error) {
2019-09-03 15:48:13 -07:00
engineResponse := engine.Mutate(policy, resource)
2019-08-26 13:34:42 -07:00
if !engineResponse.IsSuccesful() {
glog.V(4).Infof("mutation had errors reporting them")
return engineResponse, nil
2019-08-20 16:57:19 -07:00
}
2019-08-26 13:34:42 -07:00
// 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
2019-08-20 16:57:19 -07:00
}
2019-08-26 13:34:42 -07:00
return getFailedOverallRuleInfo(resource, engineResponse)
}
2019-08-20 16:57:19 -07:00
2019-08-26 13:34:42 -07:00
// 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\n", err)
return engine.EngineResponseNew{}, err
}
2019-08-26 13:34:42 -07:00
// resource does not match so there was a mutation rule violated
for index, rule := range engineResponse.PolicyResponse.Rules {
2019-08-26 16:10:19 -07:00
glog.V(4).Infof("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)
2019-08-26 13:34:42 -07:00
if len(rule.Patches) == 0 {
continue
}
2019-08-26 13:34:42 -07:00
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
}
2019-08-26 13:34:42 -07:00
// 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
}
2019-08-26 13:34:42 -07:00
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
2019-08-26 16:10:19 -07:00
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("rule not satisfied by existing resource.")
2019-08-26 13:34:42 -07:00
}
}
2019-08-26 13:34:42 -07:00
return engineResponse, nil
}