mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-06 07:57:07 +00:00
Merge pull request #323 from nirmata/refactor_engineResponse
Refactor engine response
This commit is contained in:
commit
be47523b46
43 changed files with 2589 additions and 1383 deletions
|
@ -17,4 +17,4 @@ spec:
|
|||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
# imagePullPolicy: IfNotPresent
|
||||
imagePullPolicy: Always
|
||||
|
|
|
@ -13,7 +13,7 @@ spec:
|
|||
- -c
|
||||
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
|
||||
readinessProbe:
|
||||
# successThreshold: 3
|
||||
successThreshold: 3
|
||||
exec:
|
||||
command:
|
||||
- cat
|
||||
|
@ -26,6 +26,7 @@ spec:
|
|||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
httpHeaders:
|
||||
- name: Custom-Header
|
||||
value: Awesome
|
||||
|
|
|
@ -2,7 +2,6 @@ package engine
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
|
@ -10,7 +9,6 @@ import (
|
|||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -18,43 +16,49 @@ import (
|
|||
)
|
||||
|
||||
//Generate apply generation rules on a resource
|
||||
func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) (response EngineResponse) {
|
||||
func Generate(client *client.Client, policy kyverno.Policy, ns unstructured.Unstructured) (response EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
// policy information
|
||||
func() {
|
||||
// set policy information
|
||||
response.PolicyResponse.Policy = policy.Name
|
||||
// resource details
|
||||
response.PolicyResponse.Resource.Name = ns.GetName()
|
||||
response.PolicyResponse.Resource.Kind = ns.GetKind()
|
||||
response.PolicyResponse.Resource.APIVersion = ns.GetAPIVersion()
|
||||
}()
|
||||
glog.V(4).Infof("started applying generation rules of policy %q (%v)", policy.Name, startTime)
|
||||
defer func() {
|
||||
response.ExecutionTime = time.Since(startTime)
|
||||
glog.V(4).Infof("Finished applying generation rules policy %q (%v)", policy.Name, response.ExecutionTime)
|
||||
glog.V(4).Infof("Generation Rules appplied count %q for policy %q", response.RulesAppliedCount, policy.Name)
|
||||
response.PolicyResponse.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying generation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
|
||||
glog.V(4).Infof("Generation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
|
||||
}()
|
||||
incrementAppliedRuleCount := func() {
|
||||
// rules applied succesfully count
|
||||
response.RulesAppliedCount++
|
||||
response.PolicyResponse.RulesAppliedCount++
|
||||
}
|
||||
|
||||
ris := []info.RuleInfo{}
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if rule.Generation == (kyverno.Generation{}) {
|
||||
continue
|
||||
}
|
||||
glog.V(4).Infof("applying policy %s generate rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName())
|
||||
ri := info.NewRuleInfo(rule.Name, info.Generation)
|
||||
err := applyRuleGenerator(client, ns, rule.Generation, policy.GetCreationTimestamp())
|
||||
if err != nil {
|
||||
ri.Fail()
|
||||
ri.Addf("Failed to apply rule %s generator, err %v.", rule.Name, err)
|
||||
glog.Infof("failed to apply policy %s rule %s on resource %s/%s/%s: %v", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName(), err)
|
||||
} else {
|
||||
ri.Addf("Generation succesfully for rule %s", rule.Name)
|
||||
glog.Infof("succesfully applied policy %s rule %s on resource %s/%s/%s", policy.Name, rule.Name, ns.GetKind(), ns.GetNamespace(), ns.GetName())
|
||||
}
|
||||
ris = append(ris, ri)
|
||||
ruleResponse := applyRuleGenerator(client, ns, rule, policy.GetCreationTimestamp())
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
response.RuleInfos = ris
|
||||
return response
|
||||
}
|
||||
|
||||
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen kyverno.Generation, policyCreationTime metav1.Time) error {
|
||||
func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, rule kyverno.Rule, policyCreationTime metav1.Time) (response RuleResponse) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started applying generation rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Generation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying generation rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
var err error
|
||||
resource := &unstructured.Unstructured{}
|
||||
var rdata map[string]interface{}
|
||||
|
@ -63,66 +67,84 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, gen
|
|||
nsCreationTime := ns.GetCreationTimestamp()
|
||||
return nsCreationTime.Before(&policyCreationTime)
|
||||
}()
|
||||
if gen.Data != nil {
|
||||
if rule.Generation.Data != nil {
|
||||
glog.V(4).Info("generate rule: creates new resource")
|
||||
// 1> Check if resource exists
|
||||
obj, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
||||
obj, err := client.GetResource(rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
if err == nil {
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present. checking if it contains the required configuration", gen.Kind, ns.GetName(), gen.Name)
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present. checking if it contains the required configuration", rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
// 2> If already exsists, then verify the content is contained
|
||||
// found the resource
|
||||
// check if the rule is create, if yes, then verify if the specified configuration is present in the resource
|
||||
ok, err := checkResource(gen.Data, obj)
|
||||
ok, err := checkResource(rule.Generation.Data, obj)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("generate rule:: unable to check if configuration %v, is present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name)
|
||||
return err
|
||||
glog.V(4).Infof("generate rule:: unable to check if configuration %v, is present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("unable to check if configuration %v, is present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
if !ok {
|
||||
glog.V(4).Infof("generate rule:: configuration %v not present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name)
|
||||
return errors.New("rule configuration not present in resource")
|
||||
glog.V(4).Infof("generate rule:: configuration %v not present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("configuration %v not present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
glog.V(4).Infof("generate rule: required configuration %v is present in resource %s/%s/%s", gen.Data, gen.Kind, ns.GetName(), gen.Name)
|
||||
return nil
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("required configuration %v is present in resource %s/%s/%s", rule.Generation.Data, rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
rdata, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&gen.Data)
|
||||
rdata, err = runtime.DefaultUnstructuredConverter.ToUnstructured(&rule.Generation.Data)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to parse the specified resource spec %v: %v", rule.Generation.Data, err)
|
||||
return response
|
||||
}
|
||||
}
|
||||
if gen.Clone != (kyverno.CloneFrom{}) {
|
||||
if rule.Generation.Clone != (kyverno.CloneFrom{}) {
|
||||
glog.V(4).Info("generate rule: clone resource")
|
||||
// 1> Check if resource exists
|
||||
_, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
|
||||
_, err := client.GetResource(rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
if err == nil {
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present", gen.Kind, ns.GetName(), gen.Name)
|
||||
return nil
|
||||
glog.V(4).Infof("generate rule: resource %s/%s/%s already present", rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("resource %s/%s/%s already present", rule.Generation.Kind, ns.GetName(), rule.Generation.Name)
|
||||
return response
|
||||
}
|
||||
// 2> If clone already exists return
|
||||
resource, err = client.GetResource(gen.Kind, gen.Clone.Namespace, gen.Clone.Name)
|
||||
resource, err = client.GetResource(rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s not present: %v", gen.Kind, gen.Clone.Namespace, gen.Clone.Name, err)
|
||||
return err
|
||||
glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s not present: %v", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name, err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("clone reference resource %s/%s/%s not present: %v", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name, err)
|
||||
return response
|
||||
}
|
||||
glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", gen.Kind, gen.Clone.Namespace, gen.Clone.Name)
|
||||
glog.V(4).Infof("generate rule: clone reference resource %s/%s/%s present", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
|
||||
rdata = resource.UnstructuredContent()
|
||||
}
|
||||
if processExisting {
|
||||
glog.V(4).Infof("resource %s not found in existing namespace %s", rule.Generation.Name, ns.GetName())
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("resource %s not found in existing namespace %s", rule.Generation.Name, ns.GetName())
|
||||
// for existing resources we generate an error which indirectly generates a policy violation
|
||||
return fmt.Errorf("resource %s not found in existing namespace %s", gen.Name, ns.GetName())
|
||||
return response
|
||||
}
|
||||
resource.SetUnstructuredContent(rdata)
|
||||
resource.SetName(gen.Name)
|
||||
resource.SetName(rule.Generation.Name)
|
||||
resource.SetNamespace(ns.GetName())
|
||||
// Reset resource version
|
||||
resource.SetResourceVersion("")
|
||||
_, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false)
|
||||
_, err = client.CreateResource(rule.Generation.Kind, ns.GetName(), resource, false)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", gen.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
return err
|
||||
glog.V(4).Infof("generate rule: unable to create resource %s/%s/%s: %v", rule.Generation.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("unable to create resource %s/%s/%s: %v", rule.Generation.Kind, resource.GetNamespace(), resource.GetName(), err)
|
||||
return response
|
||||
}
|
||||
glog.V(4).Infof("generate rule: created resource %s/%s/%s", gen.Kind, resource.GetNamespace(), resource.GetName())
|
||||
return nil
|
||||
glog.V(4).Infof("generate rule: created resource %s/%s/%s", rule.Generation.Kind, resource.GetNamespace(), resource.GetName())
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("created resource %s/%s/%s", rule.Generation.Kind, resource.GetNamespace(), resource.GetName())
|
||||
return response
|
||||
}
|
||||
|
||||
//checkResource checks if the config is present in th eresource
|
||||
|
|
|
@ -6,46 +6,155 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// Mutate performs mutation. Overlay first and then mutation patches
|
||||
|
||||
func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponse) {
|
||||
// var response EngineResponse
|
||||
var allPatches, rulePatches [][]byte
|
||||
var err error
|
||||
var errs []error
|
||||
ris := []info.RuleInfo{}
|
||||
// func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponse) {
|
||||
// // var response EngineResponse
|
||||
// var allPatches, rulePatches [][]byte
|
||||
// var err error
|
||||
// var errs []error
|
||||
// ris := []info.RuleInfo{}
|
||||
// startTime := time.Now()
|
||||
// glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime)
|
||||
// defer func() {
|
||||
// response.ExecutionTime = time.Since(startTime)
|
||||
// glog.V(4).Infof("finished applying mutation rules policy %v (%v)", policy.Name, response.ExecutionTime)
|
||||
// glog.V(4).Infof("Mutation Rules appplied succesfully count %v for policy %q", response.RulesAppliedCount, policy.Name)
|
||||
// }()
|
||||
// incrementAppliedRuleCount := func() {
|
||||
// // rules applied succesfully count
|
||||
// response.RulesAppliedCount++
|
||||
// }
|
||||
|
||||
// patchedDocument, err := resource.MarshalJSON()
|
||||
// if err != nil {
|
||||
// glog.Errorf("unable to marshal resource : %v\n", err)
|
||||
// }
|
||||
|
||||
// if err != nil {
|
||||
// glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
// response.PatchedResource = resource
|
||||
// return response
|
||||
// }
|
||||
|
||||
// for _, rule := range policy.Spec.Rules {
|
||||
// if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// // check if the resource satisfies the filter conditions defined in the rule
|
||||
// //TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// // dont statisfy a policy rule resource description
|
||||
// ok := MatchesResourceDescription(resource, rule)
|
||||
// if !ok {
|
||||
// glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
// continue
|
||||
// }
|
||||
|
||||
// ruleInfo := info.NewRuleInfo(rule.Name, info.Mutation)
|
||||
|
||||
// // Process Overlay
|
||||
// if rule.Mutation.Overlay != nil {
|
||||
// // ruleRespone := processOverlay(rule, res)
|
||||
// rulePatches, err = processOverlay(rule, patchedDocument)
|
||||
// if err == nil {
|
||||
// if len(rulePatches) == 0 {
|
||||
// // if array elements dont match then we skip(nil patch, no error)
|
||||
// // or if acnohor is defined and doenst match
|
||||
// // policy is not applicable
|
||||
// glog.V(4).Info("overlay does not match, so skipping applying rule")
|
||||
// continue
|
||||
// }
|
||||
|
||||
// ruleInfo.Addf("Rule %s: Overlay succesfully applied.", rule.Name)
|
||||
|
||||
// // strip slashes from string
|
||||
// ruleInfo.Patches = rulePatches
|
||||
// allPatches = append(allPatches, rulePatches...)
|
||||
|
||||
// glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
// } else {
|
||||
// glog.V(4).Infof("failed to apply overlay: %v", err)
|
||||
// ruleInfo.Fail()
|
||||
// ruleInfo.Addf("failed to apply overlay: %v", err)
|
||||
// }
|
||||
// incrementAppliedRuleCount()
|
||||
// }
|
||||
|
||||
// // Process Patches
|
||||
// if len(rule.Mutation.Patches) != 0 {
|
||||
// rulePatches, errs = processPatches(rule, patchedDocument)
|
||||
// if len(errs) > 0 {
|
||||
// ruleInfo.Fail()
|
||||
// for _, err := range errs {
|
||||
// glog.V(4).Infof("failed to apply patches: %v", err)
|
||||
// ruleInfo.Addf("patches application has failed, err %v.", err)
|
||||
// }
|
||||
// } else {
|
||||
// glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
// ruleInfo.Addf("Patches succesfully applied.")
|
||||
|
||||
// ruleInfo.Patches = rulePatches
|
||||
// allPatches = append(allPatches, rulePatches...)
|
||||
// }
|
||||
// incrementAppliedRuleCount()
|
||||
// }
|
||||
|
||||
// patchedDocument, err = ApplyPatches(patchedDocument, rulePatches)
|
||||
// if err != nil {
|
||||
// glog.Errorf("Failed to apply patches on ruleName=%s, err%v\n:", rule.Name, err)
|
||||
// }
|
||||
|
||||
// ris = append(ris, ruleInfo)
|
||||
// }
|
||||
|
||||
// patchedResource, err := ConvertToUnstructured(patchedDocument)
|
||||
// if err != nil {
|
||||
// glog.Errorf("Failed to convert patched resource to unstructuredtype, err%v\n:", err)
|
||||
// response.PatchedResource = resource
|
||||
// return response
|
||||
// }
|
||||
|
||||
// response.Patches = allPatches
|
||||
// response.PatchedResource = *patchedResource
|
||||
// response.RuleInfos = ris
|
||||
// return response
|
||||
// }
|
||||
|
||||
//MutateNew ...
|
||||
func MutateNew(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
// policy information
|
||||
func() {
|
||||
// set policy information
|
||||
response.PolicyResponse.Policy = policy.Name
|
||||
// resource details
|
||||
response.PolicyResponse.Resource.Name = resource.GetName()
|
||||
response.PolicyResponse.Resource.Namespace = resource.GetNamespace()
|
||||
response.PolicyResponse.Resource.Kind = resource.GetKind()
|
||||
response.PolicyResponse.Resource.APIVersion = resource.GetAPIVersion()
|
||||
}()
|
||||
glog.V(4).Infof("started applying mutation rules of policy %q (%v)", policy.Name, startTime)
|
||||
defer func() {
|
||||
response.ExecutionTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying mutation rules policy %v (%v)", policy.Name, response.ExecutionTime)
|
||||
glog.V(4).Infof("Mutation Rules appplied succesfully count %v for policy %q", response.RulesAppliedCount, policy.Name)
|
||||
response.PolicyResponse.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying mutation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
|
||||
glog.V(4).Infof("Mutation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
|
||||
}()
|
||||
incrementAppliedRuleCount := func() {
|
||||
// rules applied succesfully count
|
||||
response.RulesAppliedCount++
|
||||
response.PolicyResponse.RulesAppliedCount++
|
||||
}
|
||||
|
||||
patchedDocument, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.Errorf("unable to marshal resource : %v\n", err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
response.PatchedResource = resource
|
||||
return response
|
||||
}
|
||||
var patchedResource unstructured.Unstructured
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
//TODO: to be checked before calling the resources as well
|
||||
if reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if the resource satisfies the filter conditions defined in the rule
|
||||
//TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// dont statisfy a policy rule resource description
|
||||
|
@ -54,72 +163,27 @@ func Mutate(policy kyverno.Policy, resource unstructured.Unstructured) (response
|
|||
glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
continue
|
||||
}
|
||||
|
||||
ruleInfo := info.NewRuleInfo(rule.Name, info.Mutation)
|
||||
|
||||
// Process Overlay
|
||||
if rule.Mutation.Overlay != nil {
|
||||
rulePatches, err = processOverlay(rule, patchedDocument)
|
||||
if err == nil {
|
||||
if len(rulePatches) == 0 {
|
||||
// if array elements dont match then we skip(nil patch, no error)
|
||||
// or if acnohor is defined and doenst match
|
||||
// policy is not applicable
|
||||
glog.V(4).Info("overlay does not match, so skipping applying rule")
|
||||
continue
|
||||
}
|
||||
|
||||
ruleInfo.Addf("Rule %s: Overlay succesfully applied.", rule.Name)
|
||||
|
||||
// strip slashes from string
|
||||
ruleInfo.Patches = rulePatches
|
||||
allPatches = append(allPatches, rulePatches...)
|
||||
|
||||
glog.V(4).Infof("overlay applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
} else {
|
||||
glog.V(4).Infof("failed to apply overlay: %v", err)
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Addf("failed to apply overlay: %v", err)
|
||||
var ruleResponse RuleResponse
|
||||
ruleResponse, patchedResource = processOverlayNew(rule, resource)
|
||||
if reflect.DeepEqual(ruleResponse, (RuleResponse{})) {
|
||||
// overlay pattern does not match the resource conditions
|
||||
continue
|
||||
}
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
|
||||
// Process Patches
|
||||
if len(rule.Mutation.Patches) != 0 {
|
||||
rulePatches, errs = processPatches(rule, patchedDocument)
|
||||
if len(errs) > 0 {
|
||||
ruleInfo.Fail()
|
||||
for _, err := range errs {
|
||||
glog.V(4).Infof("failed to apply patches: %v", err)
|
||||
ruleInfo.Addf("patches application has failed, err %v.", err)
|
||||
}
|
||||
} else {
|
||||
glog.V(4).Infof("patches applied succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
ruleInfo.Addf("Patches succesfully applied.")
|
||||
|
||||
ruleInfo.Patches = rulePatches
|
||||
allPatches = append(allPatches, rulePatches...)
|
||||
}
|
||||
if rule.Mutation.Patches != nil {
|
||||
var ruleResponse RuleResponse
|
||||
ruleResponse, patchedResource = processPatchesNew(rule, resource)
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
incrementAppliedRuleCount()
|
||||
}
|
||||
|
||||
patchedDocument, err = ApplyPatches(patchedDocument, rulePatches)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to apply patches on ruleName=%s, err%v\n:", rule.Name, err)
|
||||
}
|
||||
|
||||
ris = append(ris, ruleInfo)
|
||||
}
|
||||
|
||||
patchedResource, err := ConvertToUnstructured(patchedDocument)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to convert patched resource to unstructuredtype, err%v\n:", err)
|
||||
response.PatchedResource = resource
|
||||
return response
|
||||
}
|
||||
|
||||
response.Patches = allPatches
|
||||
response.PatchedResource = *patchedResource
|
||||
response.RuleInfos = ris
|
||||
// send the patched resource
|
||||
response.PatchedResource = patchedResource
|
||||
return response
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
|
@ -38,6 +40,65 @@ func processOverlay(rule kyverno.Rule, rawResource []byte) ([][]byte, error) {
|
|||
return patches, err
|
||||
}
|
||||
|
||||
// rawResource handles validating admission request
|
||||
// Checks the target resources for rules defined in the policy
|
||||
// TODO: pass in the unstructured object in stead of raw byte?
|
||||
func processOverlayNew(rule kyverno.Rule, resource unstructured.Unstructured) (response RuleResponse, patchedResource unstructured.Unstructured) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started applying overlay rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Mutation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying overlay rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
patches, err := processOverlayPatches(resource.UnstructuredContent(), rule.Mutation.Overlay)
|
||||
// resource does not satisfy the overlay pattern, we dont apply this rule
|
||||
if err != nil && strings.Contains(err.Error(), "Conditions are not met") {
|
||||
glog.V(4).Infof("Resource %s/%s/%s does not meet the conditions in the rule %s with overlay pattern %s", resource.GetKind(), resource.GetNamespace(), resource.GetName(), rule.Name, rule.Mutation.Overlay)
|
||||
//TODO: send zero response and not consider this as applied?
|
||||
return RuleResponse{}, resource
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// rule application failed
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process overlay: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
// convert to RAW
|
||||
resourceRaw, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
glog.Infof("unable to marshall resource: %v", err)
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
var patchResource []byte
|
||||
patchResource, err = ApplyPatches(resourceRaw, patches)
|
||||
if err != nil {
|
||||
glog.Info("failed to apply patch")
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to apply JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
err = patchedResource.UnmarshalJSON(patchResource)
|
||||
if err != nil {
|
||||
glog.Infof("failed to unmarshall resource to undstructured: %v", err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
// rule application succesfuly
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("succesfully process overlay")
|
||||
response.Patches = patches
|
||||
// apply the patches to the resource
|
||||
return response, patchedResource
|
||||
}
|
||||
func processOverlayPatches(resource, overlay interface{}) ([][]byte, error) {
|
||||
|
||||
if !meetConditions(resource, overlay) {
|
||||
|
@ -65,6 +126,7 @@ func applyOverlay(resource, overlay interface{}, path string) ([][]byte, error)
|
|||
}
|
||||
|
||||
appliedPatches = append(appliedPatches, patch)
|
||||
//TODO : check if return is needed ?
|
||||
}
|
||||
return applyOverlayForSameTypes(resource, overlay, path)
|
||||
}
|
||||
|
|
|
@ -3,9 +3,13 @@ package engine
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
|
@ -30,8 +34,8 @@ func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, er
|
|||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
patchedDocument, err = applyPatch(patchedDocument, patchRaw)
|
||||
patches := [][]byte{patchRaw}
|
||||
patchedDocument, err = ApplyPatches(patchedDocument, patches)
|
||||
// TODO: continue on error if one of the patches fails, will add the failure event in such case
|
||||
if patch.Operation == "remove" {
|
||||
glog.Info(err)
|
||||
|
@ -84,3 +88,90 @@ func ApplyPatches(resource []byte, patches [][]byte) ([]byte, error) {
|
|||
}
|
||||
return patchedDocument, err
|
||||
}
|
||||
|
||||
//ApplyPatchNew ...
|
||||
func ApplyPatchNew(resource, patch []byte) ([]byte, error) {
|
||||
patchesList := [][]byte{patch}
|
||||
joinedPatches := JoinPatches(patchesList)
|
||||
jsonpatch, err := jsonpatch.DecodePatch(joinedPatches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedResource, err := jsonpatch.Apply(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return patchedResource, err
|
||||
|
||||
}
|
||||
|
||||
func processPatchesNew(rule kyverno.Rule, resource unstructured.Unstructured) (response RuleResponse, patchedResource unstructured.Unstructured) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started JSON patch rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Mutation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished JSON patch rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
// convert to RAW
|
||||
resourceRaw, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
response.Success = false
|
||||
glog.Infof("unable to marshall resource: %v", err)
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var patches [][]byte
|
||||
for _, patch := range rule.Mutation.Patches {
|
||||
// JSON patch
|
||||
patchRaw, err := json.Marshal(patch)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to marshall JSON patch %v: %v", patch, err)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
patchResource, err := applyPatch(resourceRaw, patchRaw)
|
||||
// TODO: continue on error if one of the patches fails, will add the failure event in such case
|
||||
if err != nil && patch.Operation == "remove" {
|
||||
glog.Info(err)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
resourceRaw = patchResource
|
||||
patches = append(patches, patchRaw)
|
||||
}
|
||||
|
||||
// error while processing JSON patches
|
||||
if len(errs) > 0 {
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", func() string {
|
||||
var str []string
|
||||
for _, err := range errs {
|
||||
str = append(str, err.Error())
|
||||
}
|
||||
return strings.Join(str, ";")
|
||||
}())
|
||||
return response, resource
|
||||
}
|
||||
err = patchedResource.UnmarshalJSON(resourceRaw)
|
||||
if err != nil {
|
||||
glog.Infof("failed to unmarshall resource to undstructured: %v", err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
}
|
||||
|
||||
// JSON patches processed succesfully
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("succesfully process JSON patches")
|
||||
response.Patches = patches
|
||||
return response, patchedResource
|
||||
}
|
||||
|
|
116
pkg/engine/response.go
Normal file
116
pkg/engine/response.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
//EngineResponseNew engine response to the action
|
||||
type EngineResponseNew struct {
|
||||
// Resource patched with the engine action changes
|
||||
PatchedResource unstructured.Unstructured
|
||||
// Policy Response
|
||||
PolicyResponse PolicyResponse
|
||||
}
|
||||
|
||||
//PolicyResponse policy application response
|
||||
type PolicyResponse struct {
|
||||
// policy name
|
||||
Policy string `json:"policy"`
|
||||
// resource details
|
||||
Resource ResourceSpec `json:"resource"`
|
||||
// policy statistics
|
||||
PolicyStats `json:",inline"`
|
||||
// rule response
|
||||
Rules []RuleResponse `json:"rules"`
|
||||
// ValidationFailureAction: audit,enforce(default)
|
||||
ValidationFailureAction string
|
||||
}
|
||||
|
||||
//ResourceSpec resource action applied on
|
||||
type ResourceSpec struct {
|
||||
//TODO: support ApiVersion
|
||||
Kind string `json:"kind"`
|
||||
APIVersion string `json:"apiVersion"`
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
//PolicyStats stores statistics for the single policy application
|
||||
type PolicyStats struct {
|
||||
// time required to process the policy rules on a resource
|
||||
ProcessingTime time.Duration `json:"processingTime"`
|
||||
// Count of rules that were applied succesfully
|
||||
RulesAppliedCount int `json:"rulesAppliedCount"`
|
||||
}
|
||||
|
||||
//RuleResponse details for each rule applicatino
|
||||
type RuleResponse struct {
|
||||
// rule name specified in policy
|
||||
Name string `json:"name"`
|
||||
// rule type (Mutation,Generation,Validation) for Kyverno Policy
|
||||
Type string `json:"type"`
|
||||
// message response from the rule application
|
||||
Message string `json:"message"`
|
||||
// JSON patches, for mutation rules
|
||||
Patches [][]byte `json:"patches,omitempty"`
|
||||
// success/fail
|
||||
Success bool `json:"success"`
|
||||
// statistics
|
||||
RuleStats `json:",inline"`
|
||||
}
|
||||
|
||||
//ToString ...
|
||||
func (rr RuleResponse) ToString() string {
|
||||
return fmt.Sprintf("rule %s (%s): %v", rr.Name, rr.Type, rr.Message)
|
||||
}
|
||||
|
||||
//RuleStats stores the statisctis for the single rule application
|
||||
type RuleStats struct {
|
||||
// time required to appliy the rule on the resource
|
||||
ProcessingTime time.Duration `json:"processingTime"`
|
||||
}
|
||||
|
||||
//IsSuccesful checks if any rule has failed or not
|
||||
func (er EngineResponseNew) IsSuccesful() bool {
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
if !r.Success {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//GetPatches returns all the patches joined
|
||||
func (er EngineResponseNew) GetPatches() [][]byte {
|
||||
var patches [][]byte
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
if r.Patches != nil {
|
||||
patches = append(patches, r.Patches...)
|
||||
}
|
||||
}
|
||||
// join patches
|
||||
return patches
|
||||
}
|
||||
|
||||
//GetFailedRules returns failed rules
|
||||
func (er EngineResponseNew) GetFailedRules() []string {
|
||||
return er.getRules(false)
|
||||
}
|
||||
|
||||
//GetSuccessRules returns success rules
|
||||
func (er EngineResponseNew) GetSuccessRules() []string {
|
||||
return er.getRules(true)
|
||||
}
|
||||
|
||||
func (er EngineResponseNew) getRules(success bool) []string {
|
||||
var rules []string
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
if r.Success == success {
|
||||
rules = append(rules, r.Name)
|
||||
}
|
||||
}
|
||||
return rules
|
||||
}
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/minio/minio/pkg/wildcard"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
|
@ -19,13 +18,35 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
//EngineResponse provides the response to the application of a policy rule set on a resource
|
||||
type EngineResponse struct {
|
||||
Patches [][]byte
|
||||
PatchedResource unstructured.Unstructured
|
||||
RuleInfos []info.RuleInfo
|
||||
EngineStats
|
||||
}
|
||||
// //EngineResponse provides the response to the application of a policy rule set on a resource
|
||||
// type EngineResponse struct {
|
||||
// // JSON patches for mutation rules
|
||||
// Patches [][]byte
|
||||
// // Resource patched with the policy changes
|
||||
// PatchedResource unstructured.Unstructured
|
||||
// // Rule details
|
||||
// RuleInfos []info.RuleInfo
|
||||
// // PolicyS
|
||||
// EngineStats
|
||||
// }
|
||||
|
||||
// type EngineResponseNew struct {
|
||||
// // error while processing engine action
|
||||
// Err error
|
||||
// // Resource patched with the engine action changes
|
||||
// PatchedResource unstructured.Unstructured
|
||||
// // Policy Response
|
||||
// PolicyRespone PolicyResponse
|
||||
// }
|
||||
|
||||
// type PolicyResponse struct {
|
||||
// // policy name
|
||||
// Policy string
|
||||
// // resource details
|
||||
// Resource kyverno.ResourceSpec
|
||||
// }
|
||||
|
||||
// type PolicyStatus
|
||||
|
||||
//EngineStats stores in the statistics for a single application of resource
|
||||
type EngineStats struct {
|
||||
|
@ -518,3 +539,21 @@ func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
|||
}
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
type RuleType int
|
||||
|
||||
const (
|
||||
Mutation RuleType = iota
|
||||
Validation
|
||||
Generation
|
||||
All
|
||||
)
|
||||
|
||||
func (ri RuleType) String() string {
|
||||
return [...]string{
|
||||
"Mutation",
|
||||
"Validation",
|
||||
"Generation",
|
||||
"All",
|
||||
}[ri]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
@ -12,102 +11,115 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// Validate handles validating admission request
|
||||
// Checks the target resources for rules defined in the policy
|
||||
func Validate(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponse) {
|
||||
// var response EngineResponse
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime)
|
||||
defer func() {
|
||||
response.ExecutionTime = time.Since(startTime)
|
||||
glog.V(4).Infof("Finished applying validation rules policy %v (%v)", policy.Name, response.ExecutionTime)
|
||||
glog.V(4).Infof("Validation Rules appplied succesfully count %v for policy %q", response.RulesAppliedCount, policy.Name)
|
||||
}()
|
||||
incrementAppliedRuleCount := func() {
|
||||
// rules applied succesfully count
|
||||
response.RulesAppliedCount++
|
||||
}
|
||||
resourceRaw, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.V(4).Infof("Skip processing validating rule, unable to marshal resource : %v\n", err)
|
||||
response.PatchedResource = resource
|
||||
return response
|
||||
}
|
||||
// // Validate handles validating admission request
|
||||
// // Checks the target resources for rules defined in the policy
|
||||
// func Validate(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponse) {
|
||||
// // var response EngineResponse
|
||||
// startTime := time.Now()
|
||||
// glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime)
|
||||
// defer func() {
|
||||
// response.ExecutionTime = time.Since(startTime)
|
||||
// glog.V(4).Infof("Finished applying validation rules policy %v (%v)", policy.Name, response.ExecutionTime)
|
||||
// glog.V(4).Infof("Validation Rules appplied succesfully count %v for policy %q", response.RulesAppliedCount, policy.Name)
|
||||
// }()
|
||||
// incrementAppliedRuleCount := func() {
|
||||
// // rules applied succesfully count
|
||||
// response.RulesAppliedCount++
|
||||
// }
|
||||
// resourceRaw, err := resource.MarshalJSON()
|
||||
// if err != nil {
|
||||
// glog.V(4).Infof("Skip processing validating rule, unable to marshal resource : %v\n", err)
|
||||
// response.PatchedResource = resource
|
||||
// return response
|
||||
// }
|
||||
|
||||
var resourceInt interface{}
|
||||
if err := json.Unmarshal(resourceRaw, &resourceInt); err != nil {
|
||||
glog.V(4).Infof("unable to unmarshal resource : %v\n", err)
|
||||
response.PatchedResource = resource
|
||||
return response
|
||||
}
|
||||
// var resourceInt interface{}
|
||||
// if err := json.Unmarshal(resourceRaw, &resourceInt); err != nil {
|
||||
// glog.V(4).Infof("unable to unmarshal resource : %v\n", err)
|
||||
// response.PatchedResource = resource
|
||||
// return response
|
||||
// }
|
||||
|
||||
var ruleInfos []info.RuleInfo
|
||||
// var ruleInfos []info.RuleInfo
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
continue
|
||||
}
|
||||
// for _, rule := range policy.Spec.Rules {
|
||||
// if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// check if the resource satisfies the filter conditions defined in the rule
|
||||
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// dont statisfy a policy rule resource description
|
||||
ok := MatchesResourceDescription(resource, rule)
|
||||
if !ok {
|
||||
glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
continue
|
||||
}
|
||||
// // check if the resource satisfies the filter conditions defined in the rule
|
||||
// // TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// // dont statisfy a policy rule resource description
|
||||
// ok := MatchesResourceDescription(resource, rule)
|
||||
// if !ok {
|
||||
// glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
// continue
|
||||
// }
|
||||
|
||||
ruleInfo := validatePatterns(resource, rule.Validation, rule.Name)
|
||||
incrementAppliedRuleCount()
|
||||
ruleInfos = append(ruleInfos, ruleInfo)
|
||||
}
|
||||
response.RuleInfos = ruleInfos
|
||||
return response
|
||||
}
|
||||
// // ruleInfo := validatePatterns(resource, rule)
|
||||
// incrementAppliedRuleCount()
|
||||
// // ruleInfos = append(ruleInfos, ruleInfo)
|
||||
// }
|
||||
// response.RuleInfos = ruleInfos
|
||||
// return response
|
||||
// }
|
||||
|
||||
// validatePatterns validate pattern and anyPattern
|
||||
func validatePatterns(resource unstructured.Unstructured, validation kyverno.Validation, ruleName string) info.RuleInfo {
|
||||
var errs []error
|
||||
ruleInfo := info.NewRuleInfo(ruleName, info.Validation)
|
||||
func validatePatterns(resource unstructured.Unstructured, rule kyverno.Rule) (response RuleResponse) {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("started applying validation rule %q (%v)", rule.Name, startTime)
|
||||
response.Name = rule.Name
|
||||
response.Type = Validation.String()
|
||||
defer func() {
|
||||
response.RuleStats.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("finished applying validation rule %q (%v)", response.Name, response.RuleStats.ProcessingTime)
|
||||
}()
|
||||
|
||||
if validation.Pattern != nil {
|
||||
err := validateResourceWithPattern(resource.Object, validation.Pattern)
|
||||
// either pattern or anyPattern can be specified in Validation rule
|
||||
if rule.Validation.Pattern != nil {
|
||||
err := validateResourceWithPattern(resource.Object, rule.Validation.Pattern)
|
||||
if err != nil {
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Addf("Failed to apply pattern: %v", err)
|
||||
} else {
|
||||
ruleInfo.Add("Pattern succesfully validated")
|
||||
glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
|
||||
// rule application failed
|
||||
glog.V(4).Infof("failed to apply validation for rule %s on resource %s/%s/%s, pattern %v ", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), rule.Validation.Pattern)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to apply pattern: %v", err)
|
||||
return response
|
||||
}
|
||||
return ruleInfo
|
||||
// rule application succesful
|
||||
glog.V(4).Infof("rule %s pattern validated succesfully on resource %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("validation pattern succesfully validated")
|
||||
return response
|
||||
}
|
||||
|
||||
if validation.AnyPattern != nil {
|
||||
for _, pattern := range validation.AnyPattern {
|
||||
//TODO: add comments to explain the flow
|
||||
if rule.Validation.AnyPattern != nil {
|
||||
var errs []error
|
||||
for _, pattern := range rule.Validation.AnyPattern {
|
||||
if err := validateResourceWithPattern(resource.Object, pattern); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
failedPattern := len(errs)
|
||||
patterns := len(validation.AnyPattern)
|
||||
|
||||
// all pattern fail
|
||||
if failedPattern == patterns {
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Addf("None of anyPattern succeed: %v", errs)
|
||||
} else {
|
||||
ruleInfo.Addf("%d/%d patterns succesfully validated", patterns-failedPattern, patterns)
|
||||
failedPattern := len(errs)
|
||||
patterns := len(rule.Validation.AnyPattern)
|
||||
// all patterns fail
|
||||
if failedPattern == patterns {
|
||||
// any Pattern application failed
|
||||
glog.V(4).Infof("none of anyPattern were processed: %v", errs)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("None of anyPattern succeed: %v", errs)
|
||||
return response
|
||||
}
|
||||
// any Pattern application succesful
|
||||
glog.V(4).Infof("%d/%d patterns validated succesfully on resource %s/%s", patterns-failedPattern, patterns, resource.GetNamespace(), resource.GetName())
|
||||
response.Success = true
|
||||
response.Message = fmt.Sprintf("%d/%d patterns succesfully validated", patterns-failedPattern, patterns)
|
||||
return response
|
||||
}
|
||||
return ruleInfo
|
||||
}
|
||||
|
||||
return info.RuleInfo{}
|
||||
return RuleResponse{}
|
||||
}
|
||||
|
||||
// validateResourceWithPattern is a start of element-by-element validation process
|
||||
|
@ -328,3 +340,51 @@ func validateArrayOfMaps(resourceMapArray []interface{}, patternMap map[string]i
|
|||
handler := CreateAnchorHandler(anchor, pattern, path)
|
||||
return handler.Handle(resourceMapArray, patternMap, originPattern)
|
||||
}
|
||||
|
||||
//ValidateNew ...
|
||||
func ValidateNew(policy kyverno.Policy, resource unstructured.Unstructured) (response EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
// policy information
|
||||
func() {
|
||||
// set policy information
|
||||
response.PolicyResponse.Policy = policy.Name
|
||||
// resource details
|
||||
response.PolicyResponse.Resource.Name = resource.GetName()
|
||||
response.PolicyResponse.Resource.Namespace = resource.GetNamespace()
|
||||
response.PolicyResponse.Resource.Kind = resource.GetKind()
|
||||
response.PolicyResponse.Resource.APIVersion = resource.GetAPIVersion()
|
||||
response.PolicyResponse.ValidationFailureAction = policy.Spec.ValidationFailureAction
|
||||
}()
|
||||
|
||||
glog.V(4).Infof("started applying validation rules of policy %q (%v)", policy.Name, startTime)
|
||||
defer func() {
|
||||
response.PolicyResponse.ProcessingTime = time.Since(startTime)
|
||||
glog.V(4).Infof("Finished applying validation rules policy %v (%v)", policy.Name, response.PolicyResponse.ProcessingTime)
|
||||
glog.V(4).Infof("Validation Rules appplied succesfully count %v for policy %q", response.PolicyResponse.RulesAppliedCount, policy.Name)
|
||||
}()
|
||||
incrementAppliedRuleCount := func() {
|
||||
// rules applied succesfully count
|
||||
response.PolicyResponse.RulesAppliedCount++
|
||||
}
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if the resource satisfies the filter conditions defined in the rule
|
||||
// TODO: this needs to be extracted, to filter the resource so that we can avoid passing resources that
|
||||
// dont statisfy a policy rule resource description
|
||||
ok := MatchesResourceDescription(resource, rule)
|
||||
if !ok {
|
||||
glog.V(4).Infof("resource %s/%s does not satisfy the resource description for the rule ", resource.GetNamespace(), resource.GetName())
|
||||
continue
|
||||
}
|
||||
if rule.Validation.Pattern != nil || rule.Validation.AnyPattern != nil {
|
||||
ruleResponse := validatePatterns(resource, rule)
|
||||
incrementAppliedRuleCount()
|
||||
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules, ruleResponse)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
|
|
@ -1571,8 +1571,8 @@ func TestValidate_ServiceTest(t *testing.T) {
|
|||
|
||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
res := Validate(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(res.RuleInfos) == 0)
|
||||
er := ValidateNew(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
|
||||
}
|
||||
|
||||
func TestValidate_MapHasFloats(t *testing.T) {
|
||||
|
@ -1668,6 +1668,6 @@ func TestValidate_MapHasFloats(t *testing.T) {
|
|||
|
||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
res := Validate(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(res.RuleInfos) == 0)
|
||||
er := ValidateNew(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ type Generator struct {
|
|||
|
||||
//Interface to generate event
|
||||
type Interface interface {
|
||||
Add(infoList ...*Info)
|
||||
Add(infoList ...Info)
|
||||
}
|
||||
|
||||
//NewEventGenerator to generate a new event controller
|
||||
|
@ -69,9 +69,9 @@ func initRecorder(client *client.Client) record.EventRecorder {
|
|||
}
|
||||
|
||||
//Add queues an event for generation
|
||||
func (gen *Generator) Add(infos ...*Info) {
|
||||
func (gen *Generator) Add(infos ...Info) {
|
||||
for _, info := range infos {
|
||||
gen.queue.Add(*info)
|
||||
gen.queue.Add(info)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,3 +180,24 @@ func NewEvent(rkind string, rnamespace string, rname string, reason Reason, mess
|
|||
Message: msgText,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEventNew(
|
||||
rkind,
|
||||
rapiVersion,
|
||||
rnamespace,
|
||||
rname,
|
||||
reason string,
|
||||
message MsgKey,
|
||||
args ...interface{}) Info {
|
||||
msgText, err := getEventMsg(message, args...)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
return Info{
|
||||
Kind: rkind,
|
||||
Name: rname,
|
||||
Namespace: rnamespace,
|
||||
Reason: reason,
|
||||
Message: msgText,
|
||||
}
|
||||
}
|
||||
|
|
192
pkg/info/info.go
192
pkg/info/info.go
|
@ -1,192 +0,0 @@
|
|||
package info
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//PolicyInfo defines policy information
|
||||
type PolicyInfo struct {
|
||||
// Name is policy name
|
||||
Name string
|
||||
// RKind represents the resource kind
|
||||
RKind string
|
||||
// RName is resource name
|
||||
RName string
|
||||
// Namespace is the ns of resource
|
||||
// empty on non-namespaced resources
|
||||
RNamespace string
|
||||
//TODO: add check/enum for types
|
||||
ValidationFailureAction string // BlockChanges, ReportViolation
|
||||
Rules []RuleInfo
|
||||
success bool
|
||||
}
|
||||
|
||||
//NewPolicyInfo returns a new policy info
|
||||
func NewPolicyInfo(policyName, rKind, rName, rNamespace, validationFailureAction string) PolicyInfo {
|
||||
pi := PolicyInfo{
|
||||
Name: policyName,
|
||||
RKind: rKind,
|
||||
RName: rName,
|
||||
RNamespace: rNamespace,
|
||||
success: true, // fail to be set explicity
|
||||
ValidationFailureAction: validationFailureAction,
|
||||
}
|
||||
return pi
|
||||
}
|
||||
|
||||
//IsSuccessful checks if policy is succesful
|
||||
// the policy is set to fail, if any of the rules have failed
|
||||
func (pi *PolicyInfo) IsSuccessful() bool {
|
||||
for _, r := range pi.Rules {
|
||||
if !r.success {
|
||||
pi.success = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
pi.success = true
|
||||
return true
|
||||
}
|
||||
|
||||
// SuccessfulRules returns list of successful rule names
|
||||
func (pi *PolicyInfo) SuccessfulRules() []string {
|
||||
var rules []string
|
||||
for _, r := range pi.Rules {
|
||||
if r.IsSuccessful() {
|
||||
rules = append(rules, r.Name)
|
||||
}
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
// FailedRules returns list of failed rule names
|
||||
func (pi *PolicyInfo) FailedRules() []string {
|
||||
var rules []string
|
||||
for _, r := range pi.Rules {
|
||||
if !r.IsSuccessful() {
|
||||
rules = append(rules, r.Name)
|
||||
}
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
//ErrorRules returns error msgs from all rule
|
||||
func (pi *PolicyInfo) ErrorRules() string {
|
||||
errorMsgs := []string{}
|
||||
for _, r := range pi.Rules {
|
||||
if !r.IsSuccessful() {
|
||||
errorMsgs = append(errorMsgs, r.ToString())
|
||||
}
|
||||
}
|
||||
return strings.Join(errorMsgs, ";")
|
||||
}
|
||||
|
||||
type RuleType int
|
||||
|
||||
const (
|
||||
Mutation RuleType = iota
|
||||
Validation
|
||||
Generation
|
||||
All
|
||||
)
|
||||
|
||||
func (ri RuleType) String() string {
|
||||
return [...]string{
|
||||
"Mutation",
|
||||
"Validation",
|
||||
"Generation",
|
||||
"All",
|
||||
}[ri]
|
||||
}
|
||||
|
||||
//RuleInfo defines rule struct
|
||||
type RuleInfo struct {
|
||||
Name string
|
||||
RuleType RuleType
|
||||
Msgs []string
|
||||
Patches [][]byte // this will store the mutation patch being applied by the rule
|
||||
success bool
|
||||
}
|
||||
|
||||
//ToString reule information
|
||||
//TODO: check if this is needed
|
||||
func (ri *RuleInfo) ToString() string {
|
||||
str := "rulename: " + ri.Name
|
||||
msgs := strings.Join(ri.Msgs, ";")
|
||||
return strings.Join([]string{str, msgs}, ";")
|
||||
}
|
||||
|
||||
//GetErrorString returns the error message for a rule
|
||||
func (ri *RuleInfo) GetErrorString() string {
|
||||
return strings.Join(ri.Msgs, ";")
|
||||
}
|
||||
|
||||
//NewRuleInfo creates a new RuleInfo
|
||||
func NewRuleInfo(ruleName string, ruleType RuleType) RuleInfo {
|
||||
return RuleInfo{
|
||||
Name: ruleName,
|
||||
Msgs: []string{},
|
||||
RuleType: ruleType,
|
||||
success: true, // fail to be set explicity
|
||||
}
|
||||
}
|
||||
|
||||
//Fail set the rule as failed
|
||||
func (ri *RuleInfo) Fail() {
|
||||
ri.success = false
|
||||
}
|
||||
|
||||
//IsSuccessful checks if rule is succesful
|
||||
func (ri *RuleInfo) IsSuccessful() bool {
|
||||
return ri.success
|
||||
}
|
||||
|
||||
//Add add msg
|
||||
func (ri *RuleInfo) Add(msg string) {
|
||||
ri.Msgs = append(ri.Msgs, msg)
|
||||
}
|
||||
|
||||
//Addf add msg with args
|
||||
func (ri *RuleInfo) Addf(msg string, args ...interface{}) {
|
||||
ri.Msgs = append(ri.Msgs, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
//RulesSuccesfuly check if the any rule has failed or not
|
||||
func rulesSuccesfuly(rules []RuleInfo) bool {
|
||||
for _, r := range rules {
|
||||
if !r.success {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//AddRuleInfos sets the rule information
|
||||
func (pi *PolicyInfo) AddRuleInfos(rules []RuleInfo) {
|
||||
if rules == nil {
|
||||
return
|
||||
}
|
||||
if !rulesSuccesfuly(rules) {
|
||||
pi.success = false
|
||||
}
|
||||
|
||||
pi.Rules = append(pi.Rules, rules...)
|
||||
}
|
||||
|
||||
//GetRuleNames gets the name of successful rules
|
||||
func (pi *PolicyInfo) GetRuleNames(onSuccess bool) string {
|
||||
var ruleNames []string
|
||||
for _, rule := range pi.Rules {
|
||||
if onSuccess {
|
||||
if rule.IsSuccessful() {
|
||||
ruleNames = append(ruleNames, rule.Name)
|
||||
}
|
||||
} else {
|
||||
if !rule.IsSuccessful() {
|
||||
ruleNames = append(ruleNames, rule.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(ruleNames, ",")
|
||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -100,42 +99,31 @@ func applyPolicyOnRaw(policy *kyverno.Policy, rawResource []byte, gvk *metav1.Gr
|
|||
|
||||
rname := engine.ParseNameFromObject(rawResource)
|
||||
rns := engine.ParseNamespaceFromObject(rawResource)
|
||||
policyInfo := info.NewPolicyInfo(policy.Name,
|
||||
gvk.Kind,
|
||||
rname,
|
||||
rns,
|
||||
policy.Spec.ValidationFailureAction)
|
||||
|
||||
resource, err := ConvertToUnstructured(rawResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//TODO check if the kind information is present resource
|
||||
// Process Mutation
|
||||
engineResponse := engine.Mutate(*policy, *resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
if !policyInfo.IsSuccessful() {
|
||||
engineResponse := engine.MutateNew(*policy, *resource)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.Warning(r.Msgs)
|
||||
for _, r := range engineResponse.PolicyResponse.Rules {
|
||||
glog.Warning(r.Message)
|
||||
}
|
||||
} else if len(engineResponse.Patches) > 0 {
|
||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||
glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||
patchedResource, err = engine.ApplyPatches(rawResource, engineResponse.Patches)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to apply mutation patches:\n%v", err)
|
||||
}
|
||||
// Process Validation
|
||||
engineResponse := engine.Validate(*policy, *resource)
|
||||
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
if !policyInfo.IsSuccessful() {
|
||||
// Process Validation
|
||||
engineResponse := engine.ValidateNew(*policy, *resource)
|
||||
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.Warning(r.Msgs)
|
||||
for _, r := range engineResponse.PolicyResponse.Rules {
|
||||
glog.Warning(r.Message)
|
||||
}
|
||||
return patchedResource, fmt.Errorf("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
} else if len(engineResponse.RuleInfos) > 0 {
|
||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||
glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,8 +202,8 @@ func (nsc *NamespaceController) syncNamespace(key string) error {
|
|||
n := namespace.DeepCopy()
|
||||
|
||||
// process generate rules
|
||||
policyInfos := nsc.processNamespace(*n)
|
||||
engineResponses := nsc.processNamespace(*n)
|
||||
// report errors
|
||||
nsc.report(policyInfos)
|
||||
nsc.report(engineResponses)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/policy"
|
||||
policyctr "github.com/nirmata/kyverno/pkg/policy"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
@ -85,13 +85,12 @@ func buildKey(policy, pv, kind, ns, name, rv string) string {
|
|||
return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv
|
||||
}
|
||||
|
||||
func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []info.PolicyInfo {
|
||||
var policyInfos []info.PolicyInfo
|
||||
func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []engine.EngineResponseNew {
|
||||
// convert to unstructured
|
||||
unstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&namespace)
|
||||
if err != nil {
|
||||
glog.Infof("unable to convert to unstructured, not processing any policies: %v", err)
|
||||
return policyInfos
|
||||
return nil
|
||||
}
|
||||
nsc.rm.Drop()
|
||||
|
||||
|
@ -100,18 +99,20 @@ func (nsc *NamespaceController) processNamespace(namespace corev1.Namespace) []i
|
|||
// get all the policies that have a generate rule and resource description satifies the namespace
|
||||
// apply policy on resource
|
||||
policies := listpolicies(ns, nsc.pLister)
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
for _, policy := range policies {
|
||||
// pre-processing, check if the policy and resource version has been processed before
|
||||
if !nsc.rm.ProcessResource(policy.Name, policy.ResourceVersion, ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion()) {
|
||||
glog.V(4).Infof("policy %s with resource version %s already processed on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion())
|
||||
continue
|
||||
}
|
||||
policyInfo := applyPolicy(nsc.client, ns, *policy, nsc.policyStatus)
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
engineResponse := applyPolicy(nsc.client, ns, *policy, nsc.policyStatus)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
|
||||
// post-processing, register the resource as processed
|
||||
nsc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), ns.GetKind(), ns.GetNamespace(), ns.GetName(), ns.GetResourceVersion())
|
||||
}
|
||||
return policyInfos
|
||||
return engineResponses
|
||||
}
|
||||
|
||||
func listpolicies(ns unstructured.Unstructured, pLister kyvernolister.PolicyLister) []*kyverno.Policy {
|
||||
|
@ -139,17 +140,23 @@ func listpolicies(ns unstructured.Unstructured, pLister kyvernolister.PolicyList
|
|||
return filteredpolicies
|
||||
}
|
||||
|
||||
func applyPolicy(client *client.Client, resource unstructured.Unstructured, p kyverno.Policy, policyStatus policy.PolicyStatusInterface) info.PolicyInfo {
|
||||
var ps policy.PolicyStat
|
||||
gatherStat := func(policyName string, er engine.EngineResponse) {
|
||||
func applyPolicy(client *client.Client, resource unstructured.Unstructured, p kyverno.Policy, policyStatus policyctr.PolicyStatusInterface) engine.EngineResponseNew {
|
||||
var policyStats []policyctr.PolicyStat
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.GenerationExecutionTime = er.ExecutionTime
|
||||
ps.Stats.RulesAppliedCount = er.RulesAppliedCount
|
||||
ps.Stats.MutationExecutionTime = policyResponse.ProcessingTime
|
||||
ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
|
||||
policyStats = append(policyStats, ps)
|
||||
}
|
||||
// send stats for aggregation
|
||||
sendStat := func(blocked bool) {
|
||||
//SEND
|
||||
policyStatus.SendStat(ps)
|
||||
for _, stat := range policyStats {
|
||||
stat.Stats.ResourceBlocked = utils.Btoi(blocked)
|
||||
//SEND
|
||||
policyStatus.SendStat(stat)
|
||||
}
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
@ -157,13 +164,11 @@ func applyPolicy(client *client.Client, resource unstructured.Unstructured, p ky
|
|||
defer func() {
|
||||
glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", p.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime))
|
||||
}()
|
||||
policyInfo := info.NewPolicyInfo(p.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), p.Spec.ValidationFailureAction)
|
||||
engineResponse := engine.Generate(client, p, resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
// gather stats
|
||||
gatherStat(p.Name, engineResponse)
|
||||
gatherStat(p.Name, engineResponse.PolicyResponse)
|
||||
//send stats
|
||||
sendStat(false)
|
||||
|
||||
return policyInfo
|
||||
return engineResponse
|
||||
}
|
||||
|
|
|
@ -4,56 +4,54 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/policyviolation"
|
||||
)
|
||||
|
||||
func (nsc *NamespaceController) report(policyInfos []info.PolicyInfo) {
|
||||
func (nsc *NamespaceController) report(engineResponses []engine.EngineResponseNew) {
|
||||
// generate events
|
||||
// generate policy violations
|
||||
for _, policyInfo := range policyInfos {
|
||||
for _, er := range engineResponses {
|
||||
// events
|
||||
// success - policy applied on resource
|
||||
// failure - policy/rule failed to apply on the resource
|
||||
reportEvents(policyInfo, nsc.eventGen)
|
||||
reportEvents(er, nsc.eventGen)
|
||||
// policy violations
|
||||
// failure - policy/rule failed to apply on the resource
|
||||
}
|
||||
|
||||
// generate policy violation
|
||||
policyviolation.GeneratePolicyViolations(nsc.pvListerSynced, nsc.pvLister, nsc.kyvernoClient, policyInfos)
|
||||
|
||||
policyviolation.CreatePV(nsc.pvLister, nsc.kyvernoClient, engineResponses)
|
||||
}
|
||||
|
||||
//reportEvents generates events for the failed resources
|
||||
func reportEvents(policyInfo info.PolicyInfo, eventGen event.Interface) {
|
||||
|
||||
if policyInfo.IsSuccessful() {
|
||||
func reportEvents(engineResponse engine.EngineResponseNew, eventGen event.Interface) {
|
||||
if engineResponse.IsSuccesful() {
|
||||
return
|
||||
}
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
for _, rule := range policyInfo.Rules {
|
||||
if rule.IsSuccessful() {
|
||||
continue
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", engineResponse.PolicyResponse.Policy, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
for _, rule := range engineResponse.PolicyResponse.Rules {
|
||||
if rule.Success {
|
||||
return
|
||||
}
|
||||
|
||||
// generate event on resource for each failed rule
|
||||
e := &event.Info{}
|
||||
e.Kind = policyInfo.RKind
|
||||
e.Namespace = policyInfo.RNamespace
|
||||
e.Name = policyInfo.RName
|
||||
glog.V(4).Infof("generation event on resource %s/%s/%s for policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = engineResponse.PolicyResponse.Resource.Kind
|
||||
e.Namespace = engineResponse.PolicyResponse.Resource.Namespace
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", policyInfo.Name, rule.RuleType.String(), rule.Name, rule.GetErrorString())
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", engineResponse.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
|
||||
eventGen.Add(e)
|
||||
|
||||
}
|
||||
// generate a event on policy for all failed rules
|
||||
e := &event.Info{}
|
||||
glog.V(4).Infof("generation event on policy %s", engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = "Policy"
|
||||
e.Namespace = ""
|
||||
e.Name = policyInfo.Name
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("failed to apply rules %s on resource %s/%s/%s", policyInfo.FailedRules(), policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
e.Message = fmt.Sprintf("failed to apply rules %v on resource %s/%s/%s", engineResponse.GetFailedRules(), engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
eventGen.Add(e)
|
||||
}
|
||||
|
|
|
@ -1,131 +1,117 @@
|
|||
package policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// applyPolicy applies policy on a resource
|
||||
//TODO: generation rules
|
||||
func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (info.PolicyInfo, error) {
|
||||
var ps PolicyStat
|
||||
gatherStat := func(policyName string, er engine.EngineResponse) {
|
||||
// ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.ValidationExecutionTime = er.ExecutionTime
|
||||
ps.Stats.RulesAppliedCount = er.RulesAppliedCount
|
||||
}
|
||||
// send stats for aggregation
|
||||
sendStat := func(blocked bool) {
|
||||
//SEND
|
||||
policyStatus.SendStat(ps)
|
||||
}
|
||||
|
||||
func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (responses []engine.EngineResponseNew) {
|
||||
startTime := time.Now()
|
||||
var policyStats []PolicyStat
|
||||
glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime)
|
||||
defer func() {
|
||||
glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime))
|
||||
}()
|
||||
// glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
||||
policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.MutationExecutionTime = policyResponse.ProcessingTime
|
||||
ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
|
||||
policyStats = append(policyStats, ps)
|
||||
}
|
||||
// send stats for aggregation
|
||||
sendStat := func(blocked bool) {
|
||||
for _, stat := range policyStats {
|
||||
stat.Stats.ResourceBlocked = utils.Btoi(blocked)
|
||||
//SEND
|
||||
policyStatus.SendStat(stat)
|
||||
}
|
||||
}
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
var engineResponse engine.EngineResponseNew
|
||||
var err error
|
||||
|
||||
//MUTATION
|
||||
mruleInfos, err := mutation(policy, resource, policyStatus)
|
||||
policyInfo.AddRuleInfos(mruleInfos)
|
||||
engineResponse, err = mutation(policy, resource, policyStatus)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
if err != nil {
|
||||
return policyInfo, err
|
||||
glog.Errorf("unable to process mutation rules: %v", err)
|
||||
}
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
//send stats
|
||||
sendStat(false)
|
||||
|
||||
//VALIDATION
|
||||
engineResponse := engine.Validate(policy, resource)
|
||||
if len(engineResponse.RuleInfos) != 0 {
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
}
|
||||
engineResponse = engine.ValidateNew(policy, resource)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
// gather stats
|
||||
gatherStat(policy.Name, engineResponse)
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
//send stats
|
||||
sendStat(false)
|
||||
|
||||
//TODO: GENERATION
|
||||
return policyInfo, nil
|
||||
return engineResponses
|
||||
}
|
||||
func mutation(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (engine.EngineResponseNew, error) {
|
||||
engineResponse := engine.MutateNew(policy, resource)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.V(4).Infof("mutation had errors reporting them")
|
||||
return engineResponse, nil
|
||||
}
|
||||
// Verify if the JSON pathes returned by the Mutate are already applied to the resource
|
||||
if reflect.DeepEqual(resource, engineResponse.PatchedResource) {
|
||||
// resources matches
|
||||
glog.V(4).Infof("resource %s/%s/%s satisfies policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
|
||||
return engineResponse, nil
|
||||
}
|
||||
return getFailedOverallRuleInfo(resource, engineResponse)
|
||||
}
|
||||
|
||||
func mutation(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) ([]info.RuleInfo, error) {
|
||||
var ps PolicyStat
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, er engine.EngineResponse) {
|
||||
// ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.MutationExecutionTime = er.ExecutionTime
|
||||
ps.Stats.RulesAppliedCount = er.RulesAppliedCount
|
||||
}
|
||||
// send stats for aggregation
|
||||
sendStat := func(blocked bool) {
|
||||
//SEND
|
||||
policyStatus.SendStat(ps)
|
||||
}
|
||||
|
||||
engineResponse := engine.Mutate(policy, resource)
|
||||
// gather stats
|
||||
gatherStat(policy.Name, engineResponse)
|
||||
//send stats
|
||||
sendStat(false)
|
||||
|
||||
patches := engineResponse.Patches
|
||||
ruleInfos := engineResponse.RuleInfos
|
||||
if len(ruleInfos) == 0 {
|
||||
//no rules processed
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, r := range ruleInfos {
|
||||
if !r.IsSuccessful() {
|
||||
// no failures while processing rule
|
||||
return ruleInfos, nil
|
||||
}
|
||||
}
|
||||
if len(patches) == 0 {
|
||||
// no patches for the resources
|
||||
// either there were failures or the overlay already was satisfied
|
||||
return ruleInfos, nil
|
||||
}
|
||||
|
||||
// (original resource + patch) == (original resource)
|
||||
mergePatches := utils.JoinPatches(patches)
|
||||
patch, err := jsonpatch.DecodePatch(mergePatches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// getFailedOverallRuleInfo gets detailed info for over-all mutation failure
|
||||
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse engine.EngineResponseNew) (engine.EngineResponseNew, error) {
|
||||
rawResource, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to marshal resource : %v", err)
|
||||
return nil, err
|
||||
glog.V(4).Infof("unable to marshal resource: %v\n", err)
|
||||
return engine.EngineResponseNew{}, err
|
||||
}
|
||||
|
||||
// apply the patches returned by mutate to the original resource
|
||||
patchedResource, err := patch.Apply(rawResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//TODO: this will be removed after the support for patching for each rule
|
||||
ruleInfo := info.NewRuleInfo("over-all mutation", info.Mutation)
|
||||
// resource does not match so there was a mutation rule violated
|
||||
for index, rule := range engineResponse.PolicyResponse.Rules {
|
||||
glog.V(4).Infof("veriying if policy %s rule %s was applied before to resource %s/%s/%s", engineResponse.PolicyResponse.Policy, rule.Name, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
if len(rule.Patches) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !jsonpatch.Equal(patchedResource, rawResource) {
|
||||
//resource does not match so there was a mutation rule violated
|
||||
// TODO : check the rule name "mutation rules"
|
||||
ruleInfo.Fail()
|
||||
ruleInfo.Add("resource does not satisfy mutation rules")
|
||||
} else {
|
||||
ruleInfo.Add("resource satisfys the mutation rule")
|
||||
}
|
||||
patch, err := jsonpatch.DecodePatch(utils.JoinPatches(rule.Patches))
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to decode patch %s: %v", rule.Patches, err)
|
||||
return engine.EngineResponseNew{}, err
|
||||
}
|
||||
|
||||
ruleInfos = append(ruleInfos, ruleInfo)
|
||||
return ruleInfos, nil
|
||||
// apply the patches returned by mutate to the original resource
|
||||
patchedResource, err := patch.Apply(rawResource)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("unable to apply patch %s: %v", rule.Patches, err)
|
||||
return engine.EngineResponseNew{}, err
|
||||
}
|
||||
|
||||
if !jsonpatch.Equal(patchedResource, rawResource) {
|
||||
glog.V(4).Infof("policy %s rule %s condition not satisifed by existing resource", engineResponse.PolicyResponse.Policy, rule.Name)
|
||||
engineResponse.PolicyResponse.Rules[index].Success = false
|
||||
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("rule not satisfied by existing resource.")
|
||||
}
|
||||
}
|
||||
return engineResponse, nil
|
||||
}
|
||||
|
|
|
@ -8,17 +8,17 @@ import (
|
|||
"github.com/minio/minio/pkg/wildcard"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []info.PolicyInfo {
|
||||
func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []engine.EngineResponseNew {
|
||||
// Parse through all the resources
|
||||
// drops the cache after configured rebuild time
|
||||
pc.rm.Drop()
|
||||
var policyInfos []info.PolicyInfo
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
// get resource that are satisfy the resource description defined in the rules
|
||||
resourceMap := listResources(pc.client, policy, pc.filterK8Resources)
|
||||
for _, resource := range resourceMap {
|
||||
|
@ -29,21 +29,13 @@ func (pc *PolicyController) processExistingResources(policy kyverno.Policy) []in
|
|||
}
|
||||
// apply the policy on each
|
||||
glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
||||
policyInfo := applyPolicyOnResource(policy, resource, pc.statusAggregator)
|
||||
policyInfos = append(policyInfos, *policyInfo)
|
||||
engineResponse := applyPolicy(policy, resource, pc.statusAggregator)
|
||||
// get engine response for mutation & validation indipendently
|
||||
engineResponses = append(engineResponses, engineResponse...)
|
||||
// post-processing, register the resource as processed
|
||||
pc.rm.RegisterResource(policy.GetName(), policy.GetResourceVersion(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
|
||||
}
|
||||
return policyInfos
|
||||
}
|
||||
|
||||
func applyPolicyOnResource(policy kyverno.Policy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) *info.PolicyInfo {
|
||||
policyInfo, err := applyPolicy(policy, resource, policyStatus)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to process policy %s on resource %s/%s/%s: %v", policy.GetName(), resource.GetKind(), resource.GetNamespace(), resource.GetName(), err)
|
||||
return nil
|
||||
}
|
||||
return &policyInfo
|
||||
return engineResponses
|
||||
}
|
||||
|
||||
func listResources(client *client.Client, policy kyverno.Policy, filterK8Resources []utils.K8Resource) map[string]unstructured.Unstructured {
|
||||
|
|
|
@ -4,15 +4,15 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/policyviolation"
|
||||
)
|
||||
|
||||
func (pc *PolicyController) report(policyInfos []info.PolicyInfo) {
|
||||
func (pc *PolicyController) report(engineResponses []engine.EngineResponseNew) {
|
||||
// generate events
|
||||
// generate policy violations
|
||||
for _, policyInfo := range policyInfos {
|
||||
for _, policyInfo := range engineResponses {
|
||||
// events
|
||||
// success - policy applied on resource
|
||||
// failure - policy/rule failed to apply on the resource
|
||||
|
@ -22,37 +22,39 @@ func (pc *PolicyController) report(policyInfos []info.PolicyInfo) {
|
|||
}
|
||||
|
||||
// generate policy violation
|
||||
policyviolation.GeneratePolicyViolations(pc.pvListerSynced, pc.pvLister, pc.kyvernoClient, policyInfos)
|
||||
policyviolation.CreatePV(pc.pvLister, pc.kyvernoClient, engineResponses)
|
||||
}
|
||||
|
||||
//reportEvents generates events for the failed resources
|
||||
func reportEvents(policyInfo info.PolicyInfo, eventGen event.Interface) {
|
||||
|
||||
if policyInfo.IsSuccessful() {
|
||||
func reportEvents(engineResponse engine.EngineResponseNew, eventGen event.Interface) {
|
||||
if engineResponse.IsSuccesful() {
|
||||
return
|
||||
}
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
for _, rule := range policyInfo.Rules {
|
||||
if rule.IsSuccessful() {
|
||||
continue
|
||||
glog.V(4).Infof("reporting results for policy %s application on resource %s/%s/%s", engineResponse.PolicyResponse.Policy, engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
for _, rule := range engineResponse.PolicyResponse.Rules {
|
||||
if rule.Success {
|
||||
return
|
||||
}
|
||||
|
||||
// generate event on resource for each failed rule
|
||||
e := &event.Info{}
|
||||
e.Kind = policyInfo.RKind
|
||||
e.Namespace = policyInfo.RNamespace
|
||||
e.Name = policyInfo.RName
|
||||
glog.V(4).Infof("generation event on resource %s/%s/%s for policy %s", engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name, engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = engineResponse.PolicyResponse.Resource.Kind
|
||||
e.Namespace = engineResponse.PolicyResponse.Resource.Namespace
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", policyInfo.Name, rule.RuleType.String(), rule.Name, rule.GetErrorString())
|
||||
e.Message = fmt.Sprintf("policy %s (%s) rule %s failed to apply. %v", engineResponse.PolicyResponse.Policy, rule.Type, rule.Name, rule.Message)
|
||||
eventGen.Add(e)
|
||||
|
||||
}
|
||||
// generate a event on policy for all failed rules
|
||||
e := &event.Info{}
|
||||
glog.V(4).Infof("generation event on policy %s", engineResponse.PolicyResponse.Policy)
|
||||
e := event.Info{}
|
||||
e.Kind = "Policy"
|
||||
e.Namespace = ""
|
||||
e.Name = policyInfo.Name
|
||||
e.Name = engineResponse.PolicyResponse.Policy
|
||||
e.Reason = "Failure"
|
||||
e.Message = fmt.Sprintf("failed to apply rules %s on resource %s/%s/%s", policyInfo.FailedRules(), policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName)
|
||||
e.Message = fmt.Sprintf("failed to apply rules %v on resource %s/%s/%s", engineResponse.GetFailedRules(), engineResponse.PolicyResponse.Resource.Kind, engineResponse.PolicyResponse.Resource.Namespace, engineResponse.PolicyResponse.Resource.Name)
|
||||
eventGen.Add(e)
|
||||
|
||||
}
|
||||
|
|
17
pkg/policy/utils.go
Normal file
17
pkg/policy/utils.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package policy
|
||||
|
||||
import kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
|
||||
// reEvaulatePolicy checks if the policy needs to be re-evaulated
|
||||
// during re-evaulation we remove all the old policy violations and re-create new ones
|
||||
// - Rule count changes
|
||||
// - Rule resource description changes
|
||||
// - Rule operation changes
|
||||
// - Rule name changed
|
||||
func reEvaulatePolicy(curP, oldP *kyverno.Policy) bool {
|
||||
// count of rules changed
|
||||
if len(curP.Spec.Rules) != len(curP.Spec.Rules) {
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
@ -29,79 +29,153 @@ func BuildPolicyViolation(policy string, resource kyverno.ResourceSpec, fRules [
|
|||
}
|
||||
|
||||
// buildPolicyViolationsForAPolicy returns a policy violation object if there are any rules that fail
|
||||
func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation {
|
||||
var fRules []kyverno.ViolatedRule
|
||||
var pv kyverno.PolicyViolation
|
||||
for _, r := range pi.Rules {
|
||||
if !r.IsSuccessful() {
|
||||
fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()})
|
||||
// func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation {
|
||||
// var fRules []kyverno.ViolatedRule
|
||||
// var pv kyverno.PolicyViolation
|
||||
// for _, r := range pi.Rules {
|
||||
// if !r.IsSuccessful() {
|
||||
// fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()})
|
||||
// }
|
||||
// }
|
||||
// if len(fRules) > 0 {
|
||||
// glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName)
|
||||
// // there is an error
|
||||
// pv = BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{
|
||||
// Kind: pi.RKind,
|
||||
// Namespace: pi.RNamespace,
|
||||
// Name: pi.RName,
|
||||
// },
|
||||
// fRules,
|
||||
// )
|
||||
|
||||
// }
|
||||
// return pv
|
||||
// }
|
||||
|
||||
func buildPVForPolicy(er engine.EngineResponseNew) kyverno.PolicyViolation {
|
||||
var violatedRules []kyverno.ViolatedRule
|
||||
glog.V(4).Infof("building policy violation for engine response %v", er)
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
// filter failed/violated rules
|
||||
if !r.Success {
|
||||
vrule := kyverno.ViolatedRule{
|
||||
Name: r.Name,
|
||||
Message: r.Message,
|
||||
Type: r.Type,
|
||||
}
|
||||
violatedRules = append(violatedRules, vrule)
|
||||
}
|
||||
}
|
||||
if len(fRules) > 0 {
|
||||
glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName)
|
||||
// there is an error
|
||||
pv = BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{
|
||||
Kind: pi.RKind,
|
||||
Namespace: pi.RNamespace,
|
||||
Name: pi.RName,
|
||||
pv := BuildPolicyViolation(er.PolicyResponse.Policy,
|
||||
kyverno.ResourceSpec{
|
||||
Kind: er.PolicyResponse.Resource.Kind,
|
||||
Namespace: er.PolicyResponse.Resource.Namespace,
|
||||
Name: er.PolicyResponse.Resource.Name,
|
||||
},
|
||||
fRules,
|
||||
)
|
||||
|
||||
}
|
||||
violatedRules,
|
||||
)
|
||||
return pv
|
||||
}
|
||||
|
||||
//generatePolicyViolations generate policyViolation resources for the rules that failed
|
||||
//TODO: check if pvListerSynced is needed
|
||||
func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister kyvernolister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) {
|
||||
//CreatePV creates policy violation resource based on the engine responses
|
||||
func CreatePV(pvLister kyvernolister.PolicyViolationLister, client *kyvernoclient.Clientset, engineResponses []engine.EngineResponseNew) {
|
||||
var pvs []kyverno.PolicyViolation
|
||||
for _, policyInfo := range policyInfos {
|
||||
if !policyInfo.IsSuccessful() {
|
||||
if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) {
|
||||
for _, er := range engineResponses {
|
||||
if !er.IsSuccesful() {
|
||||
if pv := buildPVForPolicy(er); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) {
|
||||
pvs = append(pvs, pv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(pvs) > 0 {
|
||||
for _, newPv := range pvs {
|
||||
// generate PolicyViolation objects
|
||||
glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name)
|
||||
|
||||
// check if there was a previous violation for policy & resource combination
|
||||
curPv, err := getExistingPolicyViolationIfAny(pvListerSynced, pvLister, newPv)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if curPv == nil {
|
||||
// no existing policy violation, create a new one
|
||||
_, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// compare the policyviolation spec for existing resource if present else
|
||||
if reflect.DeepEqual(curPv.Spec, newPv.Spec) {
|
||||
// if they are equal there has been no change so dont update the polivy violation
|
||||
glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec)
|
||||
continue
|
||||
}
|
||||
// spec changed so update the policyviolation
|
||||
//TODO: wont work, as name is not defined yet
|
||||
_, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv)
|
||||
if len(pvs) == 0 {
|
||||
return
|
||||
}
|
||||
for _, newPv := range pvs {
|
||||
glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name)
|
||||
// check if there was a previous policy voilation for policy & resource combination
|
||||
curPv, err := getExistingPolicyViolationIfAny(nil, pvLister, newPv)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
if curPv == nil {
|
||||
glog.V(4).Infof("creating new policy violation for policy %s & resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.ResourceSpec.Kind, newPv.Spec.ResourceSpec.Namespace, newPv.Spec.ResourceSpec.Name)
|
||||
// no existing policy violation, create a new one
|
||||
_, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
// compare the policyviolation spec for existing resource if present else
|
||||
if reflect.DeepEqual(curPv.Spec, newPv.Spec) {
|
||||
// if they are equal there has been no change so dont update the polivy violation
|
||||
glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec)
|
||||
continue
|
||||
}
|
||||
// spec changed so update the policyviolation
|
||||
glog.V(4).Infof("creating new policy violation for policy %s & resource %s/%s/%s", curPv.Spec.Policy, curPv.Spec.ResourceSpec.Kind, curPv.Spec.ResourceSpec.Namespace, curPv.Spec.ResourceSpec.Name)
|
||||
//TODO: using a generic name, but would it be helpful to have naming convention for policy violations
|
||||
// as we can only have one policy violation for each (policy + resource) combination
|
||||
_, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// //GeneratePolicyViolations generate policyViolation resources for the rules that failed
|
||||
// //TODO: check if pvListerSynced is needed
|
||||
// func GeneratePolicyViolations(pvListerSynced cache.InformerSynced, pvLister kyvernolister.PolicyViolationLister, client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) {
|
||||
// var pvs []kyverno.PolicyViolation
|
||||
// for _, policyInfo := range policyInfos {
|
||||
// if !policyInfo.IsSuccessful() {
|
||||
// if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) {
|
||||
// pvs = append(pvs, pv)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if len(pvs) > 0 {
|
||||
// for _, newPv := range pvs {
|
||||
// // generate PolicyViolation objects
|
||||
// glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name)
|
||||
|
||||
// // check if there was a previous violation for policy & resource combination
|
||||
// curPv, err := getExistingPolicyViolationIfAny(pvListerSynced, pvLister, newPv)
|
||||
// if err != nil {
|
||||
// continue
|
||||
// }
|
||||
// if curPv == nil {
|
||||
// // no existing policy violation, create a new one
|
||||
// _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// // compare the policyviolation spec for existing resource if present else
|
||||
// if reflect.DeepEqual(curPv.Spec, newPv.Spec) {
|
||||
// // if they are equal there has been no change so dont update the polivy violation
|
||||
// glog.Infof("policy violation spec %v did not change so not updating it", newPv.Spec)
|
||||
// continue
|
||||
// }
|
||||
// // spec changed so update the policyviolation
|
||||
// //TODO: wont work, as name is not defined yet
|
||||
// _, err = client.KyvernoV1alpha1().PolicyViolations().Update(&newPv)
|
||||
// if err != nil {
|
||||
// glog.Error(err)
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//TODO: change the name
|
||||
func getExistingPolicyViolationIfAny(pvListerSynced cache.InformerSynced, pvLister kyvernolister.PolicyViolationLister, newPv kyverno.PolicyViolation) (*kyverno.PolicyViolation, error) {
|
||||
// TODO: check for existing ov using label selectors on resource and policy
|
||||
// TODO: there can be duplicates, as the labels have not been assigned to the policy violation yet
|
||||
labelMap := map[string]string{"policy": newPv.Spec.Policy, "resource": newPv.Spec.ResourceSpec.ToKey()}
|
||||
ls := &metav1.LabelSelector{}
|
||||
err := metav1.Convert_Map_string_To_string_To_v1_LabelSelector(&labelMap, ls, nil)
|
||||
|
|
418
pkg/testrunner/scenario.go
Normal file
418
pkg/testrunner/scenario.go
Normal file
|
@ -0,0 +1,418 @@
|
|||
package testrunner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
ospath "path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"gopkg.in/yaml.v2"
|
||||
apiyaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
)
|
||||
|
||||
type scenarioT struct {
|
||||
testCases []scaseT
|
||||
}
|
||||
|
||||
//scase defines input and output for a case
|
||||
type scaseT struct {
|
||||
Input sInput `yaml:"input"`
|
||||
Expected sExpected `yaml:"expected"`
|
||||
}
|
||||
|
||||
//sInput defines input for a test scenario
|
||||
type sInput struct {
|
||||
Policy string `yaml:"policy"`
|
||||
Resource string `yaml:"resource"`
|
||||
LoadResources []string `yaml:"loadresources,omitempty"`
|
||||
}
|
||||
|
||||
type sExpected struct {
|
||||
Mutation sMutation `yaml:"mutation,omitempty"`
|
||||
Validation sValidation `yaml:"validation,omitempty"`
|
||||
// Generation sGeneration `yaml:"generation,omitempty"`
|
||||
}
|
||||
|
||||
type sMutation struct {
|
||||
// path to the patched resource to be compared with
|
||||
PatchedResource string `yaml:"patchedresource,omitempty"`
|
||||
// expected respone from the policy engine
|
||||
PolicyResponse engine.PolicyResponse `yaml:"policyresponse"`
|
||||
}
|
||||
|
||||
type sValidation struct {
|
||||
// expected respone from the policy engine
|
||||
PolicyResponse engine.PolicyResponse `yaml:"policyresponse"`
|
||||
}
|
||||
|
||||
//getRelativePath expects a path relative to project and builds the complete path
|
||||
func getRelativePath(path string) string {
|
||||
gp := os.Getenv("GOPATH")
|
||||
ap := ospath.Join(gp, projectPath)
|
||||
return ospath.Join(ap, path)
|
||||
}
|
||||
|
||||
func loadScenario(t *testing.T, path string) (*scenarioT, error) {
|
||||
fileBytes, err := loadFile(t, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var testCases []scaseT
|
||||
// load test cases seperated by '---'
|
||||
// each test case defines an input & expected result
|
||||
scenariosBytes := bytes.Split(fileBytes, []byte("---"))
|
||||
for _, scenarioBytes := range scenariosBytes {
|
||||
tc := scaseT{}
|
||||
if err := yaml.Unmarshal([]byte(scenarioBytes), &tc); err != nil {
|
||||
t.Errorf("failed to decode test case YAML: %v", err)
|
||||
continue
|
||||
}
|
||||
testCases = append(testCases, tc)
|
||||
}
|
||||
scenario := scenarioT{
|
||||
testCases: testCases,
|
||||
}
|
||||
|
||||
return &scenario, nil
|
||||
}
|
||||
|
||||
// loadFile loads file in byte buffer
|
||||
func loadFile(t *testing.T, path string) ([]byte, error) {
|
||||
path = getRelativePath(path)
|
||||
t.Logf("reading file %s", path)
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.ReadFile(path)
|
||||
}
|
||||
|
||||
//getFiles loads all scneario files in specified folder path
|
||||
func getFiles(t *testing.T, folder string) ([]string, error) {
|
||||
t.Logf("loading scneario files for folder %s", folder)
|
||||
files, err := ioutil.ReadDir(folder)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var yamls []string
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) == ".yml" || filepath.Ext(file.Name()) == ".yaml" {
|
||||
yamls = append(yamls, ospath.Join(folder, file.Name()))
|
||||
}
|
||||
}
|
||||
return yamls, nil
|
||||
}
|
||||
|
||||
func runScenario(t *testing.T, s *scenarioT) bool {
|
||||
for _, tc := range s.testCases {
|
||||
runTestCase(t, tc)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func runTestCase(t *testing.T, tc scaseT) bool {
|
||||
// var client *client.Client
|
||||
// client, err := getClient(tc.Input.LoadResources)
|
||||
|
||||
// generate mock client if resources are to be loaded
|
||||
// - create mock client
|
||||
// - load resources
|
||||
// client := getClient(t, tc.Input.LoadResources)
|
||||
|
||||
// apply policy
|
||||
// convert policy -> kyverno.Policy
|
||||
policy := loadPolicy(t, tc.Input.Policy)
|
||||
// convert resource -> unstructured.Unstructured
|
||||
resource := loadPolicyResource(t, tc.Input.Resource)
|
||||
|
||||
var er engine.EngineResponseNew
|
||||
// Mutation
|
||||
er = engine.MutateNew(*policy, *resource)
|
||||
func() {
|
||||
if data, err := json.Marshal(er.PolicyResponse); err == nil {
|
||||
t.Log("-----engine response----")
|
||||
t.Log(string(data))
|
||||
// for _, r := range er.PolicyResponse.Rules {
|
||||
// for _, p := range r.Patches {
|
||||
// fmt.Println(string(p))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}()
|
||||
// validate te response
|
||||
t.Log("---Mutation---")
|
||||
validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource)
|
||||
validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse)
|
||||
|
||||
// pass the patched resource from mutate to validate
|
||||
if len(er.PolicyResponse.Rules) > 0 {
|
||||
resource = &er.PatchedResource
|
||||
}
|
||||
|
||||
// Validation
|
||||
er = engine.ValidateNew(*policy, *resource)
|
||||
func() {
|
||||
if data, err := json.Marshal(er.PolicyResponse); err == nil {
|
||||
t.Log(string(data))
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}()
|
||||
// validate the response
|
||||
t.Log("---Validation---")
|
||||
validateResponse(t, er.PolicyResponse, tc.Expected.Validation.PolicyResponse)
|
||||
return true
|
||||
}
|
||||
|
||||
func validateResource(t *testing.T, responseResource unstructured.Unstructured, expectedResourceFile string) {
|
||||
resourcePrint := func(obj unstructured.Unstructured) {
|
||||
t.Log("-----patched resource----")
|
||||
if data, err := obj.MarshalJSON(); err == nil {
|
||||
t.Log(string(data))
|
||||
}
|
||||
}
|
||||
resourcePrint(responseResource)
|
||||
if expectedResourceFile == "" {
|
||||
t.Log("expected resource file not specified, wont compare resources")
|
||||
return
|
||||
}
|
||||
// load expected resource
|
||||
expectedResource := loadPolicyResource(t, expectedResourceFile)
|
||||
if expectedResource == nil {
|
||||
t.Log("failed to get the expected resource")
|
||||
return
|
||||
}
|
||||
|
||||
// resourcePrint(*expectedResource)
|
||||
// compare the resources
|
||||
if !reflect.DeepEqual(responseResource, *expectedResource) {
|
||||
t.Log("failed: response resource returned does not match expected resource")
|
||||
}
|
||||
t.Log("success: response resource returned matches expected resource")
|
||||
}
|
||||
|
||||
func validateResponse(t *testing.T, er engine.PolicyResponse, expected engine.PolicyResponse) {
|
||||
if reflect.DeepEqual(expected, (engine.PolicyResponse{})) {
|
||||
t.Log("no response expected")
|
||||
return
|
||||
}
|
||||
// cant do deepEquals and the stats will be different, or we nil those fields and then do a comparison
|
||||
// forcing only the fields that are specified to be comprared
|
||||
|
||||
// doing a field by fields comparsion will allow us to provied more details logs and granular error reporting
|
||||
// check policy name is same :P
|
||||
if er.Policy != expected.Policy {
|
||||
t.Error("Policy: error")
|
||||
}
|
||||
// compare resource spec
|
||||
compareResourceSpec(t, er.Resource, expected.Resource)
|
||||
// // stats
|
||||
// if er.RulesAppliedCount != expected.RulesAppliedCount {
|
||||
// t.Log("RulesAppliedCount: error")
|
||||
// }
|
||||
// rules
|
||||
if len(er.Rules) != len(er.Rules) {
|
||||
t.Log("rule count: error")
|
||||
}
|
||||
if len(expected.Rules) == len(expected.Rules) {
|
||||
// if there are rules being applied then we compare the rule response
|
||||
// as the rules are applied in the order defined, the comparions of rules will be in order
|
||||
for index, r := range expected.Rules {
|
||||
compareRules(t, r, expected.Rules[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compareResourceSpec(t *testing.T, resource engine.ResourceSpec, expectedResource engine.ResourceSpec) {
|
||||
// kind
|
||||
if resource.Kind != expectedResource.Kind {
|
||||
t.Error("error: kind")
|
||||
}
|
||||
// // apiVersion
|
||||
// if resource.APIVersion != expectedResource.APIVersion {
|
||||
// t.Error("error: apiVersion")
|
||||
// }
|
||||
// namespace
|
||||
if resource.Namespace != expectedResource.Namespace {
|
||||
t.Error("error: namespace")
|
||||
}
|
||||
// name
|
||||
if resource.Name != expectedResource.Name {
|
||||
t.Error("error: name")
|
||||
}
|
||||
}
|
||||
|
||||
func compareRules(t *testing.T, rule engine.RuleResponse, expectedRule engine.RuleResponse) {
|
||||
// name
|
||||
if rule.Name != expectedRule.Name {
|
||||
t.Errorf("error rule %s: name", rule.Name)
|
||||
// as the rule names dont match no need to compare the rest of the information
|
||||
return
|
||||
}
|
||||
// type
|
||||
if rule.Type != expectedRule.Type {
|
||||
t.Error("error: type")
|
||||
}
|
||||
// message
|
||||
if rule.Message != expectedRule.Message {
|
||||
t.Error("error: message")
|
||||
}
|
||||
// // patches
|
||||
// if reflect.DeepEqual(rule.Patches, expectedRule.Patches) {
|
||||
// t.Log("error: patches")
|
||||
// }
|
||||
// success
|
||||
if rule.Success != expectedRule.Success {
|
||||
t.Error("error: success")
|
||||
}
|
||||
// statistics
|
||||
}
|
||||
|
||||
func loadPolicyResource(t *testing.T, file string) *unstructured.Unstructured {
|
||||
// expect only one resource to be specified in the YAML
|
||||
resources := loadResource(t, file)
|
||||
if resources == nil {
|
||||
t.Log("no resource specified")
|
||||
return nil
|
||||
}
|
||||
if len(resources) > 1 {
|
||||
t.Logf("more than one resource specified in the file %s", file)
|
||||
t.Log("considering the first one for policy application")
|
||||
}
|
||||
return resources[0]
|
||||
}
|
||||
|
||||
func getClient(t *testing.T, files []string) *client.Client {
|
||||
if files == nil {
|
||||
t.Log("no resources to load, not createing mock client")
|
||||
return nil
|
||||
}
|
||||
var objects []runtime.Object
|
||||
if files != nil {
|
||||
glog.V(4).Infof("loading resources:")
|
||||
for _, file := range files {
|
||||
objects = loadObjects(t, file)
|
||||
}
|
||||
}
|
||||
// create mock client
|
||||
scheme := runtime.NewScheme()
|
||||
// mock client expects the resource to be as runtime.Object
|
||||
c, err := client.NewMockClient(scheme, objects...)
|
||||
if err != nil {
|
||||
t.Errorf("failed to create client. %v", err)
|
||||
return nil
|
||||
}
|
||||
t.Log("created mock client with pre-loaded resources")
|
||||
return c
|
||||
}
|
||||
|
||||
func loadResource(t *testing.T, path string) []*unstructured.Unstructured {
|
||||
var unstrResources []*unstructured.Unstructured
|
||||
t.Logf("loading resource from %s", path)
|
||||
data, err := loadFile(t, path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rBytes := bytes.Split(data, []byte("---"))
|
||||
for _, r := range rBytes {
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, gvk, err := decode(r, nil, nil)
|
||||
if err != nil {
|
||||
t.Logf("failed to decode resource: %v", err)
|
||||
continue
|
||||
}
|
||||
glog.Info(gvk)
|
||||
|
||||
data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
|
||||
if err != nil {
|
||||
t.Logf("failed to unmarshall resource. %v", err)
|
||||
continue
|
||||
}
|
||||
unstr := unstructured.Unstructured{Object: data}
|
||||
t.Logf("loaded resource %s/%s/%s", unstr.GetKind(), unstr.GetNamespace(), unstr.GetName())
|
||||
unstrResources = append(unstrResources, &unstr)
|
||||
}
|
||||
return unstrResources
|
||||
}
|
||||
|
||||
func loadObjects(t *testing.T, path string) []runtime.Object {
|
||||
var resources []runtime.Object
|
||||
t.Logf("loading objects from %s", path)
|
||||
data, err := loadFile(t, path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
rBytes := bytes.Split(data, []byte("---"))
|
||||
for _, r := range rBytes {
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, gvk, err := decode(r, nil, nil)
|
||||
if err != nil {
|
||||
t.Logf("failed to decode resource: %v", err)
|
||||
continue
|
||||
}
|
||||
t.Log(gvk)
|
||||
//TODO: add more details
|
||||
t.Logf("loaded object %s", gvk.Kind)
|
||||
resources = append(resources, obj)
|
||||
}
|
||||
return resources
|
||||
|
||||
}
|
||||
|
||||
func loadPolicy(t *testing.T, path string) *kyverno.Policy {
|
||||
t.Logf("loading policy from %s", path)
|
||||
data, err := loadFile(t, path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var policies []*kyverno.Policy
|
||||
pBytes := bytes.Split(data, []byte("---"))
|
||||
for _, p := range pBytes {
|
||||
policy := kyverno.Policy{}
|
||||
pBytes, err := apiyaml.ToJSON(p)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(pBytes, &policy); err != nil {
|
||||
t.Logf("failed to marshall polic. %v", err)
|
||||
continue
|
||||
}
|
||||
t.Logf("loaded policy %s", policy.Name)
|
||||
policies = append(policies, &policy)
|
||||
}
|
||||
|
||||
if len(policies) == 0 {
|
||||
t.Log("no policies loaded")
|
||||
return nil
|
||||
}
|
||||
if len(policies) > 1 {
|
||||
t.Log("more than one policy defined, considering first for processing")
|
||||
}
|
||||
return policies[0]
|
||||
}
|
||||
|
||||
func testScenario(t *testing.T, path string) {
|
||||
//load scenario
|
||||
scenario, err := loadScenario(t, path)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
runScenario(t, scenario)
|
||||
}
|
|
@ -1,258 +1,258 @@
|
|||
package testrunner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
// import (
|
||||
// "fmt"
|
||||
// "reflect"
|
||||
// "strconv"
|
||||
// "testing"
|
||||
|
||||
ospath "path"
|
||||
// ospath "path"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
// "github.com/golang/glog"
|
||||
// kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
// client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
// "github.com/nirmata/kyverno/pkg/engine"
|
||||
// // "github.com/nirmata/kyverno/pkg/info"
|
||||
// kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// )
|
||||
|
||||
type test struct {
|
||||
ap string
|
||||
t *testing.T
|
||||
testCase *testCase
|
||||
// input
|
||||
policy *kyverno.Policy
|
||||
tResource *resourceInfo
|
||||
loadResources []*resourceInfo
|
||||
// expected
|
||||
genResources []*resourceInfo
|
||||
patchedResource *resourceInfo
|
||||
}
|
||||
// type test struct {
|
||||
// ap string
|
||||
// t *testing.T
|
||||
// testCase *testCase
|
||||
// // input
|
||||
// policy *kyverno.Policy
|
||||
// tResource *resourceInfo
|
||||
// loadResources []*resourceInfo
|
||||
// // expected
|
||||
// genResources []*resourceInfo
|
||||
// patchedResource *resourceInfo
|
||||
// }
|
||||
|
||||
func (t *test) run() {
|
||||
var client *client.Client
|
||||
var err error
|
||||
//mock client is used if generate is defined
|
||||
if t.testCase.Expected.Generation != nil {
|
||||
// create mock client & load resources
|
||||
client, err = createClient(t.loadResources)
|
||||
if err != nil {
|
||||
t.t.Errorf("Unable to create client. err %s", err)
|
||||
}
|
||||
// TODO: handle generate
|
||||
// assuming its namespaces creation
|
||||
decode := kscheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, _, err := decode([]byte(t.tResource.rawResource), nil, nil)
|
||||
_, err = client.CreateResource(t.tResource.gvk.Kind, "", obj, false)
|
||||
if err != nil {
|
||||
t.t.Errorf("error while creating namespace %s", err)
|
||||
}
|
||||
// func (t *test) run() {
|
||||
// var client *client.Client
|
||||
// var err error
|
||||
// //mock client is used if generate is defined
|
||||
// if t.testCase.Expected.Generation != nil {
|
||||
// // create mock client & load resources
|
||||
// client, err = createClient(t.loadResources)
|
||||
// if err != nil {
|
||||
// t.t.Errorf("Unable to create client. err %s", err)
|
||||
// }
|
||||
// // TODO: handle generate
|
||||
// // assuming its namespaces creation
|
||||
// decode := kscheme.Codecs.UniversalDeserializer().Decode
|
||||
// obj, _, err := decode([]byte(t.tResource.rawResource), nil, nil)
|
||||
// _, err = client.CreateResource(t.tResource.gvk.Kind, "", obj, false)
|
||||
// if err != nil {
|
||||
// t.t.Errorf("error while creating namespace %s", err)
|
||||
// }
|
||||
|
||||
}
|
||||
// apply the policy engine
|
||||
pr, policyInfo, err := t.applyPolicy(t.policy, t.tResource, client)
|
||||
if err != nil {
|
||||
t.t.Error(err)
|
||||
return
|
||||
}
|
||||
// Expected Result
|
||||
// Test succesfuly ?
|
||||
t.overAllPass(policyInfo.IsSuccessful(), t.testCase.Expected.Passes)
|
||||
t.checkMutationResult(pr, policyInfo)
|
||||
t.checkValidationResult(policyInfo)
|
||||
t.checkGenerationResult(client, policyInfo)
|
||||
}
|
||||
// }
|
||||
// // apply the policy engine
|
||||
// pr, policyInfo, err := t.applyPolicy(t.policy, t.tResource, client)
|
||||
// if err != nil {
|
||||
// t.t.Error(err)
|
||||
// return
|
||||
// }
|
||||
// // Expected Result
|
||||
// // Test succesfuly ?
|
||||
// t.overAllPass(policyInfo.IsSuccessful(), t.testCase.Expected.Passes)
|
||||
// t.checkMutationResult(pr, policyInfo)
|
||||
// t.checkValidationResult(policyInfo)
|
||||
// t.checkGenerationResult(client, policyInfo)
|
||||
// }
|
||||
|
||||
func (t *test) checkMutationResult(pr *resourceInfo, policyInfo info.PolicyInfo) {
|
||||
if t.testCase.Expected.Mutation == nil {
|
||||
glog.Info("No Mutation check defined")
|
||||
return
|
||||
}
|
||||
// patched resource
|
||||
if !compareResource(pr, t.patchedResource) {
|
||||
fmt.Println(string(t.patchedResource.rawResource))
|
||||
fmt.Println(string(pr.rawResource))
|
||||
glog.Warningf("Expected resource %s ", string(pr.rawResource))
|
||||
t.t.Error("Patched resources not as expected")
|
||||
}
|
||||
// func (t *test) checkMutationResult(pr *resourceInfo, policyInfo info.PolicyInfo) {
|
||||
// if t.testCase.Expected.Mutation == nil {
|
||||
// glog.Info("No Mutation check defined")
|
||||
// return
|
||||
// }
|
||||
// // patched resource
|
||||
// if !compareResource(pr, t.patchedResource) {
|
||||
// fmt.Println(string(t.patchedResource.rawResource))
|
||||
// fmt.Println(string(pr.rawResource))
|
||||
// glog.Warningf("Expected resource %s ", string(pr.rawResource))
|
||||
// t.t.Error("Patched resources not as expected")
|
||||
// }
|
||||
|
||||
// check if rules match
|
||||
t.compareRules(policyInfo.Rules, t.testCase.Expected.Mutation.Rules)
|
||||
}
|
||||
// // check if rules match
|
||||
// t.compareRules(policyInfo.Rules, t.testCase.Expected.Mutation.Rules)
|
||||
// }
|
||||
|
||||
func (t *test) overAllPass(result bool, expected string) {
|
||||
b, err := strconv.ParseBool(expected)
|
||||
if err != nil {
|
||||
t.t.Error(err)
|
||||
}
|
||||
if result != b {
|
||||
t.t.Errorf("Expected value %v and actual value %v dont match", expected, result)
|
||||
}
|
||||
}
|
||||
// func (t *test) overAllPass(result bool, expected string) {
|
||||
// b, err := strconv.ParseBool(expected)
|
||||
// if err != nil {
|
||||
// t.t.Error(err)
|
||||
// }
|
||||
// if result != b {
|
||||
// t.t.Errorf("Expected value %v and actual value %v dont match", expected, result)
|
||||
// }
|
||||
// }
|
||||
|
||||
func (t *test) compareRules(ruleInfos []info.RuleInfo, rules []tRules) {
|
||||
// Compare the rules specified in the expected against the actual rule info returned by the apply policy
|
||||
for _, eRule := range rules {
|
||||
// Look-up the rule from the policy info
|
||||
rule := lookUpRule(eRule.Name, ruleInfos)
|
||||
if reflect.DeepEqual(rule, info.RuleInfo{}) {
|
||||
t.t.Errorf("Rule with name %s not found", eRule.Name)
|
||||
continue
|
||||
}
|
||||
// get the corresponding rule
|
||||
if rule.Name != eRule.Name {
|
||||
t.t.Errorf("Rule Name not matching!. expected %s , actual %s", eRule.Name, rule.Name)
|
||||
}
|
||||
if rule.RuleType.String() != eRule.Type {
|
||||
t.t.Errorf("Rule type mismatch!. expected %s, actual %s", eRule.Type, rule.RuleType.String())
|
||||
}
|
||||
if len(eRule.Messages) != len(rule.Msgs) {
|
||||
t.t.Errorf("Number of rule messages not same. expected %d, actual %d", len(eRule.Messages), len(rule.Msgs))
|
||||
}
|
||||
for i, msg := range eRule.Messages {
|
||||
if msg != rule.Msgs[i] {
|
||||
t.t.Errorf("Messges dont match!. expected %s, actual %s", msg, rule.Msgs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// func (t *test) compareRules(ruleInfos []info.RuleInfo, rules []tRules) {
|
||||
// // Compare the rules specified in the expected against the actual rule info returned by the apply policy
|
||||
// for _, eRule := range rules {
|
||||
// // Look-up the rule from the policy info
|
||||
// rule := lookUpRule(eRule.Name, ruleInfos)
|
||||
// if reflect.DeepEqual(rule, info.RuleInfo{}) {
|
||||
// t.t.Errorf("Rule with name %s not found", eRule.Name)
|
||||
// continue
|
||||
// }
|
||||
// // get the corresponding rule
|
||||
// if rule.Name != eRule.Name {
|
||||
// t.t.Errorf("Rule Name not matching!. expected %s , actual %s", eRule.Name, rule.Name)
|
||||
// }
|
||||
// if rule.RuleType.String() != eRule.Type {
|
||||
// t.t.Errorf("Rule type mismatch!. expected %s, actual %s", eRule.Type, rule.RuleType.String())
|
||||
// }
|
||||
// if len(eRule.Messages) != len(rule.Msgs) {
|
||||
// t.t.Errorf("Number of rule messages not same. expected %d, actual %d", len(eRule.Messages), len(rule.Msgs))
|
||||
// }
|
||||
// for i, msg := range eRule.Messages {
|
||||
// if msg != rule.Msgs[i] {
|
||||
// t.t.Errorf("Messges dont match!. expected %s, actual %s", msg, rule.Msgs[i])
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
func lookUpRule(name string, ruleInfos []info.RuleInfo) info.RuleInfo {
|
||||
// func lookUpRule(name string, ruleInfos []info.RuleInfo) info.RuleInfo {
|
||||
|
||||
for _, r := range ruleInfos {
|
||||
if r.Name == name {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return info.RuleInfo{}
|
||||
}
|
||||
// for _, r := range ruleInfos {
|
||||
// if r.Name == name {
|
||||
// return r
|
||||
// }
|
||||
// }
|
||||
// return info.RuleInfo{}
|
||||
// }
|
||||
|
||||
func (t *test) checkValidationResult(policyInfo info.PolicyInfo) {
|
||||
if t.testCase.Expected.Validation == nil {
|
||||
glog.Info("No Validation check defined")
|
||||
return
|
||||
}
|
||||
// func (t *test) checkValidationResult(policyInfo info.PolicyInfo) {
|
||||
// if t.testCase.Expected.Validation == nil {
|
||||
// glog.Info("No Validation check defined")
|
||||
// return
|
||||
// }
|
||||
|
||||
// check if rules match
|
||||
t.compareRules(policyInfo.Rules, t.testCase.Expected.Validation.Rules)
|
||||
}
|
||||
// // check if rules match
|
||||
// t.compareRules(policyInfo.Rules, t.testCase.Expected.Validation.Rules)
|
||||
// }
|
||||
|
||||
func (t *test) checkGenerationResult(client *client.Client, policyInfo info.PolicyInfo) {
|
||||
if t.testCase.Expected.Generation == nil {
|
||||
glog.Info("No Generate check defined")
|
||||
return
|
||||
}
|
||||
if client == nil {
|
||||
t.t.Error("client needs to be configured")
|
||||
}
|
||||
// func (t *test) checkGenerationResult(client *client.Client, policyInfo info.PolicyInfo) {
|
||||
// if t.testCase.Expected.Generation == nil {
|
||||
// glog.Info("No Generate check defined")
|
||||
// return
|
||||
// }
|
||||
// if client == nil {
|
||||
// t.t.Error("client needs to be configured")
|
||||
// }
|
||||
|
||||
// check if rules match
|
||||
t.compareRules(policyInfo.Rules, t.testCase.Expected.Generation.Rules)
|
||||
// // check if rules match
|
||||
// t.compareRules(policyInfo.Rules, t.testCase.Expected.Generation.Rules)
|
||||
|
||||
// check if the expected resources are generated
|
||||
for _, r := range t.genResources {
|
||||
n := ParseNameFromObject(r.rawResource)
|
||||
ns := ParseNamespaceFromObject(r.rawResource)
|
||||
_, err := client.GetResource(r.gvk.Kind, ns, n)
|
||||
if err != nil {
|
||||
t.t.Errorf("Resource %s/%s of kinf %s not found", ns, n, r.gvk.Kind)
|
||||
}
|
||||
// compare if the resources are same
|
||||
//TODO: comapre []bytes vs unstrcutured resource
|
||||
}
|
||||
}
|
||||
// // check if the expected resources are generated
|
||||
// for _, r := range t.genResources {
|
||||
// n := ParseNameFromObject(r.rawResource)
|
||||
// ns := ParseNamespaceFromObject(r.rawResource)
|
||||
// _, err := client.GetResource(r.gvk.Kind, ns, n)
|
||||
// if err != nil {
|
||||
// t.t.Errorf("Resource %s/%s of kinf %s not found", ns, n, r.gvk.Kind)
|
||||
// }
|
||||
// // compare if the resources are same
|
||||
// //TODO: comapre []bytes vs unstrcutured resource
|
||||
// }
|
||||
// }
|
||||
|
||||
func (t *test) applyPolicy(policy *kyverno.Policy,
|
||||
tresource *resourceInfo,
|
||||
client *client.Client) (*resourceInfo, info.PolicyInfo, error) {
|
||||
// apply policy on the trigger resource
|
||||
// Mutate
|
||||
var zeroPolicyInfo info.PolicyInfo
|
||||
var err error
|
||||
rawResource := tresource.rawResource
|
||||
rname := engine.ParseNameFromObject(rawResource)
|
||||
rns := engine.ParseNamespaceFromObject(rawResource)
|
||||
rkind := engine.ParseKindFromObject(rawResource)
|
||||
policyInfo := info.NewPolicyInfo(policy.Name,
|
||||
rkind,
|
||||
rname,
|
||||
rns,
|
||||
policy.Spec.ValidationFailureAction)
|
||||
// func (t *test) applyPolicy(policy *kyverno.Policy,
|
||||
// tresource *resourceInfo,
|
||||
// client *client.Client) (*resourceInfo, info.PolicyInfo, error) {
|
||||
// // apply policy on the trigger resource
|
||||
// // Mutate
|
||||
// var zeroPolicyInfo info.PolicyInfo
|
||||
// var err error
|
||||
// rawResource := tresource.rawResource
|
||||
// rname := engine.ParseNameFromObject(rawResource)
|
||||
// rns := engine.ParseNamespaceFromObject(rawResource)
|
||||
// rkind := engine.ParseKindFromObject(rawResource)
|
||||
// policyInfo := info.NewPolicyInfo(policy.Name,
|
||||
// rkind,
|
||||
// rname,
|
||||
// rns,
|
||||
// policy.Spec.ValidationFailureAction)
|
||||
|
||||
resource, err := ConvertToUnstructured(rawResource)
|
||||
if err != nil {
|
||||
return nil, zeroPolicyInfo, err
|
||||
}
|
||||
// resource, err := ConvertToUnstructured(rawResource)
|
||||
// if err != nil {
|
||||
// return nil, zeroPolicyInfo, err
|
||||
// }
|
||||
|
||||
// Apply Mutation Rules
|
||||
engineResponse := engine.Mutate(*policy, *resource)
|
||||
// patches, ruleInfos := engine.Mutate(*policy, rawResource, *tresource.gvk)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
// TODO: only validate if there are no errors in mutate, why?
|
||||
if policyInfo.IsSuccessful() {
|
||||
if len(engineResponse.Patches) != 0 {
|
||||
rawResource, err = engine.ApplyPatches(rawResource, engineResponse.Patches)
|
||||
if err != nil {
|
||||
return nil, zeroPolicyInfo, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// Validate
|
||||
engineResponse = engine.Validate(*policy, *resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
if err != nil {
|
||||
return nil, zeroPolicyInfo, err
|
||||
}
|
||||
// // Apply Mutation Rules
|
||||
// engineResponse := engine.Mutate(*policy, *resource)
|
||||
// // patches, ruleInfos := engine.Mutate(*policy, rawResource, *tresource.gvk)
|
||||
// policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
// // TODO: only validate if there are no errors in mutate, why?
|
||||
// if policyInfo.IsSuccessful() {
|
||||
// if len(engineResponse.Patches) != 0 {
|
||||
// rawResource, err = engine.ApplyPatches(rawResource, engineResponse.Patches)
|
||||
// if err != nil {
|
||||
// return nil, zeroPolicyInfo, err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // Validate
|
||||
// engineResponse = engine.Validate(*policy, *resource)
|
||||
// policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
// if err != nil {
|
||||
// return nil, zeroPolicyInfo, err
|
||||
// }
|
||||
|
||||
if rkind == "Namespace" {
|
||||
if client != nil {
|
||||
engineResponse := engine.Generate(client, *policy, *resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
}
|
||||
}
|
||||
// Generate
|
||||
// transform the patched Resource into resource Info
|
||||
ri, err := extractResourceRaw(rawResource)
|
||||
if err != nil {
|
||||
return nil, zeroPolicyInfo, err
|
||||
}
|
||||
// return the results
|
||||
return ri, policyInfo, nil
|
||||
}
|
||||
// if rkind == "Namespace" {
|
||||
// if client != nil {
|
||||
// engineResponse := engine.Generate(client, *policy, *resource)
|
||||
// policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
// }
|
||||
// }
|
||||
// // Generate
|
||||
// // transform the patched Resource into resource Info
|
||||
// ri, err := extractResourceRaw(rawResource)
|
||||
// if err != nil {
|
||||
// return nil, zeroPolicyInfo, err
|
||||
// }
|
||||
// // return the results
|
||||
// return ri, policyInfo, nil
|
||||
// }
|
||||
|
||||
func NewTest(ap string, t *testing.T, tc *testCase) (*test, error) {
|
||||
//---INPUT---
|
||||
p, err := tc.loadPolicy(ospath.Join(ap, tc.Input.Policy))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := tc.loadTriggerResource(ap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// func NewTest(ap string, t *testing.T, tc *testCase) (*test, error) {
|
||||
// //---INPUT---
|
||||
// p, err := tc.loadPolicy(ospath.Join(ap, tc.Input.Policy))
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// r, err := tc.loadTriggerResource(ap)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
lr, err := tc.loadPreloadedResources(ap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// lr, err := tc.loadPreloadedResources(ap)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
//---EXPECTED---
|
||||
pr, err := tc.loadPatchedResource(ap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gr, err := tc.loadGeneratedResources(ap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &test{
|
||||
ap: ap,
|
||||
t: t,
|
||||
testCase: tc,
|
||||
policy: p,
|
||||
tResource: r,
|
||||
loadResources: lr,
|
||||
genResources: gr,
|
||||
patchedResource: pr,
|
||||
}, nil
|
||||
}
|
||||
// //---EXPECTED---
|
||||
// pr, err := tc.loadPatchedResource(ap)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// gr, err := tc.loadGeneratedResources(ap)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return &test{
|
||||
// ap: ap,
|
||||
// t: t,
|
||||
// testCase: tc,
|
||||
// policy: p,
|
||||
// tResource: r,
|
||||
// loadResources: lr,
|
||||
// genResources: gr,
|
||||
// patchedResource: pr,
|
||||
// }, nil
|
||||
// }
|
||||
|
|
|
@ -1,186 +1,186 @@
|
|||
package testrunner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
ospath "path"
|
||||
// import (
|
||||
// "bytes"
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// ospath "path"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
yaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
// "github.com/golang/glog"
|
||||
// kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
// "k8s.io/apimachinery/pkg/runtime"
|
||||
// yaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
// "k8s.io/client-go/kubernetes/scheme"
|
||||
// )
|
||||
|
||||
//testCase defines the input and the expected result
|
||||
// it stores the path to the files that are to be loaded
|
||||
// for references
|
||||
type testCase struct {
|
||||
Input *tInput `yaml:"input"`
|
||||
Expected *tExpected `yaml:"expected"`
|
||||
}
|
||||
// //testCase defines the input and the expected result
|
||||
// // it stores the path to the files that are to be loaded
|
||||
// // for references
|
||||
// type testCase struct {
|
||||
// Input *tInput `yaml:"input"`
|
||||
// Expected *tExpected `yaml:"expected"`
|
||||
// }
|
||||
|
||||
// load resources store the resources that are pre-requisite
|
||||
// for the test case and are pre-loaded in the client before
|
||||
/// test case in evaluated
|
||||
type tInput struct {
|
||||
Policy string `yaml:"policy"`
|
||||
Resource string `yaml:"resource"`
|
||||
LoadResources []string `yaml:"load_resources,omitempty"`
|
||||
}
|
||||
// // load resources store the resources that are pre-requisite
|
||||
// // for the test case and are pre-loaded in the client before
|
||||
// /// test case in evaluated
|
||||
// type tInput struct {
|
||||
// Policy string `yaml:"policy"`
|
||||
// Resource string `yaml:"resource"`
|
||||
// LoadResources []string `yaml:"load_resources,omitempty"`
|
||||
// }
|
||||
|
||||
type tExpected struct {
|
||||
Passes string `yaml:"passes"`
|
||||
Mutation *tMutation `yaml:"mutation,omitempty"`
|
||||
Validation *tValidation `yaml:"validation,omitempty"`
|
||||
Generation *tGeneration `yaml:"generation,omitempty"`
|
||||
}
|
||||
// type tExpected struct {
|
||||
// Passes string `yaml:"passes"`
|
||||
// Mutation *tMutation `yaml:"mutation,omitempty"`
|
||||
// Validation *tValidation `yaml:"validation,omitempty"`
|
||||
// Generation *tGeneration `yaml:"generation,omitempty"`
|
||||
// }
|
||||
|
||||
type tMutation struct {
|
||||
PatchedResource string `yaml:"patched_resource,omitempty"`
|
||||
Rules []tRules `yaml:"rules"`
|
||||
}
|
||||
// type tMutation struct {
|
||||
// PatchedResource string `yaml:"patched_resource,omitempty"`
|
||||
// Rules []tRules `yaml:"rules"`
|
||||
// }
|
||||
|
||||
type tValidation struct {
|
||||
Rules []tRules `yaml:"rules"`
|
||||
}
|
||||
// type tValidation struct {
|
||||
// Rules []tRules `yaml:"rules"`
|
||||
// }
|
||||
|
||||
type tGeneration struct {
|
||||
Resources []string `yaml:"resources"`
|
||||
Rules []tRules `yaml:"rules"`
|
||||
}
|
||||
// type tGeneration struct {
|
||||
// Resources []string `yaml:"resources"`
|
||||
// Rules []tRules `yaml:"rules"`
|
||||
// }
|
||||
|
||||
type tRules struct {
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Messages []string `yaml:"messages"`
|
||||
}
|
||||
// type tRules struct {
|
||||
// Name string `yaml:"name"`
|
||||
// Type string `yaml:"type"`
|
||||
// Messages []string `yaml:"messages"`
|
||||
// }
|
||||
|
||||
type tResult struct {
|
||||
Reason string `yaml:"reason, omitempty"`
|
||||
}
|
||||
// type tResult struct {
|
||||
// Reason string `yaml:"reason, omitempty"`
|
||||
// }
|
||||
|
||||
func (tc *testCase) policyEngineTest() {
|
||||
// func (tc *testCase) policyEngineTest() {
|
||||
|
||||
}
|
||||
func (tc *testCase) loadPreloadedResources(ap string) ([]*resourceInfo, error) {
|
||||
return loadResources(ap, tc.Input.LoadResources...)
|
||||
// return loadResources(ap, tc.Input.LoadResources...)
|
||||
}
|
||||
// }
|
||||
// func (tc *testCase) loadPreloadedResources(ap string) ([]*resourceInfo, error) {
|
||||
// return loadResources(ap, tc.Input.LoadResources...)
|
||||
// // return loadResources(ap, tc.Input.LoadResources...)
|
||||
// }
|
||||
|
||||
func (tc *testCase) loadGeneratedResources(ap string) ([]*resourceInfo, error) {
|
||||
if tc.Expected.Generation == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return loadResources(ap, tc.Expected.Generation.Resources...)
|
||||
}
|
||||
// func (tc *testCase) loadGeneratedResources(ap string) ([]*resourceInfo, error) {
|
||||
// if tc.Expected.Generation == nil {
|
||||
// return nil, nil
|
||||
// }
|
||||
// return loadResources(ap, tc.Expected.Generation.Resources...)
|
||||
// }
|
||||
|
||||
func (tc *testCase) loadPatchedResource(ap string) (*resourceInfo, error) {
|
||||
if tc.Expected.Mutation == nil {
|
||||
return nil, nil
|
||||
}
|
||||
rs, err := loadResources(ap, tc.Expected.Mutation.PatchedResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rs) != 1 {
|
||||
glog.Warning("expects single resource mutation but multiple defined, will use first one")
|
||||
}
|
||||
return rs[0], nil
|
||||
// func (tc *testCase) loadPatchedResource(ap string) (*resourceInfo, error) {
|
||||
// if tc.Expected.Mutation == nil {
|
||||
// return nil, nil
|
||||
// }
|
||||
// rs, err := loadResources(ap, tc.Expected.Mutation.PatchedResource)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if len(rs) != 1 {
|
||||
// glog.Warning("expects single resource mutation but multiple defined, will use first one")
|
||||
// }
|
||||
// return rs[0], nil
|
||||
|
||||
}
|
||||
func (tc *testCase) loadResources(files []string) ([]*resourceInfo, error) {
|
||||
lr := []*resourceInfo{}
|
||||
for _, r := range files {
|
||||
rs, err := loadResources(r)
|
||||
if err != nil {
|
||||
// return as test case will be invalid if a resource cannot be loaded
|
||||
return nil, err
|
||||
}
|
||||
lr = append(lr, rs...)
|
||||
}
|
||||
return lr, nil
|
||||
}
|
||||
// }
|
||||
// func (tc *testCase) loadResources(files []string) ([]*resourceInfo, error) {
|
||||
// lr := []*resourceInfo{}
|
||||
// for _, r := range files {
|
||||
// rs, err := loadResources(r)
|
||||
// if err != nil {
|
||||
// // return as test case will be invalid if a resource cannot be loaded
|
||||
// return nil, err
|
||||
// }
|
||||
// lr = append(lr, rs...)
|
||||
// }
|
||||
// return lr, nil
|
||||
// }
|
||||
|
||||
func (tc *testCase) loadTriggerResource(ap string) (*resourceInfo, error) {
|
||||
rs, err := loadResources(ap, tc.Input.Resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rs) != 1 {
|
||||
glog.Warning("expects single resource trigger but multiple defined, will use first one")
|
||||
}
|
||||
return rs[0], nil
|
||||
// func (tc *testCase) loadTriggerResource(ap string) (*resourceInfo, error) {
|
||||
// rs, err := loadResources(ap, tc.Input.Resource)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if len(rs) != 1 {
|
||||
// glog.Warning("expects single resource trigger but multiple defined, will use first one")
|
||||
// }
|
||||
// return rs[0], nil
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
// Loads a single policy
|
||||
func (tc *testCase) loadPolicy(file string) (*kyverno.Policy, error) {
|
||||
p := &kyverno.Policy{}
|
||||
data, err := LoadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pBytes, err := yaml.ToJSON(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(pBytes, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.TypeMeta.Kind != "Policy" {
|
||||
return nil, fmt.Errorf("failed to parse policy")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
// // Loads a single policy
|
||||
// func (tc *testCase) loadPolicy(file string) (*kyverno.Policy, error) {
|
||||
// p := &kyverno.Policy{}
|
||||
// data, err := LoadFile(file)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// pBytes, err := yaml.ToJSON(data)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if err := json.Unmarshal(pBytes, p); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if p.TypeMeta.Kind != "Policy" {
|
||||
// return nil, fmt.Errorf("failed to parse policy")
|
||||
// }
|
||||
// return p, nil
|
||||
// }
|
||||
|
||||
// loads multiple resources
|
||||
func loadResources(ap string, args ...string) ([]*resourceInfo, error) {
|
||||
ris := []*resourceInfo{}
|
||||
for _, file := range args {
|
||||
data, err := LoadFile(ospath.Join(ap, file))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dd := bytes.Split(data, []byte(defaultYamlSeparator))
|
||||
// resources seperated by yaml seperator
|
||||
for _, d := range dd {
|
||||
ri, err := extractResourceRaw(d)
|
||||
if err != nil {
|
||||
glog.Errorf("unable to load resource. err: %s ", err)
|
||||
continue
|
||||
}
|
||||
ris = append(ris, ri)
|
||||
}
|
||||
}
|
||||
return ris, nil
|
||||
}
|
||||
// // loads multiple resources
|
||||
// func loadResources(ap string, args ...string) ([]*resourceInfo, error) {
|
||||
// ris := []*resourceInfo{}
|
||||
// for _, file := range args {
|
||||
// data, err := LoadFile(ospath.Join(ap, file))
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// dd := bytes.Split(data, []byte(defaultYamlSeparator))
|
||||
// // resources seperated by yaml seperator
|
||||
// for _, d := range dd {
|
||||
// ri, err := extractResourceRaw(d)
|
||||
// if err != nil {
|
||||
// glog.Errorf("unable to load resource. err: %s ", err)
|
||||
// continue
|
||||
// }
|
||||
// ris = append(ris, ri)
|
||||
// }
|
||||
// }
|
||||
// return ris, nil
|
||||
// }
|
||||
|
||||
func extractResourceRaw(d []byte) (*resourceInfo, error) {
|
||||
// decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
// obj, gvk, err := decode(d, nil, nil)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
obj, gvk, err := extractResourceUnMarshalled(d)
|
||||
// runtime.object to JSON
|
||||
raw, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resourceInfo{rawResource: raw,
|
||||
gvk: gvk}, nil
|
||||
}
|
||||
// func extractResourceRaw(d []byte) (*resourceInfo, error) {
|
||||
// // decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
// // obj, gvk, err := decode(d, nil, nil)
|
||||
// // if err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// obj, gvk, err := extractResourceUnMarshalled(d)
|
||||
// // runtime.object to JSON
|
||||
// raw, err := json.Marshal(obj)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return &resourceInfo{rawResource: raw,
|
||||
// gvk: gvk}, nil
|
||||
// }
|
||||
|
||||
func extractResourceUnMarshalled(d []byte) (runtime.Object, *metav1.GroupVersionKind, error) {
|
||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, gvk, err := decode(d, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return obj, &metav1.GroupVersionKind{Group: gvk.Group,
|
||||
Version: gvk.Version,
|
||||
Kind: gvk.Kind}, nil
|
||||
}
|
||||
// func extractResourceUnMarshalled(d []byte) (runtime.Object, *metav1.GroupVersionKind, error) {
|
||||
// decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||
// obj, gvk, err := decode(d, nil, nil)
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// return obj, &metav1.GroupVersionKind{Group: gvk.Group,
|
||||
// Version: gvk.Version,
|
||||
// Kind: gvk.Kind}, nil
|
||||
// }
|
||||
|
|
|
@ -1,98 +1,98 @@
|
|||
package testrunner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
// import (
|
||||
// "bytes"
|
||||
// "io/ioutil"
|
||||
// "path/filepath"
|
||||
// "testing"
|
||||
|
||||
"os"
|
||||
ospath "path"
|
||||
// "os"
|
||||
// ospath "path"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
// "github.com/golang/glog"
|
||||
// "gopkg.in/yaml.v2"
|
||||
// )
|
||||
|
||||
func runner(t *testing.T, relpath string) {
|
||||
gp := os.Getenv("GOPATH")
|
||||
ap := ospath.Join(gp, projectPath)
|
||||
// build load scenarios
|
||||
path := ospath.Join(ap, relpath)
|
||||
// Load the scenario files
|
||||
scenarioFiles := getYAMLfiles(path)
|
||||
for _, secenarioFile := range scenarioFiles {
|
||||
sc := newScenario(t, ap, secenarioFile)
|
||||
if err := sc.load(); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
// run test cases
|
||||
sc.run()
|
||||
}
|
||||
}
|
||||
// func runner(t *testing.T, relpath string) {
|
||||
// gp := os.Getenv("GOPATH")
|
||||
// ap := ospath.Join(gp, projectPath)
|
||||
// // build load scenarios
|
||||
// path := ospath.Join(ap, relpath)
|
||||
// // Load the scenario files
|
||||
// scenarioFiles := getYAMLfiles(path)
|
||||
// for _, secenarioFile := range scenarioFiles {
|
||||
// sc := newScenario(t, ap, secenarioFile)
|
||||
// if err := sc.load(); err != nil {
|
||||
// t.Error(err)
|
||||
// return
|
||||
// }
|
||||
// // run test cases
|
||||
// sc.run()
|
||||
// }
|
||||
// }
|
||||
|
||||
type scenario struct {
|
||||
ap string
|
||||
t *testing.T
|
||||
path string
|
||||
tcs []*testCase
|
||||
}
|
||||
// type scenario struct {
|
||||
// ap string
|
||||
// t *testing.T
|
||||
// path string
|
||||
// tcs []*testCase
|
||||
// }
|
||||
|
||||
func newScenario(t *testing.T, ap string, path string) *scenario {
|
||||
return &scenario{
|
||||
ap: ap,
|
||||
t: t,
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
// func newScenario(t *testing.T, ap string, path string) *scenario {
|
||||
// return &scenario{
|
||||
// ap: ap,
|
||||
// t: t,
|
||||
// path: path,
|
||||
// }
|
||||
// }
|
||||
|
||||
func getYAMLfiles(path string) (yamls []string) {
|
||||
fileInfo, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, file := range fileInfo {
|
||||
if filepath.Ext(file.Name()) == ".yml" || filepath.Ext(file.Name()) == ".yaml" {
|
||||
yamls = append(yamls, ospath.Join(path, file.Name()))
|
||||
}
|
||||
}
|
||||
return yamls
|
||||
}
|
||||
func (sc *scenario) load() error {
|
||||
// read file
|
||||
data, err := LoadFile(sc.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tcs := []*testCase{}
|
||||
// load test cases seperated by '---'
|
||||
// each test case defines an input & expected result
|
||||
dd := bytes.Split(data, []byte(defaultYamlSeparator))
|
||||
for _, d := range dd {
|
||||
tc := &testCase{}
|
||||
err := yaml.Unmarshal([]byte(d), tc)
|
||||
if err != nil {
|
||||
glog.Warningf("Error while decoding YAML object, err: %s", err)
|
||||
continue
|
||||
}
|
||||
tcs = append(tcs, tc)
|
||||
}
|
||||
sc.tcs = tcs
|
||||
return nil
|
||||
}
|
||||
// func getYAMLfiles(path string) (yamls []string) {
|
||||
// fileInfo, err := ioutil.ReadDir(path)
|
||||
// if err != nil {
|
||||
// return nil
|
||||
// }
|
||||
// for _, file := range fileInfo {
|
||||
// if filepath.Ext(file.Name()) == ".yml" || filepath.Ext(file.Name()) == ".yaml" {
|
||||
// yamls = append(yamls, ospath.Join(path, file.Name()))
|
||||
// }
|
||||
// }
|
||||
// return yamls
|
||||
// }
|
||||
// func (sc *scenario) load() error {
|
||||
// // read file
|
||||
// data, err := LoadFile(sc.path)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// tcs := []*testCase{}
|
||||
// // load test cases seperated by '---'
|
||||
// // each test case defines an input & expected result
|
||||
// dd := bytes.Split(data, []byte(defaultYamlSeparator))
|
||||
// for _, d := range dd {
|
||||
// tc := &testCase{}
|
||||
// err := yaml.Unmarshal([]byte(d), tc)
|
||||
// if err != nil {
|
||||
// glog.Warningf("Error while decoding YAML object, err: %s", err)
|
||||
// continue
|
||||
// }
|
||||
// tcs = append(tcs, tc)
|
||||
// }
|
||||
// sc.tcs = tcs
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (sc *scenario) run() {
|
||||
if len(sc.tcs) == 0 {
|
||||
sc.t.Error("No test cases to load")
|
||||
return
|
||||
}
|
||||
// func (sc *scenario) run() {
|
||||
// if len(sc.tcs) == 0 {
|
||||
// sc.t.Error("No test cases to load")
|
||||
// return
|
||||
// }
|
||||
|
||||
for _, tc := range sc.tcs {
|
||||
t, err := NewTest(sc.ap, sc.t, tc)
|
||||
if err != nil {
|
||||
sc.t.Error(err)
|
||||
continue
|
||||
}
|
||||
t.run()
|
||||
}
|
||||
}
|
||||
// for _, tc := range sc.tcs {
|
||||
// t, err := NewTest(sc.ap, sc.t, tc)
|
||||
// if err != nil {
|
||||
// sc.t.Error(err)
|
||||
// continue
|
||||
// }
|
||||
// t.run()
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -2,8 +2,37 @@ package testrunner
|
|||
|
||||
import "testing"
|
||||
|
||||
func TestCLI(t *testing.T) {
|
||||
//https://github.com/nirmata/kyverno/issues/301
|
||||
t.Skip("skipping testrunner as this needs a re-design")
|
||||
runner(t, "/test/scenarios/cli")
|
||||
// func TestCLI(t *testing.T) {
|
||||
// //https://github.com/nirmata/kyverno/issues/301
|
||||
// runner(t, "/test/scenarios/cli")
|
||||
// }
|
||||
|
||||
func Test_Mutate_EndPoint(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_mutate_endPpoint.yaml")
|
||||
}
|
||||
|
||||
func Test_Mutate_imagePullPolicy(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_mutate_imagePullPolicy.yaml")
|
||||
}
|
||||
|
||||
func Test_Mutate_Validate_qos(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_mutate_validate_qos.yaml")
|
||||
}
|
||||
|
||||
func Test_validate_containerSecurityContext(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_validate_containerSecurityContext.yaml")
|
||||
}
|
||||
|
||||
func Test_validate_healthChecks(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_validate_healthChecks.yaml")
|
||||
}
|
||||
|
||||
func Test_validate_imageRegistries(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_validate_imageRegistries.yaml")
|
||||
}
|
||||
|
||||
func Test_validate_nonRootUsers(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_validate_nonRootUser.yaml")
|
||||
}
|
||||
|
||||
//TODO add tests for Generation
|
||||
|
|
|
@ -3,11 +3,10 @@ package webhooks
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -33,25 +32,27 @@ type response struct {
|
|||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos []info.PolicyInfo) []byte {
|
||||
annots := resource.GetAnnotations()
|
||||
if annots == nil {
|
||||
annots = map[string]string{}
|
||||
func generateAnnotationPatches(annotations map[string]string, policyResponse engine.PolicyResponse) []byte {
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
|
||||
var patchResponse response
|
||||
value := annotationFromPolicies(policyInfos)
|
||||
if _, ok := annots[policyAnnotation]; ok {
|
||||
value := generateAnnotationsFromPolicyResponse(policyResponse)
|
||||
if value == nil {
|
||||
// no patches or error while processing patches
|
||||
return nil
|
||||
}
|
||||
if _, ok := annotations[policyAnnotation]; ok {
|
||||
// create update patch string
|
||||
patchResponse = response{
|
||||
Op: "replace",
|
||||
Path: "/metadata/annotations/" + policyAnnotation,
|
||||
Op: "replace",
|
||||
Value: string(value),
|
||||
}
|
||||
} else {
|
||||
patchResponse = response{
|
||||
Op: "add",
|
||||
Path: "/metadata/annotations",
|
||||
Op: "add",
|
||||
Value: map[string]string{policyAnnotation: string(value)},
|
||||
}
|
||||
}
|
||||
|
@ -67,47 +68,109 @@ func prepareAnnotationPatches(resource *unstructured.Unstructured, policyInfos [
|
|||
return patchByte
|
||||
}
|
||||
|
||||
func annotationFromPolicies(policyInfos []info.PolicyInfo) []byte {
|
||||
var policyPatches []policyPatch
|
||||
for _, policyInfo := range policyInfos {
|
||||
var pp policyPatch
|
||||
// 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)
|
||||
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)
|
||||
|
||||
pp.PolicyName = policyInfo.Name
|
||||
pp.RulePatches = annotationFromPolicy(policyInfo)
|
||||
policyPatches = append(policyPatches, pp)
|
||||
}
|
||||
|
||||
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)
|
||||
patch, err := json.Marshal(rulePatches)
|
||||
if err != nil {
|
||||
glog.Infof("failed to marshall: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var rulePatches []rulePatch
|
||||
for _, ruleInfo := range policyInfo.Rules {
|
||||
|
||||
for _, patch := range ruleInfo.Patches {
|
||||
var patchmap map[string]string
|
||||
|
||||
if err := json.Unmarshal(patch, &patchmap); err != nil {
|
||||
glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
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 patch
|
||||
}
|
||||
|
||||
// func annotationFromPolicy(policyInfo info.PolicyInfo) []rulePatch {
|
||||
// if !policyInfo.IsSuccessful() {
|
||||
// glog.V(2).Infof("Policy %s failed, skip preparing annotation\n", policyInfo.Name)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// var rulePatches []rulePatch
|
||||
// for _, ruleInfo := range policyInfo.Rules {
|
||||
|
||||
// for _, patch := range ruleInfo.Patches {
|
||||
// var patchmap map[string]string
|
||||
|
||||
// if err := json.Unmarshal(patch, &patchmap); err != nil {
|
||||
// glog.Errorf("Failed to parse patch bytes, err: %v\n", err)
|
||||
// continue
|
||||
// }
|
||||
|
||||
// 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
|
||||
// }
|
||||
|
|
|
@ -3,7 +3,6 @@ package webhooks
|
|||
import (
|
||||
"github.com/golang/glog"
|
||||
engine "github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
policyctr "github.com/nirmata/kyverno/pkg/policy"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
|
@ -12,16 +11,19 @@ import (
|
|||
)
|
||||
|
||||
// HandleMutation handles mutating webhook admission request
|
||||
func (ws *WebhookServer) handleMutation(request *v1beta1.AdmissionRequest) (bool, engine.EngineResponse) {
|
||||
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, []byte, string) {
|
||||
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)
|
||||
|
||||
var patches [][]byte
|
||||
var policyInfos []info.PolicyInfo
|
||||
var policyStats []policyctr.PolicyStat
|
||||
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, er engine.EngineResponse) {
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.MutationExecutionTime = er.ExecutionTime
|
||||
ps.Stats.RulesAppliedCount = er.RulesAppliedCount
|
||||
ps.Stats.MutationExecutionTime = policyResponse.ProcessingTime
|
||||
ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
|
||||
policyStats = append(policyStats, ps)
|
||||
}
|
||||
// send stats for aggregation
|
||||
|
@ -32,31 +34,28 @@ func (ws *WebhookServer) handleMutation(request *v1beta1.AdmissionRequest) (bool
|
|||
ws.policyStatus.SendStat(stat)
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(5).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)
|
||||
|
||||
// convert RAW to unstructured
|
||||
resource, err := engine.ConvertToUnstructured(request.Object.Raw)
|
||||
if err != nil {
|
||||
//TODO: skip applying the amiddions control ?
|
||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||
return true, nil, ""
|
||||
}
|
||||
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
|
||||
engineResponse := engine.EngineResponse{PatchedResource: *resource}
|
||||
|
||||
policies, err := ws.pLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
//TODO check if the CRD is created ?
|
||||
// Unable to connect to policy Lister to access policies
|
||||
glog.Errorln("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied")
|
||||
glog.Warning(err)
|
||||
return true, engineResponse
|
||||
return true, nil, ""
|
||||
}
|
||||
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
for _, policy := range policies {
|
||||
|
||||
// check if policy has a rule for the admission request kind
|
||||
|
@ -64,53 +63,39 @@ func (ws *WebhookServer) handleMutation(request *v1beta1.AdmissionRequest) (bool
|
|||
continue
|
||||
}
|
||||
|
||||
policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
|
||||
glog.V(4).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
|
||||
glog.V(4).Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
|
||||
engineResponse = engine.Mutate(*policy, *resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
// TODO: this can be
|
||||
engineResponse := engine.MutateNew(*policy, *resource)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
// Gather policy application statistics
|
||||
gatherStat(policy.Name, engineResponse)
|
||||
|
||||
// ps := policyctr.NewPolicyStat(policy.Name, engineResponse.ExecutionTime, nil, engineResponse.RulesAppliedCount)
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
glog.V(4).Info("Failed rule details")
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.V(4).Infof("%s: %s\n", r.Name, r.Msgs)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
patches = append(patches, engineResponse.Patches...)
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
// 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
|
||||
}
|
||||
|
||||
// ADD ANNOTATIONS
|
||||
// ADD EVENTS
|
||||
if len(patches) > 0 {
|
||||
eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Mutation)
|
||||
ws.eventGen.Add(eventsInfo...)
|
||||
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
|
||||
ws.eventGen.Add(events...)
|
||||
|
||||
annotation := prepareAnnotationPatches(resource, policyInfos)
|
||||
patches = append(patches, annotation)
|
||||
}
|
||||
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
// Send policy engine Stats
|
||||
if ok {
|
||||
if isResponseSuccesful(engineResponses) {
|
||||
sendStat(false)
|
||||
engineResponse.Patches = patches
|
||||
return true, engineResponse
|
||||
patch := engine.JoinPatches(patches)
|
||||
return true, patch, ""
|
||||
}
|
||||
|
||||
sendStat(true)
|
||||
glog.Errorf("Failed to mutate the resource: %s\n", msg)
|
||||
return false, engineResponse
|
||||
glog.Errorf("Failed to mutate the resource\n")
|
||||
return false, nil, getErrorMsg(engineResponses)
|
||||
}
|
||||
|
|
|
@ -3,51 +3,138 @@ package webhooks
|
|||
import (
|
||||
"strings"
|
||||
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
)
|
||||
|
||||
//TODO: change validation from bool -> enum(validation, mutation)
|
||||
func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, ruleType info.RuleType) []*event.Info {
|
||||
var eventsInfo []*event.Info
|
||||
ok, msg := isAdmSuccesful(policyInfoList)
|
||||
// Some policies failed to apply succesfully
|
||||
if !ok {
|
||||
for _, pi := range policyInfoList {
|
||||
if pi.IsSuccessful() {
|
||||
//generateEvents generates event info for the engine responses
|
||||
func generateEvents(engineResponses []engine.EngineResponseNew, onUpdate bool) []event.Info {
|
||||
var events []event.Info
|
||||
if !isResponseSuccesful(engineResponses) {
|
||||
for _, er := range engineResponses {
|
||||
if er.IsSuccesful() {
|
||||
// dont create events on success
|
||||
continue
|
||||
}
|
||||
rules := pi.FailedRules()
|
||||
ruleNames := strings.Join(rules, ";")
|
||||
if !onUpdate {
|
||||
// CREATE
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyApplyBlockCreate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
|
||||
glog.V(3).Infof("Rule(s) %s of policy %s blocked resource creation, error: %s\n", ruleNames, pi.Name, msg)
|
||||
} else {
|
||||
failedRules := er.GetFailedRules()
|
||||
filedRulesStr := strings.Join(failedRules, ";")
|
||||
if onUpdate {
|
||||
var e event.Info
|
||||
// UPDATE
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.RequestBlocked, event.FPolicyApplyBlockUpdate, ruleNames, pi.Name))
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyBlockResourceUpdate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
glog.V(3).Infof("Request blocked events info has prepared for %s/%s and %s/%s\n", policyKind, pi.Name, pi.RKind, pi.RName)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !onUpdate {
|
||||
// All policies were applied succesfully
|
||||
// CREATE
|
||||
for _, pi := range policyInfoList {
|
||||
rules := pi.SuccessfulRules()
|
||||
ruleNames := strings.Join(rules, ";")
|
||||
eventsInfo = append(eventsInfo,
|
||||
event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.PolicyApplied, event.SRulesApply, ruleNames, pi.Name))
|
||||
// event on resource
|
||||
e = event.NewEventNew(
|
||||
er.PolicyResponse.Resource.Kind,
|
||||
er.PolicyResponse.Resource.APIVersion,
|
||||
er.PolicyResponse.Resource.Namespace,
|
||||
er.PolicyResponse.Resource.Name,
|
||||
event.RequestBlocked.String(),
|
||||
event.FPolicyApplyBlockUpdate,
|
||||
filedRulesStr,
|
||||
er.PolicyResponse.Policy,
|
||||
)
|
||||
glog.V(4).Infof("UPDATE event on resource %s/%s/%s with policy %s", er.PolicyResponse.Resource.Kind, er.PolicyResponse.Resource.Namespace, er.PolicyResponse.Resource.Name, er.PolicyResponse.Policy)
|
||||
events = append(events, e)
|
||||
|
||||
glog.V(3).Infof("Success event info has prepared for %s/%s\n", pi.RKind, pi.RName)
|
||||
// event on policy
|
||||
e = event.NewEventNew(
|
||||
"Policy",
|
||||
kyverno.SchemeGroupVersion.String(),
|
||||
"",
|
||||
er.PolicyResponse.Policy,
|
||||
event.RequestBlocked.String(),
|
||||
event.FPolicyBlockResourceUpdate,
|
||||
er.PolicyResponse.Resource.Namespace+"/"+er.PolicyResponse.Resource.Name,
|
||||
filedRulesStr,
|
||||
)
|
||||
glog.V(4).Infof("UPDATE event on policy %s", er.PolicyResponse.Policy)
|
||||
events = append(events, e)
|
||||
|
||||
} else {
|
||||
// CREATE
|
||||
// event on policy
|
||||
e := event.NewEventNew(
|
||||
"Policy",
|
||||
kyverno.SchemeGroupVersion.String(),
|
||||
"",
|
||||
er.PolicyResponse.Policy,
|
||||
event.RequestBlocked.String(),
|
||||
event.FPolicyApplyBlockCreate,
|
||||
er.PolicyResponse.Resource.Namespace+"/"+er.PolicyResponse.Resource.Name,
|
||||
filedRulesStr,
|
||||
)
|
||||
glog.V(4).Infof("CREATE event on policy %s", er.PolicyResponse.Policy)
|
||||
events = append(events, e)
|
||||
}
|
||||
}
|
||||
return events
|
||||
}
|
||||
return eventsInfo
|
||||
if !onUpdate {
|
||||
// All policies were applied succesfully
|
||||
// CREATE
|
||||
for _, er := range engineResponses {
|
||||
successRules := er.GetSuccessRules()
|
||||
successRulesStr := strings.Join(successRules, ";")
|
||||
// event on resource
|
||||
e := event.NewEventNew(
|
||||
er.PolicyResponse.Resource.Kind,
|
||||
er.PolicyResponse.Resource.APIVersion,
|
||||
er.PolicyResponse.Resource.Namespace,
|
||||
er.PolicyResponse.Resource.Name,
|
||||
event.PolicyApplied.String(),
|
||||
event.SRulesApply,
|
||||
successRulesStr,
|
||||
er.PolicyResponse.Policy,
|
||||
)
|
||||
events = append(events, e)
|
||||
}
|
||||
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
// //TODO: change validation from bool -> enum(validation, mutation)
|
||||
// func newEventInfoFromPolicyInfo(policyInfoList []info.PolicyInfo, onUpdate bool, ruleType info.RuleType) []*event.Info {
|
||||
// var eventsInfo []*event.Info
|
||||
// ok, msg := isAdmSuccesful(policyInfoList)
|
||||
// // Some policies failed to apply succesfully
|
||||
// if !ok {
|
||||
// for _, pi := range policyInfoList {
|
||||
// if pi.IsSuccessful() {
|
||||
// continue
|
||||
// }
|
||||
// rules := pi.FailedRules()
|
||||
// ruleNames := strings.Join(rules, ";")
|
||||
// if !onUpdate {
|
||||
// // CREATE
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyApplyBlockCreate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
|
||||
// glog.V(3).Infof("Rule(s) %s of policy %s blocked resource creation, error: %s\n", ruleNames, pi.Name, msg)
|
||||
// } else {
|
||||
// // UPDATE
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.RequestBlocked, event.FPolicyApplyBlockUpdate, ruleNames, pi.Name))
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(policyKind, "", pi.Name, event.RequestBlocked, event.FPolicyBlockResourceUpdate, pi.RNamespace+"/"+pi.RName, ruleNames))
|
||||
// glog.V(3).Infof("Request blocked events info has prepared for %s/%s and %s/%s\n", policyKind, pi.Name, pi.RKind, pi.RName)
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if !onUpdate {
|
||||
// // All policies were applied succesfully
|
||||
// // CREATE
|
||||
// for _, pi := range policyInfoList {
|
||||
// rules := pi.SuccessfulRules()
|
||||
// ruleNames := strings.Join(rules, ";")
|
||||
// eventsInfo = append(eventsInfo,
|
||||
// event.NewEvent(pi.RKind, pi.RNamespace, pi.RName, event.PolicyApplied, event.SRulesApply, ruleNames, pi.Name))
|
||||
|
||||
// glog.V(3).Infof("Success event info has prepared for %s/%s\n", pi.RKind, pi.RName)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return eventsInfo
|
||||
// }
|
||||
|
|
|
@ -10,8 +10,6 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1"
|
||||
|
@ -24,6 +22,7 @@ import (
|
|||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
"github.com/nirmata/kyverno/pkg/webhookconfig"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
|
@ -52,7 +51,7 @@ func NewWebhookServer(
|
|||
client *client.Client,
|
||||
tlsPair *tlsutils.TlsPemPair,
|
||||
pInformer kyvernoinformer.PolicyInformer,
|
||||
pvInormer kyvernoinformer.PolicyViolationInformer,
|
||||
pvInformer kyvernoinformer.PolicyViolationInformer,
|
||||
eventGen event.Interface,
|
||||
webhookRegistrationClient *webhookconfig.WebhookRegistrationClient,
|
||||
policyStatus policy.PolicyStatusInterface,
|
||||
|
@ -75,8 +74,8 @@ func NewWebhookServer(
|
|||
client: client,
|
||||
kyvernoClient: kyvernoClient,
|
||||
pLister: pInformer.Lister(),
|
||||
pvLister: pvInormer.Lister(),
|
||||
pListerSynced: pInformer.Informer().HasSynced,
|
||||
pvLister: pvInformer.Lister(),
|
||||
pListerSynced: pvInformer.Informer().HasSynced,
|
||||
pvListerSynced: pInformer.Informer().HasSynced,
|
||||
eventGen: eventGen,
|
||||
webhookRegistrationClient: webhookRegistrationClient,
|
||||
|
@ -141,24 +140,43 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
var response *v1beta1.AdmissionResponse
|
||||
|
||||
allowed, engineResponse := ws.handleMutation(request)
|
||||
if !allowed {
|
||||
// TODO: add failure message to response
|
||||
// MUTATION
|
||||
ok, patches, msg := ws.HandleMutation(request)
|
||||
if !ok {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Status: "Failure",
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
response = ws.handleValidation(request, engineResponse.PatchedResource)
|
||||
if response.Allowed && len(engineResponse.Patches) > 0 {
|
||||
patchType := v1beta1.PatchTypeJSONPatch
|
||||
response.Patch = engine.JoinPatches(engineResponse.Patches)
|
||||
response.PatchType = &patchType
|
||||
// patch the resource with patches before handling validation rules
|
||||
patchedResource := processResourceWithPatches(patches, request.Object.Raw)
|
||||
|
||||
// VALIDATION
|
||||
ok, msg = ws.HandleValidation(request, patchedResource)
|
||||
if !ok {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Status: "Failure",
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
// Succesfful processing of mutation & validation rules in policy
|
||||
patchType := v1beta1.PatchTypeJSONPatch
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Status: "Success",
|
||||
},
|
||||
Patch: patches,
|
||||
PatchType: &patchType,
|
||||
}
|
||||
}
|
||||
|
||||
// RunAsync TLS server in separate thread and returns control immediately
|
||||
|
|
|
@ -6,24 +6,61 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
)
|
||||
|
||||
const policyKind = "Policy"
|
||||
|
||||
func isAdmSuccesful(policyInfos []info.PolicyInfo) (bool, string) {
|
||||
var admSuccess = true
|
||||
var errMsgs []string
|
||||
for _, pi := range policyInfos {
|
||||
if !pi.IsSuccessful() {
|
||||
admSuccess = false
|
||||
errMsgs = append(errMsgs, fmt.Sprintf("\nPolicy %s failed with following rules", pi.Name))
|
||||
// Get the error rules
|
||||
errorRules := pi.ErrorRules()
|
||||
errMsgs = append(errMsgs, errorRules)
|
||||
// func isAdmSuccesful(policyInfos []info.PolicyInfo) (bool, string) {
|
||||
// var admSuccess = true
|
||||
// var errMsgs []string
|
||||
// for _, pi := range policyInfos {
|
||||
// if !pi.IsSuccessful() {
|
||||
// admSuccess = false
|
||||
// errMsgs = append(errMsgs, fmt.Sprintf("\nPolicy %s failed with following rules", pi.Name))
|
||||
// // Get the error rules
|
||||
// errorRules := pi.ErrorRules()
|
||||
// errMsgs = append(errMsgs, errorRules)
|
||||
// }
|
||||
// }
|
||||
// return admSuccess, strings.Join(errMsgs, ";")
|
||||
// }
|
||||
|
||||
func isResponseSuccesful(engineReponses []engine.EngineResponseNew) bool {
|
||||
for _, er := range engineReponses {
|
||||
if !er.IsSuccesful() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return admSuccess, strings.Join(errMsgs, ";")
|
||||
return true
|
||||
}
|
||||
|
||||
// returns true -> if there is even one policy that blocks resource requst
|
||||
// returns false -> if all the policies are meant to report only, we dont block resource request
|
||||
func toBlockResource(engineReponses []engine.EngineResponseNew) bool {
|
||||
for _, er := range engineReponses {
|
||||
if er.PolicyResponse.ValidationFailureAction != ReportViolation {
|
||||
glog.V(4).Infof("ValidationFailureAction set to enforce for policy %s , blocking resource ceation", er.PolicyResponse.Policy)
|
||||
return true
|
||||
}
|
||||
}
|
||||
glog.V(4).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation")
|
||||
return false
|
||||
}
|
||||
|
||||
func getErrorMsg(engineReponses []engine.EngineResponseNew) string {
|
||||
var str []string
|
||||
for _, er := range engineReponses {
|
||||
if !er.IsSuccesful() {
|
||||
str = append(str, fmt.Sprintf("failed policy %s", er.PolicyResponse.Policy))
|
||||
for _, rule := range er.PolicyResponse.Rules {
|
||||
if !rule.Success {
|
||||
str = append(str, rule.ToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(str, "\n")
|
||||
}
|
||||
|
||||
//ArrayFlags to store filterkinds
|
||||
|
@ -75,15 +112,28 @@ const (
|
|||
ReportViolation = "audit"
|
||||
)
|
||||
|
||||
// returns true -> if there is even one policy that blocks resource requst
|
||||
// returns false -> if all the policies are meant to report only, we dont block resource request
|
||||
func toBlock(pis []info.PolicyInfo) bool {
|
||||
for _, pi := range pis {
|
||||
if pi.ValidationFailureAction != ReportViolation {
|
||||
glog.V(3).Infoln("ValidationFailureAction set to enforce, blocking resource ceation")
|
||||
return true
|
||||
}
|
||||
// // returns true -> if there is even one policy that blocks resource requst
|
||||
// // returns false -> if all the policies are meant to report only, we dont block resource request
|
||||
// func toBlock(pis []info.PolicyInfo) bool {
|
||||
// for _, pi := range pis {
|
||||
// if pi.ValidationFailureAction != ReportViolation {
|
||||
// glog.V(3).Infoln("ValidationFailureAction set to enforce, blocking resource ceation")
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// glog.V(3).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation")
|
||||
// return false
|
||||
// }
|
||||
|
||||
func processResourceWithPatches(patch []byte, resource []byte) []byte {
|
||||
if patch == nil {
|
||||
return nil
|
||||
}
|
||||
glog.V(3).Infoln("ValidationFailureAction set to audit, allowing resource creation, reporting with violation")
|
||||
return false
|
||||
glog.Info(string(resource))
|
||||
resource, err := engine.ApplyPatchNew(resource, patch)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to patch resource: %v", err)
|
||||
return nil
|
||||
}
|
||||
return resource
|
||||
}
|
||||
|
|
|
@ -3,28 +3,29 @@ package webhooks
|
|||
import (
|
||||
"github.com/golang/glog"
|
||||
engine "github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
policyctr "github.com/nirmata/kyverno/pkg/policy"
|
||||
"github.com/nirmata/kyverno/pkg/policyviolation"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// 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, resource unstructured.Unstructured) *v1beta1.AdmissionResponse {
|
||||
var policyInfos []info.PolicyInfo
|
||||
// patchedResource is the (resource + patches) after applying mutation rules
|
||||
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, patchedResource []byte) (bool, string) {
|
||||
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)
|
||||
|
||||
var policyStats []policyctr.PolicyStat
|
||||
|
||||
// gather stats from the engine response
|
||||
gatherStat := func(policyName string, er engine.EngineResponse) {
|
||||
gatherStat := func(policyName string, policyResponse engine.PolicyResponse) {
|
||||
ps := policyctr.PolicyStat{}
|
||||
ps.PolicyName = policyName
|
||||
ps.Stats.ValidationExecutionTime = er.ExecutionTime
|
||||
ps.Stats.RulesAppliedCount = er.RulesAppliedCount
|
||||
ps.Stats.ValidationExecutionTime = policyResponse.ProcessingTime
|
||||
ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
|
||||
policyStats = append(policyStats, ps)
|
||||
}
|
||||
// send stats for aggregation
|
||||
|
@ -36,8 +37,23 @@ func (ws *WebhookServer) handleValidation(request *v1beta1.AdmissionRequest, res
|
|||
}
|
||||
}
|
||||
|
||||
glog.V(5).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)
|
||||
resourceRaw := request.Object.Raw
|
||||
if patchedResource != nil {
|
||||
glog.V(4).Info("using patched resource from mutation to process validation rules")
|
||||
resourceRaw = patchedResource
|
||||
}
|
||||
// convert RAW to unstructured
|
||||
resource, err := engine.ConvertToUnstructured(resourceRaw)
|
||||
if err != nil {
|
||||
//TODO: skip applying the amiddions control ?
|
||||
glog.Errorf("unable to convert raw resource to unstructured: %v", err)
|
||||
return true, ""
|
||||
}
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
// all the patches to be applied on the resource
|
||||
|
||||
policies, err := ws.pLister.List(labels.NewSelector())
|
||||
if err != nil {
|
||||
|
@ -45,81 +61,46 @@ func (ws *WebhookServer) handleValidation(request *v1beta1.AdmissionRequest, res
|
|||
// Unable to connect to policy Lister to access policies
|
||||
glog.Error("Unable to connect to policy controller to access policies. Validation Rules are NOT being applied")
|
||||
glog.Warning(err)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
//TODO: check if resource gvk is available in raw resource,
|
||||
// if not then set it from the api request
|
||||
resource.SetGroupVersionKind(schema.GroupVersionKind{Group: request.Kind.Group, Version: request.Kind.Version, Kind: request.Kind.Kind})
|
||||
//TODO: check if the name and namespace is also passed right in the resource?
|
||||
// all the patches to be applied on the resource
|
||||
|
||||
var engineResponses []engine.EngineResponseNew
|
||||
for _, policy := range policies {
|
||||
|
||||
if !utils.Contains(getApplicableKindsForPolicy(policy), request.Kind.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
policyInfo := info.NewPolicyInfo(policy.Name, resource.GetKind(), resource.GetName(), resource.GetNamespace(), policy.Spec.ValidationFailureAction)
|
||||
|
||||
glog.V(4).Infof("Handling validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
|
||||
|
||||
glog.V(4).Infof("Validating resource %s/%s/%s with policy %s with %d rules\n", resource.GetKind(), resource.GetNamespace(), resource.GetName(), policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
// glog.V(4).Infof("Validating resource %s/%s/%s with policy %s with %d rules\n", resource.GetKind(), resource.GetNamespace(), resource.GetName(), policy.ObjectMeta.Name, len(policy.Spec.Rules))
|
||||
|
||||
engineResponse := engine.Validate(*policy, resource)
|
||||
if len(engineResponse.RuleInfos) == 0 {
|
||||
engineResponse := engine.ValidateNew(*policy, *resource)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
// Gather policy application statistics
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
continue
|
||||
}
|
||||
gatherStat(policy.Name, engineResponse)
|
||||
|
||||
if len(engineResponse.RuleInfos) > 0 {
|
||||
glog.V(4).Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
|
||||
}
|
||||
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, resource.GetNamespace(), resource.GetName())
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.Warningf("%s: %s\n", r.Name, r.Msgs)
|
||||
}
|
||||
}
|
||||
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
|
||||
}
|
||||
|
||||
// ADD EVENTS
|
||||
if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 {
|
||||
eventsInfo := newEventInfoFromPolicyInfo(policyInfos, (request.Operation == v1beta1.Update), info.Validation)
|
||||
// If the validationFailureAction flag is set "audit",
|
||||
// then we dont block the request and report the violations
|
||||
ws.eventGen.Add(eventsInfo...)
|
||||
}
|
||||
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
|
||||
ws.eventGen.Add(events...)
|
||||
|
||||
// If Validation fails then reject the request
|
||||
// violations are created if "audit" flag is set
|
||||
// and if there are any then we dont block the resource creation
|
||||
// Even if one the policy being applied
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
if !ok && toBlock(policyInfos) {
|
||||
if !isResponseSuccesful(engineResponses) && toBlockResource(engineResponses) {
|
||||
sendStat(true)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
return false, getErrorMsg(engineResponses)
|
||||
}
|
||||
|
||||
// ADD POLICY VIOLATIONS
|
||||
policyviolation.GeneratePolicyViolations(ws.pvListerSynced, ws.pvLister, ws.kyvernoClient, policyInfos)
|
||||
policyviolation.CreatePV(ws.pvLister, ws.kyvernoClient, engineResponses)
|
||||
|
||||
sendStat(false)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
|
15
test/output/output_mutate_endpoint.yaml
Normal file
15
test/output/output_mutate_endpoint.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
creationTimestamp:
|
||||
labels:
|
||||
isMutated: 'true'
|
||||
label: test
|
||||
name: test-endpoint
|
||||
subsets:
|
||||
- addresses:
|
||||
- ip: 192.168.10.171
|
||||
ports:
|
||||
- name: secure-connection
|
||||
port: 9663
|
||||
protocol: TCP
|
20
test/output/output_mutate_imagePullPolicy.yaml
Normal file
20
test/output/output_mutate_imagePullPolicy.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginxlatest
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginxlatest
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginxlatest
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
27
test/output/output_mutate_validate_qos.yaml
Normal file
27
test/output/output_mutate_validate_qos.yaml
Normal file
|
@ -0,0 +1,27 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
creationTimestamp:
|
||||
labels:
|
||||
test: qos
|
||||
name: qos-demo
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
resources:
|
||||
limits:
|
||||
cpu: 50m
|
||||
memory: 300Mi
|
||||
status: {}
|
21
test/scenarios/test/s1.yaml
Normal file
21
test/scenarios/test/s1.yaml
Normal file
|
@ -0,0 +1,21 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/cli/policy_deployment.yaml
|
||||
resource: examples/cli/nginx.yaml
|
||||
expected:
|
||||
mutation:
|
||||
patchedresource: test/output/nginx.yaml
|
||||
policyresponse:
|
||||
policy: policy-deployment
|
||||
resource:
|
||||
kind: Deployment
|
||||
apiVersion: 'apps/v1'
|
||||
namespace: ''
|
||||
name: nginx-deployment
|
||||
rules:
|
||||
- name: add-label
|
||||
type: Mutation
|
||||
success: true
|
||||
message: succesfully process JSON patches
|
||||
# patches: `[{"path":"/metadata/labels/isMutated","op":"add","value":"true"},
|
||||
# {"path":"/metadata/labels/app","op":"replace","value":"nginx_is_mutated"}]`
|
19
test/scenarios/test/scenario_mutate_endPpoint.yaml
Normal file
19
test/scenarios/test/scenario_mutate_endPpoint.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_mutate_endpoint.yaml
|
||||
resource: examples/resource_mutate_endpoint.yaml
|
||||
expected:
|
||||
mutation:
|
||||
patchedresource: test/output/output_mutate_endpoint.yaml
|
||||
policyresponse:
|
||||
policy: policy-endpoints
|
||||
resource:
|
||||
kind: Endpoints
|
||||
apiVersion: v1
|
||||
namespace: ''
|
||||
name: test-endpoint
|
||||
rules:
|
||||
- name: pEP
|
||||
type: Mutation
|
||||
success: true
|
||||
message: succesfully process JSON patches
|
19
test/scenarios/test/scenario_mutate_imagePullPolicy.yaml
Normal file
19
test/scenarios/test/scenario_mutate_imagePullPolicy.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_mutate_imagePullPolicy.yaml
|
||||
resource: examples/resource_mutate_imagePullPolicy.yaml
|
||||
expected:
|
||||
mutation:
|
||||
patchedresource: test/output/output_mutate_endpoint.yaml
|
||||
policyresponse:
|
||||
policy: image-pull-policy
|
||||
resource:
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
namespace: ''
|
||||
name: nginx-deployment
|
||||
rules:
|
||||
- name: image-pull-policy
|
||||
type: Mutation
|
||||
success: true
|
||||
message: succesfully process JSON patches
|
32
test/scenarios/test/scenario_mutate_validate_qos.yaml
Normal file
32
test/scenarios/test/scenario_mutate_validate_qos.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_mutate_validate_qos.yaml
|
||||
resource: examples/resource_mutate_validate_qos.yaml
|
||||
expected:
|
||||
mutation:
|
||||
patchedresource: test/output/output_mutate_validate_qos.yaml
|
||||
policyresponse:
|
||||
policy: policy-qos
|
||||
resource:
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
namespace: ''
|
||||
name: qos-demo
|
||||
rules:
|
||||
- name: add-memory-limit
|
||||
type: Mutation
|
||||
success: true
|
||||
message: succesfully process JSON patches
|
||||
validation:
|
||||
policyresponse:
|
||||
policy: policy-qos
|
||||
resource:
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
namespace: ''
|
||||
name: qos-demo
|
||||
rules:
|
||||
- name: check-cpu-memory-limits
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
success: true
|
|
@ -0,0 +1,18 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_validate_containerSecurityContext.yaml
|
||||
resource: examples/resource_validate_containerSecurityContext.yaml
|
||||
expected:
|
||||
validation:
|
||||
policyresponse:
|
||||
policy: container-security-context
|
||||
resource:
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
namespace: ''
|
||||
name: csc-demo-unprivileged
|
||||
rules:
|
||||
- name: validate-user-privilege
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
success: true
|
22
test/scenarios/test/scenario_validate_healthChecks.yaml
Normal file
22
test/scenarios/test/scenario_validate_healthChecks.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_validate_healthChecks.yaml
|
||||
resource: examples/resource_validate_healthChecks.yaml
|
||||
expected:
|
||||
validation:
|
||||
policyresponse:
|
||||
policy: check-probe-exists
|
||||
resource:
|
||||
kind: Pod
|
||||
apiVersion: v1
|
||||
namespace: ''
|
||||
name: probe
|
||||
rules:
|
||||
- name: check-readinessProbe-exists
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
success: true
|
||||
- name: check-livenessProbe-exists
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
success: true
|
18
test/scenarios/test/scenario_validate_imageRegistries.yaml
Normal file
18
test/scenarios/test/scenario_validate_imageRegistries.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_validate_imageRegistries.yaml
|
||||
resource: examples/resource_validate_imageRegistries.yaml
|
||||
expected:
|
||||
validation:
|
||||
policyresponse:
|
||||
policy: check-registries
|
||||
resource:
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
namespace: ''
|
||||
name: nirmata-nginx
|
||||
rules:
|
||||
- name: check-registries
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
success: true
|
18
test/scenarios/test/scenario_validate_nonRootUser.yaml
Normal file
18
test/scenarios/test/scenario_validate_nonRootUser.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_validate_nonRootUser.yaml
|
||||
resource: examples/resource_validate_nonRootUser.yaml
|
||||
expected:
|
||||
validation:
|
||||
policyresponse:
|
||||
policy: check-container-security-context
|
||||
resource:
|
||||
kind: Pod
|
||||
apiVersion: v1
|
||||
namespace: ''
|
||||
name: sec-ctx-unprivileged
|
||||
rules:
|
||||
- name: check-root-user
|
||||
type: Validation
|
||||
meesage: 1/2 patterns succesfully validated
|
||||
success: true
|
Loading…
Add table
Reference in a new issue