1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00

fix 365 annotation_bug

This commit is contained in:
Shuting Zhao 2019-10-07 18:31:14 -07:00
parent ed960ad277
commit 2077409c85
4 changed files with 153 additions and 107 deletions

View file

@ -11,12 +11,10 @@ import (
const (
policyAnnotation = "policies.kyverno.io"
// lastAppliedPatches = policyAnnotation + "last-applied-patches"
)
type policyPatch struct {
PolicyName string `json:"policyname"`
// RulePatches []string `json:"patches"`
PolicyName string `json:"policyname"`
RulePatches interface{} `json:"patches"`
}
@ -32,28 +30,32 @@ type response struct {
Value interface{} `json:"value"`
}
func generateAnnotationPatches(annotations map[string]string, policyResponse engine.PolicyResponse) []byte {
func generateAnnotationPatches(annotations map[string]string, engineResponses []engine.EngineResponseNew) []byte {
if annotations == nil {
annotations = map[string]string{}
annotations = make(map[string]string)
}
var patchResponse response
value := generateAnnotationsFromPolicyResponse(policyResponse)
value := annotationFromPolicyResponses(engineResponses)
if value == nil {
// no patches or error while processing patches
return nil
}
if _, ok := annotations[policyAnnotation]; ok {
// create update patch string
patchResponse = response{
Path: "/metadata/annotations/" + policyAnnotation,
Op: "replace",
Path: "/metadata/annotations/" + policyAnnotation,
Value: string(value),
}
} else {
// insert 'policies.kyverno.io' entry in annotation map
annotations[policyAnnotation] = string(value)
patchResponse = response{
Path: "/metadata/annotations",
Op: "add",
Value: map[string]string{policyAnnotation: string(value)},
Path: "/metadata/annotations",
Value: annotations,
}
}
@ -68,109 +70,59 @@ func generateAnnotationPatches(annotations map[string]string, policyResponse eng
return patchByte
}
// func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos []info.PolicyInfo) []byte {
// annots := resource.GetAnnotations()
// if annots == nil {
// annots = map[string]string{}
// }
// var patchResponse response
// value := annotationFromPolicies(policyInfos)
// if _, ok := annots[policyAnnotation]; ok {
// // create update patch string
// patchResponse = response{
// Op: "replace",
// Path: "/metadata/annotations/" + policyAnnotation,
// Value: string(value),
// }
// } else {
// patchResponse = response{
// Op: "add",
// Path: "/metadata/annotations",
// Value: map[string]string{policyAnnotation: string(value)},
// }
// }
// patchByte, _ := json.Marshal(patchResponse)
// // check the patch
// _, err := jsonpatch.DecodePatch([]byte("[" + string(patchByte) + "]"))
// if err != nil {
// glog.Errorf("Failed to make patch from annotation'%s', err: %v\n ", string(patchByte), err)
// }
// return patchByte
// }
// func annotationFromPolicies(policyInfos []info.PolicyInfo) []byte {
// var policyPatches []policyPatch
// for _, policyInfo := range policyInfos {
// var pp policyPatch
// pp.PolicyName = policyInfo.Name
// pp.RulePatches = annotationFromPolicy(policyInfo)
// policyPatches = append(policyPatches, pp)
// }
// result, _ := json.Marshal(policyPatches)
// return result
// }
func generateAnnotationsFromPolicyResponse(policyResponse engine.PolicyResponse) []byte {
var rulePatches []rulePatch
// generate annotation for each mutation JSON patch to be applied on the resource
for _, rule := range policyResponse.Rules {
var patchmap map[string]string
patch := engine.JoinPatches(rule.Patches)
if err := json.Unmarshal(patch, &patchmap); err != nil {
glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
func annotationFromPolicyResponses(engineResponses []engine.EngineResponseNew) []byte {
var policyPatches []policyPatch
for _, engineResponse := range engineResponses {
if !engineResponse.IsSuccesful() {
glog.V(3).Infof("Policy %s failed, skip preparing annotation\n", engineResponse.PolicyResponse.Policy)
continue
}
rp := rulePatch{
RuleName: rule.Name,
Op: patchmap["op"],
Path: patchmap["path"]}
rulePatches = append(rulePatches, rp)
glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
var pp policyPatch
rulePatches := annotationFromPolicyResponse(engineResponse.PolicyResponse)
if rulePatches == nil {
continue
}
pp.RulePatches = rulePatches
pp.PolicyName = engineResponse.PolicyResponse.Policy
policyPatches = append(policyPatches, pp)
}
patch, err := json.Marshal(rulePatches)
if err != nil {
glog.Infof("failed to marshall: %v", err)
// return nil if there's no patches
// otherwise result = null, len(result) = 4
if policyPatches == nil {
return nil
}
return patch
result, _ := json.Marshal(policyPatches)
return result
}
// func annotationFromPolicy(policyInfo info.PolicyInfo) []rulePatch {
// if !policyInfo.IsSuccessful() {
// glog.V(2).Infof("Policy %s failed, skip preparing annotation\n", policyInfo.Name)
// return nil
// }
func annotationFromPolicyResponse(policyResponse engine.PolicyResponse) []rulePatch {
var rulePatches []rulePatch
for _, ruleInfo := range policyResponse.Rules {
for _, patch := range ruleInfo.Patches {
var patchmap map[string]interface{}
if err := json.Unmarshal(patch, &patchmap); err != nil {
glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
continue
}
// var rulePatches []rulePatch
// for _, ruleInfo := range policyInfo.Rules {
rp := rulePatch{
RuleName: ruleInfo.Name,
Op: patchmap["op"].(string),
Path: patchmap["path"].(string)}
// for _, patch := range ruleInfo.Patches {
// var patchmap map[string]string
rulePatches = append(rulePatches, rp)
glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
}
}
// if err := json.Unmarshal(patch, &patchmap); err != nil {
// glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
// continue
// }
if len(rulePatches) == 0 {
return nil
}
// rp := rulePatch{
// RuleName: ruleInfo.Name,
// Op: patchmap["op"],
// Path: patchmap["path"]}
// rulePatches = append(rulePatches, rp)
// glog.V(4).Infof("Annotation value prepared: %v\n", rulePatches)
// }
// }
// return rulePatches
// }
return rulePatches
}

View file

@ -0,0 +1,92 @@
package webhooks
import (
"testing"
"github.com/nirmata/kyverno/pkg/engine"
"gotest.tools/assert"
)
func newPolicyResponse(policy, rule string, patchesStr []string, success bool) engine.PolicyResponse {
var patches [][]byte
for _, p := range patchesStr {
patches = append(patches, []byte(p))
}
return engine.PolicyResponse{
Policy: policy,
Rules: []engine.RuleResponse{
engine.RuleResponse{
Name: rule,
Patches: patches,
Success: success},
},
}
}
func newEngineResponse(policy, rule string, patchesStr []string, success bool) engine.EngineResponseNew {
return engine.EngineResponseNew{
PolicyResponse: newPolicyResponse(policy, rule, patchesStr, success),
}
}
func Test_empty_annotation(t *testing.T) {
patchStr := `{ "op": "replace", "path": "/spec/containers/0/imagePullPolicy", "value": "IfNotPresent" }`
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{patchStr}, true)
annPatches := generateAnnotationPatches(nil, []engine.EngineResponseNew{engineResponse})
expectedPatches := `{"op":"add","path":"/metadata/annotations","value":{"policies.kyverno.io":"[{\"policyname\":\"mutate-container\",\"patches\":[{\"rulename\":\"default-imagepullpolicy\",\"op\":\"replace\",\"path\":\"/spec/containers/0/imagePullPolicy\"}]}]"}}`
assert.Assert(t, string(annPatches) == expectedPatches)
}
func Test_exist_annotation(t *testing.T) {
annotation := map[string]string{
"test": "annotation",
}
patchStr := `{ "op": "replace", "path": "/spec/containers/0/imagePullPolicy", "value": "IfNotPresent" }`
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{patchStr}, true)
annPatches := generateAnnotationPatches(annotation, []engine.EngineResponseNew{engineResponse})
expectedPatches := `{"op":"add","path":"/metadata/annotations","value":{"policies.kyverno.io":"[{\"policyname\":\"mutate-container\",\"patches\":[{\"rulename\":\"default-imagepullpolicy\",\"op\":\"replace\",\"path\":\"/spec/containers/0/imagePullPolicy\"}]}]","test":"annotation"}}`
assert.Assert(t, string(annPatches) == expectedPatches)
}
func Test_exist_kyverno_annotation(t *testing.T) {
annotation := map[string]string{
"policies.kyverno.io": "old-annotation",
}
patchStr := `{ "op": "replace", "path": "/spec/containers/0/imagePullPolicy", "value": "IfNotPresent" }`
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{patchStr}, true)
annPatches := generateAnnotationPatches(annotation, []engine.EngineResponseNew{engineResponse})
expectedPatches := `{"op":"replace","path":"/metadata/annotations/policies.kyverno.io","value":"[{\"policyname\":\"mutate-container\",\"patches\":[{\"rulename\":\"default-imagepullpolicy\",\"op\":\"replace\",\"path\":\"/spec/containers/0/imagePullPolicy\"}]}]"}`
assert.Assert(t, string(annPatches) == expectedPatches)
}
func Test_annotation_nil_patch(t *testing.T) {
annotation := map[string]string{
"policies.kyverno.io": "old-annotation",
}
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", nil, true)
annPatches := generateAnnotationPatches(annotation, []engine.EngineResponseNew{engineResponse})
assert.Assert(t, annPatches == nil)
engineResponseNew := newEngineResponse("mutate-container", "default-imagepullpolicy", []string{""}, true)
annPatchesNew := generateAnnotationPatches(annotation, []engine.EngineResponseNew{engineResponseNew})
assert.Assert(t, annPatchesNew == nil)
}
func Test_annotation_failed_Patch(t *testing.T) {
annotation := map[string]string{
"policies.kyverno.io": "old-annotation",
}
engineResponse := newEngineResponse("mutate-container", "default-imagepullpolicy", nil, false)
annPatches := generateAnnotationPatches(annotation, []engine.EngineResponseNew{engineResponse})
assert.Assert(t, annPatches == nil)
}

View file

@ -91,15 +91,17 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool
}
// gather patches
patches = append(patches, engineResponse.GetPatches()...)
// generate annotations
if annPatches := generateAnnotationPatches(resource.GetAnnotations(), engineResponse.PolicyResponse); annPatches != nil {
patches = append(patches, annPatches)
}
glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
//TODO: check if there is an order to policy application on resource
// resource = &engineResponse.PatchedResource
}
// generate annotations
if annPatches := generateAnnotationPatches(resource.GetAnnotations(), engineResponses); annPatches != nil {
patches = append(patches, annPatches)
}
// ADD EVENTS
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
ws.eventGen.Add(events...)

View file

@ -86,7 +86,7 @@ func processResourceWithPatches(patch []byte, resource []byte) []byte {
if patch == nil {
return nil
}
glog.Info(string(resource))
resource, err := engine.ApplyPatchNew(resource, patch)
if err != nil {
glog.Errorf("failed to patch resource: %v", err)