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

Merge pull request #241 from nirmata/196_anchor_selection_on_peer

196 anchor selection on peer
This commit is contained in:
shuting 2019-07-31 18:04:19 -07:00 committed by GitHub
commit 9f36bd5b03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 1215 additions and 100 deletions

View file

@ -169,9 +169,4 @@ spec:
name:
type: string
data:
AnyValue: {}
---
kind: Namespace
apiVersion: v1
metadata:
name: "kyverno"
AnyValue: {}

View file

@ -6,6 +6,7 @@ import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/golang/glog"
@ -19,19 +20,27 @@ import (
func ProcessOverlay(rule kubepolicy.Rule, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, error) {
var resource interface{}
var appliedPatches [][]byte
err := json.Unmarshal(rawResource, &resource)
if err != nil {
if err := json.Unmarshal(rawResource, &resource); err != nil {
return nil, err
}
patches, err := mutateResourceWithOverlay(resource, *rule.Mutation.Overlay)
if err != nil {
return nil, err
resourceInfo := ParseResourceInfoFromObject(rawResource)
patches, err := processOverlayPatches(resource, *rule.Mutation.Overlay)
if err != nil && strings.Contains(err.Error(), "Conditions are not met") {
glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name)
return nil, nil
}
appliedPatches = append(appliedPatches, patches...)
return appliedPatches, err
return patches, err
}
func processOverlayPatches(resource, overlay interface{}) ([][]byte, error) {
if !meetConditions(resource, overlay) {
return nil, errors.New("Conditions are not met")
}
return mutateResourceWithOverlay(resource, overlay)
}
// mutateResourceWithOverlay is a start of overlaying process

173
pkg/engine/overlayCondition.go Executable file
View file

@ -0,0 +1,173 @@
package engine
import (
"reflect"
"github.com/golang/glog"
)
func meetConditions(resource, overlay interface{}) bool {
// overlay has no anchor, return true
if !hasNestedAnchors(overlay) {
return true
}
// resource item exists but has different type
// return false if anchor exists in overlay
// conditon never be true in this case
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
if hasNestedAnchors(overlay) {
glog.V(3).Infof("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource)
return false
}
return true
}
return checkConditions(resource, overlay)
}
func checkConditions(resource, overlay interface{}) bool {
switch typedOverlay := overlay.(type) {
case map[string]interface{}:
typedResource := resource.(map[string]interface{})
return checkConditionOnMap(typedResource, typedOverlay)
case []interface{}:
typedResource := resource.([]interface{})
return checkConditionOnArray(typedResource, typedOverlay)
default:
return true
}
}
func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}) bool {
anchors := getAnchorsFromMap(overlayMap)
if len(anchors) > 0 {
if !isConditionMetOnMap(resourceMap, anchors) {
return false
}
return true
}
for key, value := range overlayMap {
resourcePart, ok := resourceMap[key]
if ok && !isAddingAnchor(key) {
if !meetConditions(resourcePart, value) {
return false
}
}
}
// key does not exist or isAddingAnchor
return true
}
func checkConditionOnArray(resource, overlay []interface{}) bool {
if 0 == len(resource) {
return false
}
if 0 == len(overlay) {
return true
}
if reflect.TypeOf(resource[0]) != reflect.TypeOf(overlay[0]) {
glog.Warningf("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0])
return false
}
return checkConditionsOnArrayOfSameTypes(resource, overlay)
}
func checkConditionsOnArrayOfSameTypes(resource, overlay []interface{}) bool {
switch overlay[0].(type) {
case map[string]interface{}:
return checkConditionsOnArrayOfMaps(resource, overlay)
default:
glog.Warningf("Anchors not supported in overlay of array type %T\n", overlay[0])
return false
}
}
func checkConditionsOnArrayOfMaps(resource, overlay []interface{}) bool {
for _, overlayElement := range overlay {
typedOverlay := overlayElement.(map[string]interface{})
anchors, overlayWithoutAnchor := getElementsFromMap(typedOverlay)
if len(anchors) > 0 {
if !validAnchorMap(anchors) {
return false
}
if !isConditionMet(resource, anchors) {
return false
}
}
for key, val := range overlayWithoutAnchor {
if hasNestedAnchors(val) {
for _, resourceElement := range resource {
typedResource := resourceElement.(map[string]interface{})
if resourcePart, ok := typedResource[key]; ok {
if !meetConditions(resourcePart, val) {
return false
}
}
}
}
}
}
return true
}
func validAnchorMap(anchors map[string]interface{}) bool {
for _, val := range anchors {
switch val.(type) {
case map[string]interface{}, []interface{}:
glog.Warning("Maps and arrays as patterns are not supported")
return false
}
}
return true
}
func isConditionMet(resource []interface{}, anchors map[string]interface{}) bool {
for _, resourceElement := range resource {
typedResource := resourceElement.(map[string]interface{})
for key, pattern := range anchors {
key = key[1 : len(key)-1]
value, ok := typedResource[key]
if !ok {
continue
}
if len(resource) == 1 {
if !ValidateValueWithPattern(value, pattern) {
return false
}
} else {
ValidateValueWithPattern(value, pattern)
return true
}
}
}
return true
}
func isConditionMetOnMap(resource, anchors map[string]interface{}) bool {
for key, pattern := range anchors {
key = key[1 : len(key)-1]
value, ok := resource[key]
if !ok {
continue
}
if !ValidateValueWithPattern(value, pattern) {
return false
}
}
return true
}

View file

@ -0,0 +1,602 @@
package engine
import (
"encoding/json"
"testing"
"gotest.tools/assert"
)
func TestMeetConditions_NoAnchor(t *testing.T) {
overlayRaw := []byte(`
{
"subsets":[
{
"ports":[
{
"name":"secure-connection",
"port":444,
"protocol":"UDP"
}
]
}
]
}`)
var overlay interface{}
json.Unmarshal(overlayRaw, &overlay)
res := meetConditions(nil, overlay)
assert.Assert(t, res)
}
func TestMeetConditions_invalidConditionalAnchor(t *testing.T) {
resourceRaw := []byte(`
{
"apiVersion":"v1",
"kind":"Endpoints",
"metadata":{
"name":"test-endpoint",
"labels":{
"label":"test"
}
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.10.171"
}
],
"ports":[
{
"name":"secure-connection",
"port":443,
"protocol":"TCP"
}
]
}
]
}`)
overlayRaw := []byte(`
{
"subsets":[
{
"(ports)":[
{
"name":"secure-connection",
"port":444,
"protocol":"UDP"
}
]
}
]
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
res := meetConditions(resource, overlay)
assert.Assert(t, !res)
overlayRaw = []byte(`
{
"(subsets)":[
{
"ports":[
{
"name":"secure-connection",
"port":444,
"protocol":"UDP"
}
]
}
]
}`)
json.Unmarshal(overlayRaw, &overlay)
res = meetConditions(resource, overlay)
assert.Assert(t, !res)
}
func TestMeetConditions_DifferentTypes(t *testing.T) {
resourceRaw := []byte(`
{
"apiVersion":"v1",
"kind":"Endpoints",
"metadata":{
"name":"test-endpoint",
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.10.171"
}
],
}
]
}`)
overlayRaw := []byte(`
{
"subsets":[
{
"ports":[
{
"(name)":"secure-connection",
"port":444,
"protocol":"UDP"
}
]
}
]
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
// anchor exist
res := meetConditions(resource, overlay)
assert.Assert(t, !res)
}
func TestMeetConditions_anchosInSameObject(t *testing.T) {
resourceRaw := []byte(`
{
"apiVersion":"v1",
"kind":"Endpoints",
"metadata":{
"name":"test-endpoint",
"labels":{
"label":"test"
}
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.10.171"
}
],
"ports":[
{
"name":"secure-connection",
"port":443,
"protocol":"TCP"
}
]
}
]
}`)
overlayRaw := []byte(`
{
"subsets":[
{
"ports":[
{
"(name)":"secure-connection",
"(port)":444,
"protocol":"UDP"
}
]
}
]
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
// no anchor
res := meetConditions(resource, overlay)
assert.Assert(t, !res)
}
func TestMeetConditions_anchorOnPeer(t *testing.T) {
resourceRaw := []byte(`
{
"apiVersion":"v1",
"kind":"Endpoints",
"metadata":{
"name":"test-endpoint",
"labels":{
"label":"test"
}
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.10.171"
}
],
"ports":[
{
"name":"secure-connection",
"port":443,
"protocol":"TCP"
}
]
}
]
}`)
overlayRaw := []byte(`
{
"subsets":[
{
"addresses":[
{
"(ip)":"192.168.10.171"
}
],
"ports":[
{
"(name)":"secure-connection",
"port":444,
"protocol":"UDP"
}
]
}
]
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
res := meetConditions(resource, overlay)
assert.Assert(t, res)
}
func TestMeetConditions_anchorsOnMetaAndSpec(t *testing.T) {
overlayRaw := []byte(`{
"spec": {
"template": {
"metadata": {
"labels": {
"(app)": "nginx"
}
},
"spec": {
"containers": [
{
"(image)": "*:latest",
"imagePullPolicy": "IfNotPresent",
"ports": [
{
"containerPort": 8080
}
]
}
]
}
}
}
}`)
resourceRaw := []byte(`{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "nginx-deployment",
"labels": {
"app": "nginx"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "nginx"
}
},
"template": {
"metadata": {
"labels": {
"app": "nginx"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:latest",
"ports": [
{
"containerPort": 80
}
]
},
{
"name": "ghost",
"image": "ghost:latest"
}
]
}
}
}
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
res := meetConditions(resource, overlay)
assert.Assert(t, res)
}
var resourceRawAnchorOnPeers = []byte(`{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "psp-demo-unprivileged",
"labels": {
"app.type": "prod"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "psp"
}
},
"template": {
"metadata": {
"labels": {
"app": "psp"
}
},
"spec": {
"securityContext": {
"runAsNonRoot": true
},
"containers": [
{
"name": "sec-ctx-unprivileged",
"image": "nginxinc/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"allowPrivilegeEscalation": false
},
"env": [
{
"name": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
func TestMeetConditions_anchorsOnPeer_single(t *testing.T) {
overlayRaw := []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(image)": "*/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"allowPrivilegeEscalation": false
},
"env": [
{
"name": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRawAnchorOnPeers, &resource)
json.Unmarshal(overlayRaw, &overlay)
res := meetConditions(resource, overlay)
assert.Assert(t, res)
}
func TestMeetConditions_anchorsOnPeer_two(t *testing.T) {
overlayRaw := []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(image)": "*/nginx-unprivileged",
"securityContext": {
"(runAsNonRoot)": true,
"allowPrivilegeEscalation": false
},
"env": [
{
"name": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRawAnchorOnPeers, &resource)
json.Unmarshal(overlayRaw, &overlay)
res := meetConditions(resource, overlay)
assert.Assert(t, res)
overlayRaw = []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(image)": "*/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"allowPrivilegeEscalation": false
},
"env": [
{
"(name)": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
json.Unmarshal(overlayRaw, &overlay)
res = meetConditions(resource, overlay)
assert.Assert(t, res)
overlayRaw = []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"image": "*/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"(allowPrivilegeEscalation)": false
},
"env": [
{
"(name)": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
json.Unmarshal(overlayRaw, &overlay)
res = meetConditions(resource, overlay)
assert.Assert(t, res)
}
func TestMeetConditions_anchorsOnPeer_multiple(t *testing.T) {
overlayRaw := []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(image)": "*/nginx-unprivileged",
"securityContext": {
"(runAsNonRoot)": true,
"allowPrivilegeEscalation": false
},
"env": [
{
"(name)": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRawAnchorOnPeers, &resource)
json.Unmarshal(overlayRaw, &overlay)
res := meetConditions(resource, overlay)
assert.Assert(t, res)
overlayRaw = []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(image)": "*/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"(allowPrivilegeEscalation)": false
},
"env": [
{
"(name)": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
json.Unmarshal(overlayRaw, &overlay)
res = meetConditions(resource, overlay)
assert.Assert(t, res)
overlayRaw = []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(image)": "*/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"(allowPrivilegeEscalation)": false
},
"env": [
{
"(name)": "ENV_KEY",
"(value)": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
json.Unmarshal(overlayRaw, &overlay)
res = meetConditions(resource, overlay)
assert.Assert(t, res)
}

View file

@ -0,0 +1,25 @@
package engine
import (
"encoding/json"
"strings"
"github.com/golang/glog"
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
)
func patchOverlay(rule kubepolicy.Rule, rawResource []byte) ([][]byte, error) {
var resource interface{}
if err := json.Unmarshal(rawResource, &resource); err != nil {
return nil, err
}
resourceInfo := ParseResourceInfoFromObject(rawResource)
patches, err := processOverlayPatches(resource, *rule.Mutation.Overlay)
if err != nil && strings.Contains(err.Error(), "Conditions are not met") {
glog.Infof("Resource does not meet conditions in overlay pattern, resource=%s, rule=%s\n", resourceInfo, rule.Name)
return nil, nil
}
return patches, err
}

View file

@ -1,70 +0,0 @@
package engine
import (
"reflect"
)
// func processoverlay(rule kubepolicy.Rule, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, error) {
// var resource interface{}
// var appliedPatches [][]byte
// err := json.Unmarshal(rawResource, &resource)
// if err != nil {
// return nil, err
// }
// patches, err := mutateResourceWithOverlay(resource, *rule.Mutation.Overlay)
// if err != nil {
// return nil, err
// }
// appliedPatches = append(appliedPatches, patches...)
// return appliedPatches, err
// }
func applyoverlay(resource, overlay interface{}, path string) ([][]byte, error) {
var appliedPatches [][]byte
// resource item exists but has different type - replace
// all subtree within this path by overlay
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
patch, err := replaceSubtree(overlay, path)
if err != nil {
return nil, err
}
appliedPatches = append(appliedPatches, patch)
}
return applyOverlayForSameTypes(resource, overlay, path)
}
func checkConditions(resource, overlay interface{}, path string) bool {
switch typedOverlay := overlay.(type) {
case map[string]interface{}:
typedResource := resource.(map[string]interface{})
if !checkConditionOnMap(typedResource, typedOverlay) {
return false
}
case []interface{}:
typedResource := resource.([]interface{})
if !checkConditionOnArray(typedResource, typedOverlay) {
return false
}
case string, float64, int64, bool:
default:
return false
}
return true
}
func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}) bool {
// _ := getAnchorsFromMap(overlayMap)
return false
}
func checkConditionOnArray(resource, overlay []interface{}) bool {
return false
}

View file

@ -16,7 +16,7 @@ func compareJSONAsMap(t *testing.T, expected, actual []byte) {
assert.Assert(t, reflect.DeepEqual(expectedMap, actualMap))
}
func TestApplyOverlay_NestedListWithAnchor(t *testing.T) {
func TestProcessOverlayPatches_NestedListWithAnchor(t *testing.T) {
resourceRaw := []byte(`
{
"apiVersion":"v1",
@ -65,7 +65,7 @@ func TestApplyOverlay_NestedListWithAnchor(t *testing.T) {
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, patches != nil)
@ -109,7 +109,7 @@ func TestApplyOverlay_NestedListWithAnchor(t *testing.T) {
compareJSONAsMap(t, expectedResult, patched)
}
func TestApplyOverlay_InsertIntoArray(t *testing.T) {
func TestProcessOverlayPatches_InsertIntoArray(t *testing.T) {
resourceRaw := []byte(`
{
"apiVersion":"v1",
@ -165,7 +165,7 @@ func TestApplyOverlay_InsertIntoArray(t *testing.T) {
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, patches != nil)
@ -226,7 +226,7 @@ func TestApplyOverlay_InsertIntoArray(t *testing.T) {
compareJSONAsMap(t, expectedResult, patched)
}
func TestApplyOverlay_TestInsertToArray(t *testing.T) {
func TestProcessOverlayPatches_TestInsertToArray(t *testing.T) {
overlayRaw := []byte(`
{
"spec":{
@ -286,7 +286,7 @@ func TestApplyOverlay_TestInsertToArray(t *testing.T) {
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, patches != nil)
@ -301,7 +301,7 @@ func TestApplyOverlay_TestInsertToArray(t *testing.T) {
assert.Assert(t, patched != nil)
}
func TestApplyOverlay_ImagePullPolicy(t *testing.T) {
func TestProcessOverlayPatches_ImagePullPolicy(t *testing.T) {
overlayRaw := []byte(`{
"spec": {
"template": {
@ -369,7 +369,7 @@ func TestApplyOverlay_ImagePullPolicy(t *testing.T) {
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, len(patches) != 0)
@ -429,9 +429,76 @@ func TestApplyOverlay_ImagePullPolicy(t *testing.T) {
}`)
compareJSONAsMap(t, expectedResult, doc)
overlayRaw = []byte(`{
"spec": {
"template": {
"metadata": {
"labels": {
"(app)": "nginx"
}
},
"spec": {
"containers": [
{
"(image)": "*:latest",
"imagePullPolicy": "IfNotPresent",
"ports": [
{
"containerPort": 8080
}
]
}
]
}
}
}
}`)
json.Unmarshal(overlayRaw, &overlay)
patches, err = processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, len(patches) != 0)
doc, err = ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
compareJSONAsMap(t, expectedResult, doc)
overlayRaw = []byte(`{
"spec": {
"template": {
"metadata": {
"labels": {
"(app)": "nginx1"
}
},
"spec": {
"containers": [
{
"(image)": "*:latest",
"imagePullPolicy": "IfNotPresent",
"ports": [
{
"containerPort": 8080
}
]
}
]
}
}
}
}`)
json.Unmarshal(overlayRaw, &overlay)
patches, err = processOverlayPatches(resource, overlay)
assert.Error(t, err, "Conditions are not met")
assert.Assert(t, len(patches) == 0)
}
func TestApplyOverlay_AddingAnchor(t *testing.T) {
func TestProcessOverlayPatches_AddingAnchor(t *testing.T) {
overlayRaw := []byte(`{
"metadata": {
"name": "nginx-deployment",
@ -455,7 +522,7 @@ func TestApplyOverlay_AddingAnchor(t *testing.T) {
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, len(patches) != 0)
@ -474,7 +541,7 @@ func TestApplyOverlay_AddingAnchor(t *testing.T) {
compareJSONAsMap(t, expectedResult, doc)
}
func TestApplyOverlay_AddingAnchorInsideListElement(t *testing.T) {
func TestProcessOverlayPatches_AddingAnchorInsideListElement(t *testing.T) {
overlayRaw := []byte(`
{
"spec": {
@ -540,7 +607,7 @@ func TestApplyOverlay_AddingAnchorInsideListElement(t *testing.T) {
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := applyOverlay(resource, overlay, "/")
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, len(patches) != 0)
@ -592,4 +659,290 @@ func TestApplyOverlay_AddingAnchorInsideListElement(t *testing.T) {
}
}`)
compareJSONAsMap(t, expectedResult, doc)
// multiple anchors
overlayRaw = []byte(`
{
"spec": {
"template": {
"metadata": {
"labels": {
"(app)": "nginx"
}
},
"spec": {
"containers": [
{
"(image)": "*:latest",
"+(imagePullPolicy)": "IfNotPresent"
}
]
}
}
}
}`)
json.Unmarshal(overlayRaw, &overlay)
patches, err = processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, len(patches) != 0)
doc, err = ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
compareJSONAsMap(t, expectedResult, doc)
}
func TestProcessOverlayPatches_anchorOnPeer(t *testing.T) {
resourceRaw := []byte(`
{
"apiVersion":"v1",
"kind":"Endpoints",
"metadata":{
"name":"test-endpoint",
"labels":{
"label":"test"
}
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.10.171"
}
],
"ports":[
{
"name":"secure-connection",
"port":443,
"protocol":"TCP"
}
]
}
]
}`)
overlayRaw := []byte(`
{
"subsets":[
{
"addresses":[
{
"(ip)":"192.168.10.171"
}
],
"ports":[
{
"(name)":"secure-connection",
"port":444,
"protocol":"UDP"
}
]
}
]
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRaw, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(` {
"apiVersion":"v1",
"kind":"Endpoints",
"metadata":{
"name":"test-endpoint",
"labels":{
"label":"test"
}
},
"subsets":[
{
"addresses":[
{
"ip":"192.168.10.171"
}
],
"ports":[
{
"name":"secure-connection",
"port":444,
"protocol":"UDP"
}
]
}
]
}`)
compareJSONAsMap(t, expectedResult, doc)
overlayRaw = []byte(`
{
"subsets":[
{
"addresses":[
{
"ip":"192.168.10.171"
}
],
"ports":[
{
"(name)":"secure-connection",
"(port)":444,
"protocol":"UDP"
}
]
}
]
}`)
json.Unmarshal(overlayRaw, &overlay)
patches, err = processOverlayPatches(resource, overlay)
assert.Error(t, err, "Conditions are not met")
assert.Assert(t, len(patches) == 0)
}
func TestProcessOverlayPatches_insertWithCondition(t *testing.T) {
resourceRaw := []byte(`{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "psp-demo-unprivileged",
"labels": {
"app.type": "prod"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "psp"
}
},
"template": {
"metadata": {
"labels": {
"app": "psp"
}
},
"spec": {
"securityContext": {
"runAsNonRoot": true
},
"containers": [
{
"name": "sec-ctx-unprivileged",
"image": "nginxinc/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"allowPrivilegeEscalation": false
},
"env": [
{
"name": "ENV_KEY",
"value": "ENV_VALUE"
}
]
}
]
}
}
}
}`)
overlayRaw := []byte(`{
"spec": {
"template": {
"spec": {
"containers": [
{
"(image)": "*/nginx-unprivileged",
"securityContext": {
"(runAsNonRoot)": true,
"allowPrivilegeEscalation": true
},
"env": [
{
"name": "ENV_NEW_KEY",
"value": "ENV_NEW_VALUE"
}
]
}
]
}
}
}
}`)
var resource, overlay interface{}
json.Unmarshal(resourceRawAnchorOnPeers, &resource)
json.Unmarshal(overlayRaw, &overlay)
patches, err := processOverlayPatches(resource, overlay)
assert.NilError(t, err)
assert.Assert(t, len(patches) != 0)
doc, err := ApplyPatches(resourceRaw, patches)
assert.NilError(t, err)
expectedResult := []byte(`{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "psp-demo-unprivileged",
"labels": {
"app.type": "prod"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "psp"
}
},
"template": {
"metadata": {
"labels": {
"app": "psp"
}
},
"spec": {
"securityContext": {
"runAsNonRoot": true
},
"containers": [
{
"name": "sec-ctx-unprivileged",
"image": "nginxinc/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true,
"allowPrivilegeEscalation": true
},
"env": [
{
"name": "ENV_KEY",
"value": "ENV_VALUE"
},
{
"name": "ENV_NEW_KEY",
"value": "ENV_NEW_VALUE"
}
]
}
]
}
}
}
}`)
compareJSONAsMap(t, expectedResult, doc)
}

View file

@ -222,6 +222,15 @@ func parseMetadataFromObject(bytes []byte) map[string]interface{} {
return meta
}
// ParseResourceInfoFromObject get kind/namepace/name from resource
func ParseResourceInfoFromObject(rawResource []byte) string {
kind := ParseKindFromObject(rawResource)
namespace := ParseNamespaceFromObject(rawResource)
name := ParseNameFromObject(rawResource)
return strings.Join([]string{kind, namespace, name}, "/")
}
//ParseKindFromObject get kind from resource
func ParseKindFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
@ -302,6 +311,20 @@ func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{}
return result
}
func getElementsFromMap(anchorsMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
anchors := make(map[string]interface{})
elementsWithoutanchor := make(map[string]interface{})
for key, value := range anchorsMap {
if isConditionAnchor(key) || isExistanceAnchor(key) {
anchors[key] = value
} else if !isAddingAnchor(key) {
elementsWithoutanchor[key] = value
}
}
return anchors, elementsWithoutanchor
}
func getAnchorFromMap(anchorsMap map[string]interface{}) (string, interface{}) {
for key, value := range anchorsMap {
if isConditionAnchor(key) || isExistanceAnchor(key) {

View file

@ -12,7 +12,7 @@ import (
// HandleMutation handles mutating webhook admission request
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
glog.V(3).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
policies, err := ws.policyLister.List(labels.NewSelector())

View file

@ -33,6 +33,8 @@ func NewWebhookRegistrationClient(clientConfig *rest.Config, client *client.Clie
return nil, err
}
glog.V(3).Infof("Registering webhook client using serverIP %s\n", serverIP)
return &WebhookRegistrationClient{
registrationClient: registrationClient,
client: client,

View file

@ -137,9 +137,10 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
// RunAsync TLS server in separate thread and returns control immediately
func (ws *WebhookServer) RunAsync() {
go func(ws *WebhookServer) {
glog.V(3).Infof("serving on %s\n", ws.server.Addr)
err := ws.server.ListenAndServeTLS("", "")
if err != nil {
glog.Fatal(err)
glog.Fatalf("error serving TLS: %v\n", err)
}
}(ws)
glog.Info("Started Webhook Server")

View file

@ -12,7 +12,8 @@ import (
// HandleValidation handles validating webhook admission request
// If there are no errors in validating rule we apply generation rules
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
glog.V(3).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
policyInfos := []*info.PolicyInfo{}

View file

@ -13,7 +13,7 @@ hub_user_name="nirmata"
project_name="kyverno"
echo "# Ensuring Go dependencies..."
dep ensure || exit 2
dep ensure -v || exit 2
echo "# Building executable ${project_name}..."
chmod +x scripts/update-codegen.sh

View file

@ -55,6 +55,7 @@ openssl x509 -req -in ${destdir}/webhook.csr -CA ${destdir}/rootCA.crt -CAkey ${
kubectl delete -f definitions/install_debug.yaml 2>/dev/null
kubectl delete namespace kyverno 2>/dev/null
echo "Generating corresponding kubernetes secrets for TLS pair and root CA"
# create project namespace