mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-15 17:51:20 +00:00
annotations v1
This commit is contained in:
parent
a36ed10425
commit
bd9e8585c7
7 changed files with 506 additions and 4 deletions
220
pkg/annotations/annotations.go
Normal file
220
pkg/annotations/annotations.go
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
package annotations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/info"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Policy information for annotations
|
||||||
|
type Policy struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Rules []Rule `json:"rules,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rule information for annotations
|
||||||
|
type Rule struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Changes string `json:"changes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStatus(status bool) string {
|
||||||
|
if status {
|
||||||
|
return "Success"
|
||||||
|
}
|
||||||
|
return "Failure"
|
||||||
|
}
|
||||||
|
func getRules(rules []*info.RuleInfo) []Rule {
|
||||||
|
var annrules []Rule
|
||||||
|
for _, r := range rules {
|
||||||
|
annrule := Rule{Name: r.Name,
|
||||||
|
Status: getStatus(r.IsSuccessful()),
|
||||||
|
Type: r.RuleType.String()}
|
||||||
|
//TODO: add mutation changes in policyInfo and in annotation
|
||||||
|
annrules = append(annrules, annrule)
|
||||||
|
}
|
||||||
|
return annrules
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Policy) updatePolicy(obj *Policy, ruleType info.RuleType) {
|
||||||
|
p.Status = obj.Status
|
||||||
|
p.updatePolicyRules(obj.Rules, ruleType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update rules of a given type
|
||||||
|
func (p *Policy) updatePolicyRules(rules []Rule, ruleType info.RuleType) {
|
||||||
|
var updatedRules []Rule
|
||||||
|
//TODO: check the selecting update add advantage
|
||||||
|
// filter rules for different type
|
||||||
|
for _, r := range rules {
|
||||||
|
if r.Type != ruleType.String() {
|
||||||
|
updatedRules = append(updatedRules, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add rules for current type
|
||||||
|
updatedRules = append(updatedRules, rules...)
|
||||||
|
// set the rule
|
||||||
|
p.Rules = updatedRules
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (p *Policy) containsPolicyRules(rules []Rule, ruleType info.RuleType) {
|
||||||
|
// for _, r := range rules {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
func newAnnotationForPolicy(pi *info.PolicyInfo) *Policy {
|
||||||
|
return &Policy{Status: getStatus(pi.IsSuccessful()),
|
||||||
|
Rules: getRules(pi.Rules)}
|
||||||
|
}
|
||||||
|
|
||||||
|
//AddPolicy will add policy annotation if not present or update if present
|
||||||
|
func AddPolicy(obj *unstructured.Unstructured, pi *info.PolicyInfo, ruleType info.RuleType) error {
|
||||||
|
PolicyObj := newAnnotationForPolicy(pi)
|
||||||
|
// get annotation
|
||||||
|
ann := obj.GetAnnotations()
|
||||||
|
// check if policy already has annotation
|
||||||
|
cPolicy, ok := ann[pi.Name]
|
||||||
|
if !ok {
|
||||||
|
PolicyByte, err := json.Marshal(PolicyObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// insert policy information
|
||||||
|
ann[pi.Name] = string(PolicyByte)
|
||||||
|
// set annotation back to unstr
|
||||||
|
obj.SetAnnotations(ann)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cPolicyObj := Policy{}
|
||||||
|
err := json.Unmarshal([]byte(cPolicy), &cPolicyObj)
|
||||||
|
// update policy information inside the annotation
|
||||||
|
// 1> policy status
|
||||||
|
// 2> rule (name, status,changes,type)
|
||||||
|
cPolicyObj.updatePolicy(PolicyObj, ruleType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cPolicyByte, err := json.Marshal(cPolicyObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// update policy information
|
||||||
|
ann[pi.Name] = string(cPolicyByte)
|
||||||
|
// set annotation back to unstr
|
||||||
|
obj.SetAnnotations(ann)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//RemovePolicy to remove annotations fro
|
||||||
|
func RemovePolicy(obj *unstructured.Unstructured, policy string) {
|
||||||
|
// get annotations
|
||||||
|
ann := obj.GetAnnotations()
|
||||||
|
delete(ann, policy)
|
||||||
|
// set annotation back to unstr
|
||||||
|
obj.SetAnnotations(ann)
|
||||||
|
}
|
||||||
|
|
||||||
|
//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
|
||||||
|
}
|
||||||
|
if annotations, ok := meta["annotations"].(map[string]string); ok {
|
||||||
|
return annotations
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//AddPolicyJSONPatch generate JSON Patch to add policy informatino JSON patch
|
||||||
|
func AddPolicyJSONPatch(ann map[string]string, pi *info.PolicyInfo, ruleType info.RuleType) ([]byte, error) {
|
||||||
|
if ann == nil {
|
||||||
|
ann = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
PolicyObj := newAnnotationForPolicy(pi)
|
||||||
|
cPolicy, ok := ann[pi.Name]
|
||||||
|
if !ok {
|
||||||
|
PolicyByte, err := json.Marshal(PolicyObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// insert policy information
|
||||||
|
ann[pi.Name] = string(PolicyByte)
|
||||||
|
// create add JSON patch
|
||||||
|
return createAddJSONPatch(ann)
|
||||||
|
}
|
||||||
|
cPolicyObj := Policy{}
|
||||||
|
err := json.Unmarshal([]byte(cPolicy), &cPolicyObj)
|
||||||
|
// update policy information inside the annotation
|
||||||
|
// 1> policy status
|
||||||
|
// 2> rule (name, status,changes,type)
|
||||||
|
cPolicyObj.updatePolicy(PolicyObj, ruleType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cPolicyByte, err := json.Marshal(cPolicyObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// update policy information
|
||||||
|
ann[pi.Name] = string(cPolicyByte)
|
||||||
|
// create update JSON patch
|
||||||
|
return createReplaceJSONPatch(ann)
|
||||||
|
}
|
||||||
|
|
||||||
|
//RemovePolicyJSONPatch remove JSON patch
|
||||||
|
func RemovePolicyJSONPatch(ann map[string]string, policy string) ([]byte, error) {
|
||||||
|
if ann == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
delete(ann, policy)
|
||||||
|
if len(ann) == 0 {
|
||||||
|
return createRemoveJSONPatch(ann)
|
||||||
|
}
|
||||||
|
return createReplaceJSONPatch(ann)
|
||||||
|
}
|
||||||
|
|
||||||
|
type patchMapValue struct {
|
||||||
|
Op string `json:"op"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Value map[string]string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRemoveJSONPatch(ann map[string]string) ([]byte, error) {
|
||||||
|
payload := []patchMapValue{{
|
||||||
|
Op: "remove",
|
||||||
|
Path: "/metadata/annotations",
|
||||||
|
}}
|
||||||
|
return json.Marshal(payload)
|
||||||
|
|
||||||
|
}
|
||||||
|
func createAddJSONPatch(ann map[string]string) ([]byte, error) {
|
||||||
|
if ann == nil {
|
||||||
|
ann = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
payload := []patchMapValue{{
|
||||||
|
Op: "add",
|
||||||
|
Path: "/metadata/annotations",
|
||||||
|
Value: ann,
|
||||||
|
}}
|
||||||
|
return json.Marshal(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createReplaceJSONPatch(ann map[string]string) ([]byte, error) {
|
||||||
|
if ann == nil {
|
||||||
|
ann = make(map[string]string, 0)
|
||||||
|
}
|
||||||
|
payload := []patchMapValue{{
|
||||||
|
Op: "replace",
|
||||||
|
Path: "/metadata/annotations",
|
||||||
|
Value: ann,
|
||||||
|
}}
|
||||||
|
return json.Marshal(payload)
|
||||||
|
}
|
25
pkg/annotations/annotations_test.go
Normal file
25
pkg/annotations/annotations_test.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package annotations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/info"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddPatch(t *testing.T) {
|
||||||
|
objRaw := []byte(`{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"nginx-deployment","namespace":"default","creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"containers":[{"name":"nginx","image":"nginx:latest","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"},{"name":"ghost","image":"ghost:latest","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"}},"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxUnavailable":"25%","maxSurge":"25%"}},"revisionHistoryLimit":10,"progressDeadlineSeconds":600},"status":{}}`)
|
||||||
|
piRaw := []byte(`{"Name":"set-image-pull-policy","RKind":"Deployment","RName":"nginx-deployment","RNamespace":"default","ValidationFailureAction":"","Rules":[{"Name":"nginx-deployment","Msgs":["Rule nginx-deployment: Overlay succesfully applied."],"RuleType":0}]}`)
|
||||||
|
ann := ParseAnnotationsFromObject(objRaw)
|
||||||
|
pi := info.PolicyInfo{}
|
||||||
|
err := json.Unmarshal(piRaw, &pi)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
patch, err := AddPolicyJSONPatch(ann, &pi, info.Mutation)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(patch))
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
patchTypes "k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
"k8s.io/client-go/discovery/cached/memory"
|
"k8s.io/client-go/discovery/cached/memory"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
|
@ -40,7 +41,6 @@ func NewClient(config *rest.Config) (*Client, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
kclient, err := kubernetes.NewForConfig(config)
|
kclient, err := kubernetes.NewForConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -110,6 +110,11 @@ func (c *Client) GetResource(kind string, namespace string, name string, subreso
|
||||||
return c.getResourceInterface(kind, namespace).Get(name, meta.GetOptions{}, subresources...)
|
return c.getResourceInterface(kind, namespace).Get(name, meta.GetOptions{}, subresources...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Patch
|
||||||
|
func (c *Client) PatchResource(kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) {
|
||||||
|
return c.getResourceInterface(kind, namespace).Patch(name, patchTypes.JSONPatchType, patch, meta.PatchOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
// ListResource returns the list of resources in unstructured/json format
|
// ListResource returns the list of resources in unstructured/json format
|
||||||
// Access items using []Items
|
// Access items using []Items
|
||||||
func (c *Client) ListResource(kind string, namespace string, lselector *meta.LabelSelector) (*unstructured.UnstructuredList, error) {
|
func (c *Client) ListResource(kind string, namespace string, lselector *meta.LabelSelector) (*unstructured.UnstructuredList, error) {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/nirmata/kyverno/pkg/info"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
@ -110,3 +113,215 @@ func retry(attempts int, sleep time.Duration, fn func() error) error {
|
||||||
type stop struct {
|
type stop struct {
|
||||||
error
|
error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAnnotations(obj *unstructured.Unstructured) map[string]interface{} {
|
||||||
|
var annotationsMaps map[string]interface{}
|
||||||
|
unstr := obj.UnstructuredContent()
|
||||||
|
metadata, ok := unstr["metadata"]
|
||||||
|
if ok {
|
||||||
|
metadataMap, ok := metadata.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
glog.Info("type mismatch")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
annotations, ok := metadataMap["annotations"]
|
||||||
|
if !ok {
|
||||||
|
glog.Info("annotations not present")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
annotationsMaps, ok = annotations.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
glog.Info("type mismatch")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return annotationsMaps
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetAnnotations(obj *unstructured.Unstructured, annotations map[string]interface{}) error {
|
||||||
|
unstr := obj.UnstructuredContent()
|
||||||
|
metadata, ok := unstr["metadata"]
|
||||||
|
if ok {
|
||||||
|
metadataMap, ok := metadata.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("type mismatch")
|
||||||
|
}
|
||||||
|
metadataMap["annotations"] = annotations
|
||||||
|
unstr["metadata"] = metadataMap
|
||||||
|
obj.SetUnstructuredContent(unstr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnnotationPolicies struct {
|
||||||
|
// map[policy_name]
|
||||||
|
Policies map[string]AnnotationPolicy `json:"policies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnnotationPolicy struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Rules []AnnotationRule `json:"rules,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AnnotationRule struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Changes string `json:"changes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStatus(status bool) string {
|
||||||
|
if status {
|
||||||
|
return "Success"
|
||||||
|
}
|
||||||
|
return "Failure"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRules(rules []*info.RuleInfo) []AnnotationRule {
|
||||||
|
var annrules []AnnotationRule
|
||||||
|
for _, r := range rules {
|
||||||
|
annrule := AnnotationRule{Name: r.Name,
|
||||||
|
Status: getStatus(r.IsSuccessful())}
|
||||||
|
//TODO: add mutation changes in policyInfo and in annotation
|
||||||
|
annrules = append(annrules, annrule)
|
||||||
|
}
|
||||||
|
return annrules
|
||||||
|
}
|
||||||
|
|
||||||
|
// input rules can be mutation or validation
|
||||||
|
func (ap AnnotationPolicy) updateRules(rules interface{}, validation bool) (error, interface{}) {
|
||||||
|
ruleList, ok := rules.([]interface{})
|
||||||
|
updated := false
|
||||||
|
if !ok {
|
||||||
|
return errors.New("type mismatch"), false
|
||||||
|
}
|
||||||
|
|
||||||
|
// for mutation rule check if the rules are same
|
||||||
|
// var mode string
|
||||||
|
// if validation {
|
||||||
|
// mode = "Validation"
|
||||||
|
// } else {
|
||||||
|
// mode = "Mutation"
|
||||||
|
// }
|
||||||
|
// // if lengths are differrent then update
|
||||||
|
// if len(ruleList) != len(ap.Rules) {
|
||||||
|
// return nil, ap.updateRules
|
||||||
|
// }
|
||||||
|
// check if there is any update in the rules
|
||||||
|
// order of rules is assumed same while comparison
|
||||||
|
for i, r := range ruleList {
|
||||||
|
rule, ok := r.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("type mismatch"), nil
|
||||||
|
}
|
||||||
|
// Name
|
||||||
|
name, ok := rule["name"].(string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("type mismatch"), nil
|
||||||
|
}
|
||||||
|
if name != ap.Rules[i].Name {
|
||||||
|
updated = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Status
|
||||||
|
status, ok := rule["status"].(string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("type mismatch"), nil
|
||||||
|
}
|
||||||
|
if status != ap.Rules[i].Status {
|
||||||
|
updated = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if updated {
|
||||||
|
return nil, ap.Rules
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAnnotationPolicy(pi *info.PolicyInfo) AnnotationPolicy {
|
||||||
|
status := getStatus(pi.IsSuccessful())
|
||||||
|
rules := getRules(pi.Rules)
|
||||||
|
return AnnotationPolicy{Status: status,
|
||||||
|
Rules: rules}
|
||||||
|
}
|
||||||
|
|
||||||
|
//func GetPolicies(policies interface{}) map[string]
|
||||||
|
func AddPolicy(pi *info.PolicyInfo, ann map[string]interface{}, validation bool) (error, map[string]interface{}) {
|
||||||
|
// Lets build the policy annotation struct from policyInfo
|
||||||
|
annpolicy := newAnnotationPolicy(pi)
|
||||||
|
// Add policy to annotations
|
||||||
|
// If policy does not exist -> Add
|
||||||
|
// If already exists then update the status and rules
|
||||||
|
policies, ok := ann["policies"]
|
||||||
|
if ok {
|
||||||
|
policiesMap, ok := policies.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
glog.Info("type mismatch")
|
||||||
|
return errors.New("type mismatch"), nil
|
||||||
|
}
|
||||||
|
// check if policy record is present
|
||||||
|
policy, ok := policiesMap[pi.Name]
|
||||||
|
if !ok {
|
||||||
|
// not present then we add
|
||||||
|
policiesMap[pi.Name] = annpolicy
|
||||||
|
ann["policies"] = policiesMap
|
||||||
|
return nil, ann
|
||||||
|
}
|
||||||
|
policyMap, ok := policy.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("type mismatch"), nil
|
||||||
|
}
|
||||||
|
// We just update the annotations
|
||||||
|
// status
|
||||||
|
status := policyMap["status"]
|
||||||
|
statusStr, ok := status.(string)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("type mismatch"), nil
|
||||||
|
}
|
||||||
|
if statusStr != annpolicy.Status {
|
||||||
|
policyMap["status"] = annpolicy.Status
|
||||||
|
}
|
||||||
|
// check rules
|
||||||
|
rules, ok := policyMap["rules"]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("no rules"), nil
|
||||||
|
}
|
||||||
|
err, newRules := annpolicy.updateRules(rules, validation)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if newRules == nil {
|
||||||
|
//nothing to update
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// update the new rule
|
||||||
|
policyMap["rules"] = newRules
|
||||||
|
// update policies map
|
||||||
|
policiesMap[pi.Name] = policyMap
|
||||||
|
ann["policies"] = policiesMap
|
||||||
|
return nil, ann
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePolicy
|
||||||
|
func RemovePolicy(pi *info.PolicyInfo, ann map[string]interface{}) (error, map[string]interface{}) {
|
||||||
|
policies, ok := ann["policies"]
|
||||||
|
if ok {
|
||||||
|
policiesMap, ok := policies.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
glog.Info("type mismatch")
|
||||||
|
return errors.New("type mismatch"), nil
|
||||||
|
}
|
||||||
|
// check if policy record is present
|
||||||
|
_, ok = policiesMap[pi.Name]
|
||||||
|
if ok {
|
||||||
|
// delete the pair
|
||||||
|
delete(policiesMap, pi.Name)
|
||||||
|
ann["policies"] = policiesMap
|
||||||
|
return nil, ann
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
@ -103,7 +103,8 @@ func applyPolicyOnRaw(policy *kubepolicy.Policy, rawResource []byte, gvk *metav1
|
||||||
policyInfo := info.NewPolicyInfo(policy.Name,
|
policyInfo := info.NewPolicyInfo(policy.Name,
|
||||||
gvk.Kind,
|
gvk.Kind,
|
||||||
rname,
|
rname,
|
||||||
rns)
|
rns,
|
||||||
|
policy.Spec.ValidationFailureAction)
|
||||||
|
|
||||||
// Process Mutation
|
// Process Mutation
|
||||||
patches, ruleInfos := engine.Mutate(*policy, rawResource, *gvk)
|
patches, ruleInfos := engine.Mutate(*policy, rawResource, *gvk)
|
||||||
|
|
|
@ -80,7 +80,7 @@ func scanDir(dir string) ([]string, error) {
|
||||||
|
|
||||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("prevent panic by handling failure accessing a path %q: %v\n", dir, err)
|
return fmt.Errorf("prevent panic by handling failure accessing a path %q: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, path)
|
res = append(res, path)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
package webhooks
|
package webhooks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"github.com/nirmata/kyverno/pkg/annotations"
|
||||||
engine "github.com/nirmata/kyverno/pkg/engine"
|
engine "github.com/nirmata/kyverno/pkg/engine"
|
||||||
"github.com/nirmata/kyverno/pkg/info"
|
"github.com/nirmata/kyverno/pkg/info"
|
||||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
|
@ -23,6 +28,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
||||||
}
|
}
|
||||||
|
|
||||||
var allPatches [][]byte
|
var allPatches [][]byte
|
||||||
|
var annPatches []byte
|
||||||
policyInfos := []*info.PolicyInfo{}
|
policyInfos := []*info.PolicyInfo{}
|
||||||
for _, policy := range policies {
|
for _, policy := range policies {
|
||||||
// check if policy has a rule for the admission request kind
|
// check if policy has a rule for the admission request kind
|
||||||
|
@ -65,6 +71,18 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
policyInfos = append(policyInfos, policyInfo)
|
policyInfos = append(policyInfos, policyInfo)
|
||||||
|
annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Mutation)
|
||||||
|
if annPatch != nil {
|
||||||
|
if annPatches == nil {
|
||||||
|
annPatches = annPatch
|
||||||
|
} else {
|
||||||
|
annPatches, err = jsonpatch.MergePatch(annPatches, annPatch)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Mergining docs")
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allPatches) > 0 {
|
if len(allPatches) > 0 {
|
||||||
|
@ -74,10 +92,17 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
||||||
|
|
||||||
ok, msg := isAdmSuccesful(policyInfos)
|
ok, msg := isAdmSuccesful(policyInfos)
|
||||||
if ok {
|
if ok {
|
||||||
|
patches := engine.JoinPatches(allPatches)
|
||||||
|
if len(annPatches) > 0 {
|
||||||
|
patches, err = jsonpatch.MergePatch(patches, annPatches)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
patchType := v1beta1.PatchTypeJSONPatch
|
patchType := v1beta1.PatchTypeJSONPatch
|
||||||
return &v1beta1.AdmissionResponse{
|
return &v1beta1.AdmissionResponse{
|
||||||
Allowed: true,
|
Allowed: true,
|
||||||
Patch: engine.JoinPatches(allPatches),
|
Patch: patches,
|
||||||
PatchType: &patchType,
|
PatchType: &patchType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,3 +113,14 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addAnnotationsToResource(rawResource []byte, pi *info.PolicyInfo, ruleType info.RuleType) []byte {
|
||||||
|
// get annotations
|
||||||
|
ann := annotations.ParseAnnotationsFromObject(rawResource)
|
||||||
|
patch, err := annotations.AddPolicyJSONPatch(ann, pi, ruleType)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return patch
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue