1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

support variable substitution in overlay mutation

This commit is contained in:
shivkumar dudhani 2019-12-12 18:25:54 -08:00
parent 80867b78ea
commit 8414681e60
7 changed files with 120 additions and 15 deletions

View file

@ -2,7 +2,7 @@ package context
import (
"github.com/golang/glog"
jmespath "github.com/jmespath/go-jmespath"
"github.com/jmespath/go-jmespath"
)
//Query searches for query in the context

View file

@ -12,6 +12,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
startTime := time.Now()
policy := policyContext.Policy
resource := policyContext.NewResource
ctx := policyContext.Context
// policy information
func() {
@ -61,7 +62,7 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
// Process Overlay
if rule.Mutation.Overlay != nil {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = processOverlay(rule, patchedResource)
ruleResponse, patchedResource = processOverlay(ctx, rule, patchedResource)
if ruleResponse.Success == true && ruleResponse.Patches == nil {
// overlay pattern does not match the resource conditions
glog.V(4).Infof(ruleResponse.Message)

View file

@ -0,0 +1,89 @@
package engine
import (
"encoding/json"
"reflect"
"testing"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/context"
"gotest.tools/assert"
)
func Test_VariableSubstitutionOverlay(t *testing.T) {
rawPolicy := []byte(`
{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "add-label"
},
"spec": {
"rules": [
{
"name": "add-name-label",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"mutate": {
"overlay": {
"metadata": {
"labels": {
"appname": "{{resource.metadata.name}}"
}
}
}
}
}
]
}
}
`)
rawResource := []byte(`
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "check-root-user"
},
"spec": {
"containers": [
{
"name": "check-root-user",
"image": "nginxinc/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true
}
}
]
}
}
`)
expectedPatch := []byte(`{ "op": "add", "path": "/metadata/labels", "value": {"appname":"check-root-user"} }`)
var policy kyverno.ClusterPolicy
json.Unmarshal(rawPolicy, &policy)
resourceUnstructured, err := ConvertToUnstructured(rawResource)
assert.NilError(t, err)
ctx := context.NewContext()
ctx.Add("resource", rawResource)
value, err := ctx.Query("resource.metadata.name")
t.Log(value)
if err != nil {
t.Error(err)
}
policyContext := PolicyContext{
Policy: policy,
Context: ctx,
NewResource: *resourceUnstructured}
er := Mutate(policyContext)
t.Log(string(expectedPatch))
t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
t.Error("patches dont match")
}
}

View file

@ -15,12 +15,14 @@ import (
jsonpatch "github.com/evanphx/json-patch"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/engine/anchor"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/variables"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/validate"
)
// processOverlay processes validation patterns on the resource
func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
func processOverlay(ctx context.EvalInterface, rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
startTime := time.Now()
glog.V(4).Infof("started applying overlay rule %q (%v)", rule.Name, startTime)
resp.Name = rule.Name
@ -29,8 +31,13 @@ func processOverlay(rule kyverno.Rule, resource unstructured.Unstructured) (resp
resp.RuleStats.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying overlay rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
}()
// substitute variables
// first pass we substitute all the JMESPATH substitution for the variable
// variable: {{<JMESPATH>}}
// if a JMESPATH fails, we dont return error but variable is substitured with nil and error log
overlay := variables.SubstituteVariables(ctx, rule.Mutation.Overlay)
patches, overlayerr := processOverlayPatches(resource.UnstructuredContent(), rule.Mutation.Overlay)
patches, overlayerr := processOverlayPatches(resource.UnstructuredContent(), overlay)
// resource does not satisfy the overlay pattern, we don't apply this rule
if !reflect.DeepEqual(overlayerr, overlayError{}) {
switch overlayerr.statusCode {

View file

@ -3,6 +3,7 @@ package webhooks
import (
"fmt"
"strings"
"encoding/json"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
@ -10,6 +11,7 @@ import (
"github.com/nirmata/kyverno/pkg/engine/response"
"k8s.io/api/admission/v1beta1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
authenticationv1 "k8s.io/api/authentication/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@ -152,3 +154,12 @@ func convertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
}
return resource, nil
}
func transformUser(userInfo authenticationv1.UserInfo) []byte {
data, err := json.Marshal(userInfo)
if err != nil {
glog.Errorf("failed to marshall resource %v: %v", userInfo, err)
return nil
}
return data
}

View file

@ -4,6 +4,7 @@ import (
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
engine "github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
policyctr "github.com/nirmata/kyverno/pkg/policy"
"github.com/nirmata/kyverno/pkg/utils"
@ -62,6 +63,12 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, polic
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
resource.SetNamespace(request.Namespace)
var engineResponses []response.EngineResponse
// build context
ctx := context.NewContext()
// load incoming resource into the context
ctx.Add("resource", request.Object.Raw)
ctx.Add("user", transformUser(request.UserInfo))
policyContext := engine.PolicyContext{
NewResource: *resource,
AdmissionInfo: engine.RequestInfo{

View file

@ -1,7 +1,6 @@
package webhooks
import (
"encoding/json"
"reflect"
"time"
@ -13,7 +12,6 @@ import (
policyctr "github.com/nirmata/kyverno/pkg/policy"
"github.com/nirmata/kyverno/pkg/utils"
v1beta1 "k8s.io/api/admission/v1beta1"
authenticationv1 "k8s.io/api/authentication/v1"
)
// HandleValidation handles validating webhook admission request
@ -66,6 +64,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
// load incoming resource into the context
ctx.Add("resource", request.Object.Raw)
ctx.Add("user", transformUser(request.UserInfo))
policyContext := engine.PolicyContext{
NewResource: newR,
OldResource: oldR,
@ -127,12 +126,3 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), request.Kind, request.Namespace, request.Name)
return true, ""
}
func transformUser(userInfo authenticationv1.UserInfo) []byte {
data, err := json.Marshal(userInfo)
if err != nil {
glog.Errorf("failed to marshall resource %v: %v", userInfo, err)
return nil
}
return data
}