mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-08 18:15:48 +00:00
Merge branch 'master' into 196_anchor_selection_on_peer
This commit is contained in:
commit
7d2abc5df3
16 changed files with 217 additions and 73 deletions
|
@ -160,7 +160,7 @@ Here are some the major features we plan on completing before a 1.0 release:
|
|||
## Getting help
|
||||
|
||||
* For feature requests and bugs, file an [issue](https://github.com/nirmata/kyverno/issues).
|
||||
* For discussions or questions, join the [mailing list](https://groups.google.com/forum/#!forum/kyverno)
|
||||
* For discussions or questions, join our [Kubernetes Slack channel #kyverno](https://app.slack.com/client/T09NY5SBT/CLGR9BJU9) or the [mailing list](https://groups.google.com/forum/#!forum/kyverno)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ spec:
|
|||
validationFailureAction:
|
||||
type: string
|
||||
enum:
|
||||
- block
|
||||
- report
|
||||
- enforce # blocks the resorce api-reques if a rule fails. Default behavior
|
||||
- audit # allows resource creationg and reports the failed validation rules as violations
|
||||
rules:
|
||||
type: array
|
||||
items:
|
||||
|
|
2
main.go
2
main.go
|
@ -51,7 +51,7 @@ func main() {
|
|||
eventController,
|
||||
annotationsController)
|
||||
|
||||
genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces())
|
||||
genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces(), annotationsController)
|
||||
tlsPair, err := initTLSPemPair(clientConfig, client)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)
|
||||
|
|
|
@ -23,7 +23,7 @@ type Policy struct {
|
|||
type Rule struct {
|
||||
Status string `json:"status"`
|
||||
Changes string `json:"changes,omitempty"` // TODO for mutation changes
|
||||
Error string `json:"error,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Policy) getOverAllStatus() string {
|
||||
|
@ -61,7 +61,7 @@ func getRules(rules []*pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule
|
|||
|
||||
rule := Rule{Status: getStatus(r.IsSuccessful())}
|
||||
if !r.IsSuccessful() {
|
||||
rule.Error = r.GetErrorString()
|
||||
rule.Message = r.GetErrorString()
|
||||
}
|
||||
annrules[r.Name] = rule
|
||||
}
|
||||
|
|
|
@ -12,5 +12,5 @@ func getStatus(status bool) string {
|
|||
}
|
||||
|
||||
func BuildKey(policyName string) string {
|
||||
return "policies.kyverno.io." + policyName
|
||||
return "policies.kyverno.io/" + policyName
|
||||
}
|
||||
|
|
|
@ -225,7 +225,14 @@ func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
|
|||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
if mpatch == nil && vpatch == nil {
|
||||
|
||||
// Generation rules
|
||||
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Validation)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
if mpatch == nil && vpatch == nil && gpatch == nil {
|
||||
//nothing to patch
|
||||
continue
|
||||
}
|
||||
|
@ -237,11 +244,21 @@ func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
|
|||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if mpatch == nil {
|
||||
patch = vpatch
|
||||
} else {
|
||||
patch = mpatch
|
||||
}
|
||||
|
||||
// generation
|
||||
if gpatch != nil {
|
||||
patch, err = jsonpatch.MergePatch(patch, gpatch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
// add the anotation to the resource
|
||||
_, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch)
|
||||
if err != nil {
|
||||
|
|
|
@ -10,22 +10,14 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// As the logic to process the policies in stateless, we do not need to define struct and implement behaviors for it
|
||||
// Instead we expose them as standalone functions passing the required atrributes
|
||||
// The each function returns the changes that need to be applied on the resource
|
||||
// the caller is responsible to apply the changes to the resource
|
||||
// ProcessExisting checks for mutation and validation violations of existing resources
|
||||
func ProcessExisting(client *client.Client, policy *types.Policy) []*info.PolicyInfo {
|
||||
glog.Infof("Applying policy %s on existing resources", policy.Name)
|
||||
// key uid
|
||||
resourceMap := map[string]*resourceInfo{}
|
||||
resourceMap := map[string]resourceInfo{}
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
for _, k := range rule.Kinds {
|
||||
if k == "Namespace" {
|
||||
// REWORK: will be handeled by namespace controller
|
||||
continue
|
||||
}
|
||||
// kind -> resource
|
||||
gvr := client.DiscoveryClient.GetGVRFromKind(k)
|
||||
// label selectors
|
||||
|
@ -34,6 +26,10 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
|
|||
if rule.ResourceDescription.Namespace != nil {
|
||||
namespace = *rule.ResourceDescription.Namespace
|
||||
}
|
||||
if k == "Namespace" {
|
||||
namespace = ""
|
||||
}
|
||||
|
||||
list, err := client.ListResource(k, namespace, rule.ResourceDescription.Selector)
|
||||
if err != nil {
|
||||
glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.Selector.String())
|
||||
|
@ -49,7 +45,7 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
|
|||
continue
|
||||
}
|
||||
}
|
||||
ri := &resourceInfo{resource: &res, gvk: &metav1.GroupVersionKind{Group: gvk.Group,
|
||||
ri := resourceInfo{resource: res, gvk: &metav1.GroupVersionKind{Group: gvk.Group,
|
||||
Version: gvk.Version,
|
||||
Kind: gvk.Kind}}
|
||||
|
||||
|
@ -74,7 +70,7 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
|
|||
return policyInfos
|
||||
}
|
||||
|
||||
func applyPolicy(client *client.Client, policy *types.Policy, res *resourceInfo) (*info.PolicyInfo, error) {
|
||||
func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) (*info.PolicyInfo, error) {
|
||||
policyInfo := info.NewPolicyInfo(policy.Name, res.gvk.Kind, res.resource.GetName(), res.resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
rawResource, err := res.resource.MarshalJSON()
|
||||
|
@ -93,7 +89,12 @@ func applyPolicy(client *client.Client, policy *types.Policy, res *resourceInfo)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Generate rule managed by generation controller
|
||||
if res.gvk.Kind == "Namespace" {
|
||||
|
||||
// Generation
|
||||
gruleInfos := GenerateNew(client, policy, res.resource)
|
||||
policyInfo.AddRuleInfos(gruleInfos)
|
||||
}
|
||||
|
||||
return policyInfo, nil
|
||||
}
|
||||
|
@ -104,6 +105,16 @@ func mutation(p *types.Policy, rawResource []byte, gvk *metav1.GroupVersionKind)
|
|||
// no rules were processed
|
||||
return nil, nil
|
||||
}
|
||||
// if there are any errors return
|
||||
for _, r := range ruleInfos {
|
||||
if !r.IsSuccessful() {
|
||||
return ruleInfos, nil
|
||||
}
|
||||
}
|
||||
// if there are no patches // for overlay
|
||||
if len(patches) == 0 {
|
||||
return ruleInfos, nil
|
||||
}
|
||||
// option 2: (original Resource + patch) compare with (original resource)
|
||||
mergePatches := JoinPatches(patches)
|
||||
// merge the patches
|
||||
|
|
|
@ -9,13 +9,12 @@ import (
|
|||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
//GenerateNew apply generation rules on a resource
|
||||
func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns *corev1.Namespace) []*info.RuleInfo {
|
||||
func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns unstructured.Unstructured) []*info.RuleInfo {
|
||||
ris := []*info.RuleInfo{}
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if rule.Generation == nil {
|
||||
|
@ -35,14 +34,14 @@ func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns *corev1.Name
|
|||
return ris
|
||||
}
|
||||
|
||||
func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1alpha1.Generation) error {
|
||||
func applyRuleGeneratorNew(client *client.Client, ns unstructured.Unstructured, gen *v1alpha1.Generation) error {
|
||||
var err error
|
||||
resource := &unstructured.Unstructured{}
|
||||
var rdata map[string]interface{}
|
||||
|
||||
if gen.Data != nil {
|
||||
// 1> Check if resource exists
|
||||
obj, err := client.GetResource(gen.Kind, ns.Name, gen.Name)
|
||||
obj, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
||||
if err == nil {
|
||||
// 2> If already exsists, then verify the content is contained
|
||||
// found the resource
|
||||
|
@ -64,7 +63,7 @@ func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1a
|
|||
}
|
||||
if gen.Clone != nil {
|
||||
// 1> Check if resource exists
|
||||
_, err := client.GetResource(gen.Kind, ns.Name, gen.Name)
|
||||
_, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -77,11 +76,11 @@ func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1a
|
|||
}
|
||||
resource.SetUnstructuredContent(rdata)
|
||||
resource.SetName(gen.Name)
|
||||
resource.SetNamespace(ns.Name)
|
||||
resource.SetNamespace(ns.GetName())
|
||||
// Reset resource version
|
||||
resource.SetResourceVersion("")
|
||||
|
||||
_, err = client.CreateResource(gen.Kind, ns.Name, resource, false)
|
||||
_, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func compareJsonAsMap(t *testing.T, expected, actual []byte) {
|
||||
func compareJSONAsMap(t *testing.T, expected, actual []byte) {
|
||||
var expectedMap, actualMap map[string]interface{}
|
||||
assert.NilError(t, json.Unmarshal(expected, &expectedMap))
|
||||
assert.NilError(t, json.Unmarshal(actual, &actualMap))
|
||||
|
@ -106,7 +106,7 @@ func TestApplyOverlay_NestedListWithAnchor(t *testing.T) {
|
|||
]
|
||||
}`)
|
||||
|
||||
compareJsonAsMap(t, expectedResult, patched)
|
||||
compareJSONAsMap(t, expectedResult, patched)
|
||||
}
|
||||
|
||||
func TestApplyOverlay_InsertIntoArray(t *testing.T) {
|
||||
|
@ -223,7 +223,7 @@ func TestApplyOverlay_InsertIntoArray(t *testing.T) {
|
|||
]
|
||||
}`)
|
||||
|
||||
compareJsonAsMap(t, expectedResult, patched)
|
||||
compareJSONAsMap(t, expectedResult, patched)
|
||||
}
|
||||
|
||||
func TestApplyOverlay_TestInsertToArray(t *testing.T) {
|
||||
|
@ -428,7 +428,7 @@ func TestApplyOverlay_ImagePullPolicy(t *testing.T) {
|
|||
}
|
||||
}`)
|
||||
|
||||
compareJsonAsMap(t, expectedResult, doc)
|
||||
compareJSONAsMap(t, expectedResult, doc)
|
||||
}
|
||||
|
||||
func TestApplyOverlay_AddingAnchor(t *testing.T) {
|
||||
|
@ -471,7 +471,7 @@ func TestApplyOverlay_AddingAnchor(t *testing.T) {
|
|||
}
|
||||
}`)
|
||||
|
||||
compareJsonAsMap(t, expectedResult, doc)
|
||||
compareJSONAsMap(t, expectedResult, doc)
|
||||
}
|
||||
|
||||
func TestApplyOverlay_AddingAnchorInsideListElement(t *testing.T) {
|
||||
|
@ -591,5 +591,5 @@ func TestApplyOverlay_AddingAnchorInsideListElement(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}`)
|
||||
compareJsonAsMap(t, expectedResult, doc)
|
||||
compareJSONAsMap(t, expectedResult, doc)
|
||||
}
|
||||
|
|
|
@ -253,6 +253,6 @@ func convertToFloat(value interface{}) (float64, error) {
|
|||
}
|
||||
|
||||
type resourceInfo struct {
|
||||
resource *unstructured.Unstructured
|
||||
resource unstructured.Unstructured
|
||||
gvk *metav1.GroupVersionKind
|
||||
}
|
||||
|
|
|
@ -197,6 +197,7 @@ func valFromReferenceToString(value interface{}, operator string) (string, error
|
|||
}
|
||||
}
|
||||
|
||||
//FormAbsolutePath returns absolute path
|
||||
func FormAbsolutePath(referencePath, absolutePath string) string {
|
||||
if filepath.IsAbs(referencePath) {
|
||||
return referencePath
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
policyLister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
|
@ -22,13 +23,14 @@ import (
|
|||
|
||||
//Controller watches the 'Namespace' resource creation/update and applied the generation rules on them
|
||||
type Controller struct {
|
||||
client *client.Client
|
||||
namespaceLister v1CoreLister.NamespaceLister
|
||||
namespaceSynced cache.InformerSynced
|
||||
policyLister policyLister.PolicyLister
|
||||
eventController event.Generator
|
||||
violationBuilder violation.Generator
|
||||
workqueue workqueue.RateLimitingInterface
|
||||
client *client.Client
|
||||
namespaceLister v1CoreLister.NamespaceLister
|
||||
namespaceSynced cache.InformerSynced
|
||||
policyLister policyLister.PolicyLister
|
||||
eventController event.Generator
|
||||
violationBuilder violation.Generator
|
||||
annotationsController annotations.Controller
|
||||
workqueue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
//NewGenController returns a new Controller to manage generation rules
|
||||
|
@ -36,17 +38,19 @@ func NewGenController(client *client.Client,
|
|||
eventController event.Generator,
|
||||
policyInformer policySharedInformer.PolicyInformer,
|
||||
violationBuilder violation.Generator,
|
||||
namespaceInformer v1Informer.NamespaceInformer) *Controller {
|
||||
namespaceInformer v1Informer.NamespaceInformer,
|
||||
annotationsController annotations.Controller) *Controller {
|
||||
|
||||
// create the controller
|
||||
controller := &Controller{
|
||||
client: client,
|
||||
namespaceLister: namespaceInformer.Lister(),
|
||||
namespaceSynced: namespaceInformer.Informer().HasSynced,
|
||||
policyLister: policyInformer.GetLister(),
|
||||
eventController: eventController,
|
||||
violationBuilder: violationBuilder,
|
||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), wqNamespace),
|
||||
client: client,
|
||||
namespaceLister: namespaceInformer.Lister(),
|
||||
namespaceSynced: namespaceInformer.Informer().HasSynced,
|
||||
policyLister: policyInformer.GetLister(),
|
||||
eventController: eventController,
|
||||
violationBuilder: violationBuilder,
|
||||
annotationsController: annotationsController,
|
||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), wqNamespace),
|
||||
}
|
||||
namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: controller.createNamespaceHandler,
|
||||
|
@ -77,7 +81,7 @@ func (c *Controller) enqueueNamespace(obj interface{}) {
|
|||
func (c *Controller) Run(stopCh <-chan struct{}) error {
|
||||
|
||||
if ok := cache.WaitForCacheSync(stopCh, c.namespaceSynced); !ok {
|
||||
return fmt.Errorf("faield to wait for caches to sync")
|
||||
return fmt.Errorf("failed to wait for caches to sync")
|
||||
}
|
||||
|
||||
for i := 0; i < workerCount; i++ {
|
||||
|
|
|
@ -4,7 +4,10 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
event "github.com/nirmata/kyverno/pkg/event"
|
||||
|
@ -12,6 +15,7 @@ import (
|
|||
violation "github.com/nirmata/kyverno/pkg/violation"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func (c *Controller) processNamespace(ns *corev1.Namespace) error {
|
||||
|
@ -62,9 +66,19 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
|
|||
"",
|
||||
p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW
|
||||
|
||||
ruleInfos := engine.GenerateNew(c.client, p, ns)
|
||||
// convert to unstructured
|
||||
unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
unstObj := unstructured.Unstructured{Object: unstrMap}
|
||||
ruleInfos := engine.GenerateNew(c.client, p, unstObj)
|
||||
policyInfo.AddRuleInfos(ruleInfos)
|
||||
|
||||
// generate annotations
|
||||
c.createAnnotations(policyInfo)
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name)
|
||||
for _, r := range ruleInfos {
|
||||
|
@ -78,9 +92,11 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
|
|||
|
||||
if onViolation {
|
||||
glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name)
|
||||
// Policy Violation
|
||||
v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.GetFailedRules())
|
||||
c.violationBuilder.Add(v)
|
||||
} else {
|
||||
// Event
|
||||
eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked,
|
||||
event.FPolicyApplyBlockCreate, policyInfo.RName, policyInfo.GetRuleNames(false))
|
||||
|
||||
|
@ -100,3 +116,31 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
|
|||
|
||||
c.eventController.Add(eventInfo)
|
||||
}
|
||||
|
||||
func (c *Controller) createAnnotations(pi *info.PolicyInfo) {
|
||||
//get resource
|
||||
obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
// add annotation for policy application
|
||||
ann := obj.GetAnnotations()
|
||||
// Generation rules
|
||||
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Mutation)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
if gpatch == nil {
|
||||
// nothing to patch
|
||||
return
|
||||
}
|
||||
|
||||
// add the anotation to the resource
|
||||
_, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,12 +67,11 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
|||
glog.Warningf("%s: %s\n", r.Name, r.Msgs)
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
// // CleanUp Violations if exists
|
||||
// err := ws.violationBuilder.RemoveInactiveViolation(policy.Name, request.Kind.Kind, rns, rname, info.Validation)
|
||||
// if err != nil {
|
||||
// glog.Info(err)
|
||||
// }
|
||||
// CleanUp Violations if exists
|
||||
err := ws.violationBuilder.RemoveInactiveViolation(policy.Name, request.Kind.Kind, rns, rname, info.Mutation)
|
||||
if err != nil {
|
||||
glog.Info(err)
|
||||
}
|
||||
allPatches = append(allPatches, policyPatches...)
|
||||
glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -54,6 +55,7 @@ func parseKinds(list []string) []string {
|
|||
return kinds
|
||||
}
|
||||
|
||||
//ArrayFlags to store filterkinds
|
||||
type ArrayFlags []string
|
||||
|
||||
func (i *ArrayFlags) String() string {
|
||||
|
@ -64,6 +66,7 @@ func (i *ArrayFlags) String() string {
|
|||
return sb.String()
|
||||
}
|
||||
|
||||
//Set setter for array flags
|
||||
func (i *ArrayFlags) Set(value string) error {
|
||||
*i = append(*i, value)
|
||||
return nil
|
||||
|
@ -89,8 +92,8 @@ func getApplicableKindsForPolicy(p *v1alpha1.Policy) []string {
|
|||
|
||||
// Policy Reporting Modes
|
||||
const (
|
||||
BlockChanges = "block"
|
||||
ReportViolation = "report"
|
||||
BlockChanges = "enforce"
|
||||
ReportViolation = "audit"
|
||||
)
|
||||
|
||||
// returns true -> if there is even one policy that blocks resource requst
|
||||
|
@ -105,31 +108,97 @@ func toBlock(pis []*info.PolicyInfo) bool {
|
|||
}
|
||||
|
||||
func checkIfOnlyAnnotationsUpdate(request *v1beta1.AdmissionRequest) bool {
|
||||
var err error
|
||||
// process only if its for existing resources
|
||||
if request.Operation != v1beta1.Update {
|
||||
return false
|
||||
}
|
||||
// updated resoruce
|
||||
obj := request.Object
|
||||
objUnstr := unstructured.Unstructured{}
|
||||
err := objUnstr.UnmarshalJSON(obj.Raw)
|
||||
|
||||
// approach : we only compare if the addition contains annotations the are added with prefix "policies.kyverno.io"
|
||||
// get annotations for the old resource
|
||||
oldObj := request.OldObject
|
||||
oldObjUnstr := unstructured.Unstructured{}
|
||||
// need to set kind as some request dont contain kind meta-data raw resource but in the api request
|
||||
oldObj.Raw = setKindForObject(oldObj.Raw, request.Kind.Kind)
|
||||
err = oldObjUnstr.UnmarshalJSON(oldObj.Raw)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return false
|
||||
}
|
||||
objUnstr.SetAnnotations(nil)
|
||||
objUnstr.SetGeneration(0)
|
||||
oldobj := request.OldObject
|
||||
oldobjUnstr := unstructured.Unstructured{}
|
||||
err = oldobjUnstr.UnmarshalJSON(oldobj.Raw)
|
||||
oldAnn := oldObjUnstr.GetAnnotations()
|
||||
|
||||
// get annotations for the new resource
|
||||
newObj := request.Object
|
||||
newObjUnstr := unstructured.Unstructured{}
|
||||
// need to set kind as some request dont contain kind meta-data raw resource but in the api request
|
||||
newObj.Raw = setKindForObject(newObj.Raw, request.Kind.Kind)
|
||||
err = newObjUnstr.UnmarshalJSON(newObj.Raw)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return false
|
||||
}
|
||||
oldobjUnstr.SetAnnotations(nil)
|
||||
oldobjUnstr.SetGeneration(0)
|
||||
if reflect.DeepEqual(objUnstr, oldobjUnstr) {
|
||||
newAnn := newObjUnstr.GetAnnotations()
|
||||
policiesAppliedNew := 0
|
||||
newAnnPolicy := map[string]string{}
|
||||
// check if annotations changed
|
||||
// assuming that we only add an annotation with the given prefix
|
||||
for k, v := range newAnn {
|
||||
// check prefix
|
||||
policyName := strings.Split(k, "/")
|
||||
if len(policyName) == 1 {
|
||||
continue
|
||||
}
|
||||
if policyName[0] == "policies.kyverno.io" {
|
||||
newAnnPolicy[policyName[1]] = v
|
||||
policiesAppliedNew++
|
||||
}
|
||||
}
|
||||
|
||||
oldAnnPolicy := map[string]string{}
|
||||
policiesAppliedOld := 0
|
||||
// check if annotations changed
|
||||
// assuming that we only add an annotation with the given prefix
|
||||
for k, v := range oldAnn {
|
||||
// check prefix
|
||||
policyName := strings.Split(k, "/")
|
||||
if len(policyName) == 1 {
|
||||
continue
|
||||
}
|
||||
if policyName[0] == "policies.kyverno.io" {
|
||||
oldAnnPolicy[policyName[1]] = v
|
||||
policiesAppliedOld++
|
||||
}
|
||||
}
|
||||
diffCount := policiesAppliedNew - policiesAppliedOld
|
||||
switch diffCount {
|
||||
case 1: // policy applied
|
||||
return true
|
||||
case -1: // policy removed
|
||||
return true
|
||||
case 0: // no new policy added or remove
|
||||
// need to check if the policy was updated
|
||||
if !reflect.DeepEqual(newAnnPolicy, oldAnnPolicy) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
//TODO: Hack if there an update on self link then we ignore
|
||||
if oldObjUnstr.GetSelfLink() != newObjUnstr.GetSelfLink() {
|
||||
return true
|
||||
}
|
||||
|
||||
// then there is some other change and we should process it
|
||||
return false
|
||||
}
|
||||
|
||||
func setKindForObject(bytes []byte, kind string) []byte {
|
||||
var objectJSON map[string]interface{}
|
||||
json.Unmarshal(bytes, &objectJSON)
|
||||
objectJSON["kind"] = kind
|
||||
data, err := json.Marshal(objectJSON)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
glog.Error("unable to marshall, not setting the kind")
|
||||
return bytes
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
|
||||
if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 {
|
||||
eventsInfo, violations := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Validation)
|
||||
// If the validationFailureAction flag is set "report",
|
||||
// If the validationFailureAction flag is set "audit",
|
||||
// then we dont block the request and report the violations
|
||||
ws.violationBuilder.Add(violations...)
|
||||
ws.eventController.Add(eventsInfo...)
|
||||
|
@ -113,7 +113,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
}
|
||||
// If Validation fails then reject the request
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
// violations are created if "report" flag is set
|
||||
// violations are created if "audit" flag is set
|
||||
// and if there are any then we dont bock the resource creation
|
||||
// Even if one the policy being applied
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue