1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

522 adding force mutate function

This commit is contained in:
shravan 2020-03-06 01:09:38 +05:30
parent 7aa1e1515b
commit 4db0cf7a87
4 changed files with 133 additions and 69 deletions

View file

@ -116,7 +116,7 @@ func processOverlayPatches(resource, overlay interface{}) ([][]byte, overlayErro
}
}
patchBytes, err := mutateResourceWithOverlay(resource, overlay)
patchBytes, err := MutateResourceWithOverlay(resource, overlay)
if err != nil {
return patchBytes, newOverlayError(overlayFailure, err.Error())
}
@ -124,8 +124,8 @@ func processOverlayPatches(resource, overlay interface{}) ([][]byte, overlayErro
return patchBytes, overlayError{}
}
// mutateResourceWithOverlay is a start of overlaying process
func mutateResourceWithOverlay(resource, pattern interface{}) ([][]byte, error) {
// MutateResourceWithOverlay is a start of overlaying process
func MutateResourceWithOverlay(resource, pattern interface{}) ([][]byte, error) {
// It assumes that mutation is started from root, so "/" is passed
return applyOverlay(resource, pattern, "/")
}

View file

@ -1,10 +1,15 @@
package engine
import (
"fmt"
"reflect"
"strings"
"time"
"github.com/nirmata/kyverno/pkg/engine/utils"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/mutate"
@ -122,6 +127,71 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
resp.PatchedResource = patchedResource
return resp
}
func mutateResourceWithOverlay(resource unstructured.Unstructured, overlay interface{}) (unstructured.Unstructured, error) {
patches, err := mutate.MutateResourceWithOverlay(resource.UnstructuredContent(), overlay)
if err != nil {
return unstructured.Unstructured{}, err
}
if len(patches) == 0 {
return resource, nil
}
// convert to RAW
resourceRaw, err := resource.MarshalJSON()
if err != nil {
return unstructured.Unstructured{}, err
}
var patchResource []byte
patchResource, err = utils.ApplyPatches(resourceRaw, patches)
if err != nil {
return unstructured.Unstructured{}, err
}
resource = unstructured.Unstructured{}
err = resource.UnmarshalJSON(patchResource)
if err != nil {
return unstructured.Unstructured{}, err
}
return resource, nil
}
// ForceMutate does not check any conditions, it simply mutates the given resource
func ForceMutate(ctx context.EvalInterface, policy kyverno.ClusterPolicy, resource unstructured.Unstructured) (unstructured.Unstructured, error) {
var err error
for _, rule := range policy.Spec.Rules {
if !rule.HasMutate() {
continue
}
mutation := rule.Mutation.DeepCopy()
if mutation.Overlay != nil {
overlay := mutation.Overlay
if overlay, err = variables.SubstituteVars(ctx, overlay); err != nil {
return unstructured.Unstructured{}, err
}
resource, err = mutateResourceWithOverlay(resource, overlay)
if err != nil {
return unstructured.Unstructured{}, fmt.Errorf("could not mutate resource with overlay on rule %v:%v", rule.Name, err)
}
}
if rule.Mutation.Patches != nil {
var resp response.RuleResponse
resp, resource = mutate.ProcessPatches(rule, resource)
if !resp.Success {
return unstructured.Unstructured{}, fmt.Errorf(resp.Message)
}
}
}
return resource, nil
}
func incrementAppliedRuleCount(resp *response.EngineResponse) {
resp.PolicyResponse.RulesAppliedCount++
}

View file

@ -4,6 +4,8 @@ import (
"encoding/json"
"time"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/golang/glog"
"gopkg.in/yaml.v2"
@ -66,28 +68,45 @@ func (c *crdSync) sync() {
return
}
deleteCRDFromPreviousSync()
for _, crd := range crds.Items {
var crdDefinition crdDefinition
crdRaw, _ := json.Marshal(crd.Object)
_ = json.Unmarshal(crdRaw, &crdDefinition)
crdName := crdDefinition.Spec.Names.Kind
if len(crdDefinition.Spec.Versions) < 1 {
glog.V(4).Infof("could not parse crd schema, no versions present")
continue
}
var schema yaml.MapSlice
schemaRaw, _ := json.Marshal(crdDefinition.Spec.Versions[0].Schema.OpenAPIV3Schema)
_ = yaml.Unmarshal(schemaRaw, &schema)
parsedSchema, err := openapi_v2.NewSchema(schema, compiler.NewContext("schema", nil))
if err != nil {
glog.V(4).Infof("could not parse crd schema:%v", err)
continue
}
openApiGlobalState.kindToDefinitionName[crdName] = crdName
openApiGlobalState.definitions[crdName] = parsedSchema
parseCRD(crd)
}
}
func deleteCRDFromPreviousSync() {
for _, crd := range openApiGlobalState.crdList {
delete(openApiGlobalState.kindToDefinitionName, crd)
delete(openApiGlobalState.definitions, crd)
}
openApiGlobalState.crdList = []string{}
}
func parseCRD(crd unstructured.Unstructured) {
var crdDefinition crdDefinition
crdRaw, _ := json.Marshal(crd.Object)
_ = json.Unmarshal(crdRaw, &crdDefinition)
crdName := crdDefinition.Spec.Names.Kind
if len(crdDefinition.Spec.Versions) < 1 {
glog.V(4).Infof("could not parse crd schema, no versions present")
return
}
var schema yaml.MapSlice
schemaRaw, _ := json.Marshal(crdDefinition.Spec.Versions[0].Schema.OpenAPIV3Schema)
_ = yaml.Unmarshal(schemaRaw, &schema)
parsedSchema, err := openapi_v2.NewSchema(schema, compiler.NewContext("schema", nil))
if err != nil {
glog.V(4).Infof("could not parse crd schema:%v", err)
return
}
openApiGlobalState.crdList = append(openApiGlobalState.crdList, crdName)
openApiGlobalState.kindToDefinitionName[crdName] = crdName
openApiGlobalState.definitions[crdName] = parsedSchema
}

View file

@ -29,21 +29,19 @@ var openApiGlobalState struct {
document *openapi_v2.Document
definitions map[string]*openapi_v2.Schema
kindToDefinitionName map[string]string
crdList []string
models proto.Models
isSet bool
}
func init() {
if !openApiGlobalState.isSet {
defaultDoc, err := getSchemaDocument()
if err != nil {
panic(err)
}
defaultDoc, err := getSchemaDocument()
if err != nil {
panic(err)
}
err = useOpenApiDocument(defaultDoc)
if err != nil {
panic(err)
}
err = useOpenApiDocument(defaultDoc)
if err != nil {
panic(err)
}
}
@ -51,11 +49,6 @@ func ValidatePolicyMutation(policy v1.ClusterPolicy) error {
openApiGlobalState.mutex.RLock()
defer openApiGlobalState.mutex.RUnlock()
if !openApiGlobalState.isSet {
glog.V(4).Info("Cannot Validate policy: Validation global state not set")
return nil
}
var kindToRules = make(map[string][]v1.Rule)
for _, rule := range policy.Spec.Rules {
if rule.HasMutate() {
@ -73,7 +66,7 @@ func ValidatePolicyMutation(policy v1.ClusterPolicy) error {
}
for kind, rules := range kindToRules {
newPolicy := policy
newPolicy := *policy.DeepCopy()
newPolicy.Spec.Rules = rules
resource, _ := generateEmptyResource(openApiGlobalState.definitions[openApiGlobalState.kindToDefinitionName[kind]]).(map[string]interface{})
if resource == nil {
@ -89,22 +82,11 @@ func ValidatePolicyMutation(policy v1.ClusterPolicy) error {
glog.V(4).Infof("Failed to load service account in context:%v", err)
}
policyContext := engine.PolicyContext{
Policy: newPolicy,
NewResource: newResource,
Context: ctx,
patchedResource, err := engine.ForceMutate(ctx, *newPolicy.DeepCopy(), newResource)
if err != nil {
return err
}
resp := engine.Mutate(policyContext)
if len(resp.GetSuccessRules()) != len(rules) {
var errMessages []string
for _, rule := range resp.PolicyResponse.Rules {
if !rule.Success {
errMessages = append(errMessages, fmt.Sprintf("Invalid rule : %v, %v", rule.Name, rule.Message))
}
}
return fmt.Errorf(strings.Join(errMessages, "\n"))
}
err = ValidateResource(*resp.PatchedResource.DeepCopy(), kind)
err = ValidateResource(*patchedResource.DeepCopy(), kind)
if err != nil {
return err
}
@ -113,22 +95,11 @@ func ValidatePolicyMutation(policy v1.ClusterPolicy) error {
return nil
}
// For crd, we do not store definition in document
func getSchemaFromDefinitions(kind string) (proto.Schema, error) {
path := proto.NewPath(kind)
return (&proto.Definitions{}).ParseSchema(openApiGlobalState.definitions[kind], &path)
}
func ValidateResource(patchedResource unstructured.Unstructured, kind string) error {
openApiGlobalState.mutex.RLock()
defer openApiGlobalState.mutex.RUnlock()
var err error
if !openApiGlobalState.isSet {
glog.V(4).Info("Cannot Validate resource: Validation global state not set")
return nil
}
kind = openApiGlobalState.kindToDefinitionName[kind]
schema := openApiGlobalState.models.LookupModel(kind)
if schema == nil {
@ -171,8 +142,6 @@ func useOpenApiDocument(customDoc *openapi_v2.Document) error {
return err
}
openApiGlobalState.isSet = true
return nil
}
@ -186,6 +155,12 @@ func getSchemaDocument() (*openapi_v2.Document, error) {
return openapi_v2.NewDocument(spec, compiler.NewContext("$root", nil))
}
// For crd, we do not store definition in document
func getSchemaFromDefinitions(kind string) (proto.Schema, error) {
path := proto.NewPath(kind)
return (&proto.Definitions{}).ParseSchema(openApiGlobalState.definitions[kind], &path)
}
func generateEmptyResource(kindSchema *openapi_v2.Schema) interface{} {
types := kindSchema.GetType().GetValue()