1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00
kyverno/pkg/policy/apply.go
2020-02-22 23:35:02 +05:30

121 lines
4.8 KiB
Go

package policy
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
"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) (responses []response.EngineResponse) {
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))
}()
var engineResponses []response.EngineResponse
var engineResponse response.EngineResponse
var err error
// build context
ctx := context.NewContext()
ctx.AddResource(transformResource(resource))
//MUTATION
engineResponse, err = mutation(policy, resource, ctx)
engineResponses = append(engineResponses, engineResponse)
if err != nil {
glog.Errorf("unable to process mutation rules: %v", err)
}
//VALIDATION
engineResponse = engine.Validate(engine.PolicyContext{Policy: policy, Context: ctx, NewResource: resource})
engineResponses = append(engineResponses, engineResponse)
//TODO: GENERATION
return engineResponses
}
func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, ctx context.EvalInterface) (response.EngineResponse, error) {
engineResponse := engine.Mutate(engine.PolicyContext{Policy: policy, NewResource: resource, Context: ctx})
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)
}
// getFailedOverallRuleInfo gets detailed info for over-all mutation failure
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse response.EngineResponse) (response.EngineResponse, error) {
rawResource, err := resource.MarshalJSON()
if err != nil {
glog.V(4).Infof("unable to marshal resource: %v\n", err)
return response.EngineResponse{}, err
}
// resource does not match so there was a mutation rule violated
for index, rule := range engineResponse.PolicyResponse.Rules {
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)
if len(rule.Patches) == 0 {
continue
}
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 response.EngineResponse{}, err
}
// 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 response.EngineResponse{}, err
}
if !jsonpatch.Equal(patchedResource, rawResource) {
glog.V(4).Infof("policy %s rule %s condition not satisfied by existing resource", engineResponse.PolicyResponse.Policy, rule.Name)
engineResponse.PolicyResponse.Rules[index].Success = false
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(rule.Patches))
}
}
return engineResponse, nil
}
type jsonPatch struct {
Op string `json:"op"`
Path string `json:"path"`
Value interface{} `json:"value"`
}
func extractPatchPath(patches [][]byte) string {
var resultPath []string
// extract the patch path and value
for _, patch := range patches {
glog.V(4).Infof("expected json patch not found in resource: %s", string(patch))
var data jsonPatch
if err := json.Unmarshal(patch, &data); err != nil {
glog.V(4).Infof("Failed to decode the generated patch %v: Error %v", string(patch), err)
continue
}
resultPath = append(resultPath, data.Path)
}
return strings.Join(resultPath, ";")
}