1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 09:26:54 +00:00
kyverno/pkg/annotations/annotations.go

333 lines
8.4 KiB
Go
Raw Normal View History

2019-07-17 15:04:02 -07:00
package annotations
import (
"encoding/json"
"reflect"
2019-07-17 15:04:02 -07:00
"github.com/golang/glog"
pinfo "github.com/nirmata/kyverno/pkg/info"
2019-07-17 15:04:02 -07:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
//Policy information for annotations
type Policy struct {
Status string `json:"status"`
// Key Type/Name
MutationRules map[string]Rule `json:"mutationrules,omitempty"`
ValidationRules map[string]Rule `json:"validationrules,omitempty"`
GenerationRules map[string]Rule `json:"generationrules,omitempty"`
2019-07-17 15:04:02 -07:00
}
//Rule information for annotations
type Rule struct {
Status string `json:"status"`
2019-07-17 23:13:28 -07:00
Changes string `json:"changes,omitempty"` // TODO for mutation changes
2019-07-23 13:59:48 -04:00
Message string `json:"message,omitempty"`
2019-07-18 15:49:29 -07:00
}
2019-07-19 20:30:55 -07:00
func (p *Policy) getOverAllStatus() string {
// mutation
for _, v := range p.MutationRules {
if v.Status == "Failure" {
return "Failure"
}
}
// validation
for _, v := range p.ValidationRules {
if v.Status == "Failure" {
return "Failure"
}
}
// generation
for _, v := range p.GenerationRules {
if v.Status == "Failure" {
return "Failure"
}
}
return "Success"
}
func getRules(rules []*pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule {
if len(rules) == 0 {
return nil
}
annrules := make(map[string]Rule, 0)
// var annrules map[string]Rule
2019-07-17 15:04:02 -07:00
for _, r := range rules {
if r.RuleType != ruleType {
continue
}
2019-07-19 16:22:26 -07:00
rule := Rule{Status: getStatus(r.IsSuccessful())}
if !r.IsSuccessful() {
2019-07-23 13:59:48 -04:00
rule.Message = r.GetErrorString()
2019-07-19 16:22:26 -07:00
}
annrules[r.Name] = rule
2019-07-17 15:04:02 -07:00
}
return annrules
}
func (p *Policy) updatePolicy(obj *Policy, ruleType pinfo.RuleType) bool {
updates := false
// Check Mutation rules
switch ruleType {
case pinfo.Mutation:
if p.compareMutationRules(obj.MutationRules) {
updates = true
}
case pinfo.Validation:
if p.compareValidationRules(obj.ValidationRules) {
updates = true
}
case pinfo.Generation:
if p.compareGenerationRules(obj.GenerationRules) {
updates = true
}
2019-07-19 20:30:55 -07:00
if p.Status != obj.Status {
updates = true
}
}
2019-07-19 20:30:55 -07:00
// check if any rules failed
p.Status = p.getOverAllStatus()
// If there are any updates then the annotation can be updated, can skip
return updates
2019-07-17 15:04:02 -07:00
}
func (p *Policy) compareMutationRules(rules map[string]Rule) bool {
// check if the rules have changed
if !reflect.DeepEqual(p.MutationRules, rules) {
p.MutationRules = rules
return true
2019-07-17 15:04:02 -07:00
}
return false
2019-07-17 15:04:02 -07:00
}
func (p *Policy) compareValidationRules(rules map[string]Rule) bool {
// check if the rules have changed
if !reflect.DeepEqual(p.ValidationRules, rules) {
p.ValidationRules = rules
return true
}
return false
}
func (p *Policy) compareGenerationRules(rules map[string]Rule) bool {
// check if the rules have changed
if !reflect.DeepEqual(p.GenerationRules, rules) {
p.GenerationRules = rules
return true
}
return false
}
func newAnnotationForPolicy(pi *pinfo.PolicyInfo) *Policy {
2019-07-17 15:04:02 -07:00
return &Policy{Status: getStatus(pi.IsSuccessful()),
MutationRules: getRules(pi.Rules, pinfo.Mutation),
ValidationRules: getRules(pi.Rules, pinfo.Validation),
GenerationRules: getRules(pi.Rules, pinfo.Generation),
}
2019-07-17 15:04:02 -07:00
}
//AddPolicy will add policy annotation if not present or update if present
// modifies obj
// returns true, if there is any update -> caller need to update the obj
// returns false, if there is no change -> caller can skip the update
func AddPolicy(obj *unstructured.Unstructured, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) bool {
2019-07-17 15:04:02 -07:00
PolicyObj := newAnnotationForPolicy(pi)
// get annotation
ann := obj.GetAnnotations()
// check if policy already has annotation
2019-07-19 15:10:40 -07:00
cPolicy, ok := ann[BuildKey(pi.Name)]
2019-07-17 15:04:02 -07:00
if !ok {
PolicyByte, err := json.Marshal(PolicyObj)
if err != nil {
glog.Error(err)
return false
2019-07-17 15:04:02 -07:00
}
// insert policy information
2019-07-19 15:10:40 -07:00
ann[BuildKey(pi.Name)] = string(PolicyByte)
2019-07-17 15:04:02 -07:00
// set annotation back to unstr
obj.SetAnnotations(ann)
return true
2019-07-17 15:04:02 -07:00
}
cPolicyObj := Policy{}
err := json.Unmarshal([]byte(cPolicy), &cPolicyObj)
if err != nil {
return false
2019-07-17 15:04:02 -07:00
}
// update policy information inside the annotation
// 1> policy status
// 2> Mutation, Validation, Generation
if cPolicyObj.updatePolicy(PolicyObj, ruleType) {
2019-07-19 20:30:55 -07:00
cPolicyByte, err := json.Marshal(cPolicyObj)
if err != nil {
return false
}
// update policy information
2019-07-19 15:10:40 -07:00
ann[BuildKey(pi.Name)] = string(cPolicyByte)
// set annotation back to unstr
obj.SetAnnotations(ann)
return true
2019-07-17 15:04:02 -07:00
}
return false
2019-07-17 15:04:02 -07:00
}
//RemovePolicy to remove annotations
// return true -> if there was an entry and we deleted it
// return false -> if there is no entry, caller need not update
func RemovePolicy(obj *unstructured.Unstructured, policy string) bool {
2019-07-17 15:04:02 -07:00
// get annotations
ann := obj.GetAnnotations()
2019-07-17 23:13:28 -07:00
if ann == nil {
return false
}
2019-07-19 15:10:40 -07:00
if _, ok := ann[BuildKey(policy)]; !ok {
return false
}
2019-07-19 15:10:40 -07:00
delete(ann, BuildKey(policy))
2019-07-17 15:04:02 -07:00
// set annotation back to unstr
obj.SetAnnotations(ann)
return true
2019-07-17 15:04:02 -07:00
}
//ParseAnnotationsFromObject extracts annotations from the JSON obj
func ParseAnnotationsFromObject(bytes []byte) map[string]string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
meta, ok := objectJSON["metadata"].(map[string]interface{})
if !ok {
glog.Error("unable to parse")
return nil
}
2019-07-17 23:13:28 -07:00
ann, ok, err := unstructured.NestedStringMap(meta, "annotations")
if err != nil || !ok {
return nil
2019-07-17 15:04:02 -07:00
}
2019-07-17 23:13:28 -07:00
return ann
2019-07-17 15:04:02 -07:00
}
//AddPolicyJSONPatch generate JSON Patch to add policy informatino JSON patch
func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) (map[string]string, []byte, error) {
2019-07-25 13:14:55 -04:00
if !pi.ContainsRuleType(ruleType) {
return nil, nil, nil
}
PolicyObj := newAnnotationForPolicy(pi)
2019-07-17 15:04:02 -07:00
if ann == nil {
ann = make(map[string]string, 0)
2019-07-25 13:14:55 -04:00
PolicyByte, err := json.Marshal(PolicyObj)
if err != nil {
return nil, nil, err
}
// create a json patch to add annotation object
ann[BuildKeyString(pi.Name)] = string(PolicyByte)
// create add JSON patch
jsonPatch, err := createAddJSONPatchMap(ann)
return ann, jsonPatch, err
2019-07-17 15:04:02 -07:00
}
2019-07-25 13:14:55 -04:00
// if the annotations map is present then we
2019-07-19 15:10:40 -07:00
cPolicy, ok := ann[BuildKey(pi.Name)]
2019-07-17 15:04:02 -07:00
if !ok {
PolicyByte, err := json.Marshal(PolicyObj)
if err != nil {
return nil, nil, err
2019-07-17 15:04:02 -07:00
}
// insert policy information
2019-07-19 15:10:40 -07:00
ann[BuildKey(pi.Name)] = string(PolicyByte)
2019-07-17 15:04:02 -07:00
// create add JSON patch
2019-07-24 14:25:28 -04:00
jsonPatch, err := createAddJSONPatch(BuildKey(pi.Name), string(PolicyByte))
2019-07-19 15:10:40 -07:00
return ann, jsonPatch, err
2019-07-17 15:04:02 -07:00
}
cPolicyObj := Policy{}
err := json.Unmarshal([]byte(cPolicy), &cPolicyObj)
// update policy information inside the annotation
// 1> policy status
// 2> rule (name, status,changes,type)
2019-07-17 23:13:28 -07:00
update := cPolicyObj.updatePolicy(PolicyObj, ruleType)
if !update {
return nil, nil, err
2019-07-17 15:04:02 -07:00
}
2019-07-19 20:30:55 -07:00
2019-07-17 15:04:02 -07:00
cPolicyByte, err := json.Marshal(cPolicyObj)
if err != nil {
return nil, nil, err
2019-07-17 15:04:02 -07:00
}
// update policy information
2019-07-19 15:10:40 -07:00
ann[BuildKey(pi.Name)] = string(cPolicyByte)
2019-07-17 15:04:02 -07:00
// create update JSON patch
2019-07-24 14:25:28 -04:00
jsonPatch, err := createReplaceJSONPatch(BuildKey(pi.Name), string(cPolicyByte))
return ann, jsonPatch, err
2019-07-17 15:04:02 -07:00
}
//RemovePolicyJSONPatch remove JSON patch
func RemovePolicyJSONPatch(ann map[string]string, policy string) (map[string]string, []byte, error) {
2019-07-17 15:04:02 -07:00
if ann == nil {
return nil, nil, nil
2019-07-17 15:04:02 -07:00
}
2019-07-24 14:25:28 -04:00
jsonPatch, err := createRemoveJSONPatchKey(policy)
return ann, jsonPatch, err
2019-07-17 15:04:02 -07:00
}
type patchMapValue struct {
Op string `json:"op"`
Path string `json:"path"`
Value map[string]string `json:"value"`
}
2019-07-24 14:25:28 -04:00
type patchStringValue struct {
Op string `json:"op"`
Path string `json:"path"`
Value string `json:"value"`
}
func createRemoveJSONPatchMap() ([]byte, error) {
2019-07-17 15:04:02 -07:00
payload := []patchMapValue{{
Op: "remove",
Path: "/metadata/annotations",
}}
return json.Marshal(payload)
}
2019-07-25 13:14:55 -04:00
func createAddJSONPatchMap(ann map[string]string) ([]byte, error) {
payload := []patchMapValue{{
Op: "add",
Path: "/metadata/annotations",
Value: ann,
}}
return json.Marshal(payload)
}
2019-07-24 14:25:28 -04:00
func createAddJSONPatch(key, value string) ([]byte, error) {
payload := []patchStringValue{{
2019-07-17 15:04:02 -07:00
Op: "add",
2019-07-24 14:25:28 -04:00
Path: "/metadata/annotations/" + key,
Value: value,
2019-07-17 15:04:02 -07:00
}}
return json.Marshal(payload)
}
2019-07-24 14:25:28 -04:00
func createReplaceJSONPatch(key, value string) ([]byte, error) {
// if ann == nil {
// ann = make(map[string]string, 0)
// }
payload := []patchStringValue{{
2019-07-17 15:04:02 -07:00
Op: "replace",
2019-07-24 14:25:28 -04:00
Path: "/metadata/annotations/" + key,
Value: value,
2019-07-17 15:04:02 -07:00
}}
return json.Marshal(payload)
}
2019-07-24 14:25:28 -04:00
func createRemoveJSONPatchKey(key string) ([]byte, error) {
payload := []patchStringValue{{
Op: "remove",
Path: "/metadata/annotations/" + key,
}}
return json.Marshal(payload)
}