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

135 lines
5 KiB
Go
Raw Normal View History

package policy
import (
"encoding/json"
2019-08-26 13:34:42 -07:00
"fmt"
"reflect"
"strings"
"time"
jsonpatch "github.com/evanphx/json-patch"
2020-03-17 11:05:20 -07:00
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/resourcecache"
"github.com/kyverno/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, logger logr.Logger, excludeGroupRole []string, resCache resourcecache.ResourceCacheIface) (responses []response.EngineResponse) {
startTime := time.Now()
defer func() {
2020-05-17 09:54:32 -07:00
name := resource.GetKind() + "/" + resource.GetName()
ns := resource.GetNamespace()
if ns != "" {
name = ns + "/" + name
}
logger.V(3).Info("applyPolicy", "resource", name, "processingTime", time.Since(startTime).String())
}()
2019-08-26 13:34:42 -07:00
var engineResponses []response.EngineResponse
var engineResponseMutation, engineResponseValidation response.EngineResponse
2019-08-26 13:34:42 -07:00
var err error
// build context
ctx := context.NewContext()
err = ctx.AddResource(transformResource(resource))
if err != nil {
logger.Error(err, "enable to add transform resource to ctx")
}
//MUTATION
engineResponseMutation, err = mutation(policy, resource, ctx, logger, resCache, ctx)
if err != nil {
2020-03-17 11:05:20 -07:00
logger.Error(err, "failed to process mutation rule")
}
//VALIDATION
engineResponseValidation = engine.Validate(engine.PolicyContext{Policy: policy, Context: ctx, NewResource: resource, ExcludeGroupRole: excludeGroupRole, ResourceCache: resCache, JSONContext: ctx})
engineResponses = append(engineResponses, mergeRuleRespose(engineResponseMutation, engineResponseValidation))
//TODO: GENERATION
2019-08-26 13:34:42 -07:00
return engineResponses
}
func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, ctx context.EvalInterface, log logr.Logger, resCache resourcecache.ResourceCacheIface, jsonContext *context.Context) (response.EngineResponse, error) {
engineResponse := engine.Mutate(engine.PolicyContext{Policy: policy, NewResource: resource, Context: ctx, ResourceCache: resCache, JSONContext: jsonContext})
if !engineResponse.IsSuccessful() {
2020-03-17 11:05:20 -07:00
log.V(4).Info("failed to apply mutation rules; reporting them")
2019-08-26 13:34:42 -07:00
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
2020-03-17 11:05:20 -07:00
log.V(4).Info("resource already satisfys the policy")
2019-08-26 13:34:42 -07:00
return engineResponse, nil
2019-08-20 16:57:19 -07:00
}
2020-03-17 11:05:20 -07:00
return getFailedOverallRuleInfo(resource, engineResponse, log)
2019-08-26 13:34:42 -07:00
}
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
2020-03-17 11:05:20 -07:00
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse response.EngineResponse, log logr.Logger) (response.EngineResponse, error) {
2019-08-26 13:34:42 -07:00
rawResource, err := resource.MarshalJSON()
if err != nil {
2020-10-27 00:41:16 +05:30
log.Error(err, "failed to marshall resource")
return response.EngineResponse{}, 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 {
2020-05-26 23:07:48 -07:00
log.V(4).Info("verifying if policy rule was applied before", "rule", rule.Name)
patches := rule.Patches
patch, err := jsonpatch.DecodePatch(utils.JoinPatches(patches))
2019-08-26 13:34:42 -07:00
if err != nil {
log.Error(err, "failed to decode JSON patch", "patches", patches)
return response.EngineResponse{}, err
2019-08-26 13:34:42 -07:00
}
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 {
log.Error(err, "failed to apply JSON patch", "patches", patches)
return response.EngineResponse{}, err
2019-08-26 13:34:42 -07:00
}
2019-08-26 13:34:42 -07:00
if !jsonpatch.Equal(patchedResource, rawResource) {
2020-03-17 11:05:20 -07:00
log.V(4).Info("policy rule conditions not satisfied by resource", "rule", rule.Name)
2019-08-26 13:34:42 -07:00
engineResponse.PolicyResponse.Rules[index].Success = false
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(patches, log))
2019-08-26 13:34:42 -07:00
}
}
2019-08-26 13:34:42 -07:00
return engineResponse, nil
}
type jsonPatch struct {
Op string `json:"op"`
Path string `json:"path"`
Value interface{} `json:"value"`
}
2020-03-17 11:05:20 -07:00
func extractPatchPath(patches [][]byte, log logr.Logger) string {
var resultPath []string
// extract the patch path and value
for _, patch := range patches {
2020-03-17 11:05:20 -07:00
log.V(4).Info("expected json patch not found in resource", "patch", string(patch))
var data jsonPatch
if err := json.Unmarshal(patch, &data); err != nil {
2020-03-17 11:05:20 -07:00
log.Error(err, "failed to decode the generate patch", "patch", string(patch))
continue
}
resultPath = append(resultPath, data.Path)
}
return strings.Join(resultPath, ";")
}
func mergeRuleRespose(mutation, validation response.EngineResponse) response.EngineResponse {
mutation.PolicyResponse.Rules = append(mutation.PolicyResponse.Rules, validation.PolicyResponse.Rules...)
return mutation
}