mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-13 19:28:55 +00:00
fixes + support generate policies
This commit is contained in:
parent
d43b4d93c2
commit
0a132054e1
14 changed files with 120 additions and 696 deletions
|
@ -3,7 +3,6 @@ package testrunner
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
ospath "path"
|
||||
|
@ -16,6 +15,7 @@ import (
|
|||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
@ -43,7 +43,7 @@ type sInput struct {
|
|||
type sExpected struct {
|
||||
Mutation sMutation `yaml:"mutation,omitempty"`
|
||||
Validation sValidation `yaml:"validation,omitempty"`
|
||||
// Generation sGeneration `yaml:"generation,omitempty"`
|
||||
Generation sGeneration `yaml:"generation,omitempty"`
|
||||
}
|
||||
|
||||
type sMutation struct {
|
||||
|
@ -58,6 +58,13 @@ type sValidation struct {
|
|||
PolicyResponse engine.PolicyResponse `yaml:"policyresponse"`
|
||||
}
|
||||
|
||||
type sGeneration struct {
|
||||
// generated resources
|
||||
GeneratedResources []kyverno.ResourceSpec `yaml:"generatedResources"`
|
||||
// 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")
|
||||
|
@ -126,13 +133,6 @@ func runScenario(t *testing.T, s *scenarioT) bool {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -143,17 +143,6 @@ func runTestCase(t *testing.T, tc scaseT) bool {
|
|||
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)
|
||||
|
@ -166,18 +155,43 @@ func runTestCase(t *testing.T, tc scaseT) bool {
|
|||
|
||||
// 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)
|
||||
|
||||
// Generation
|
||||
if resource.GetKind() == "Namespace" {
|
||||
// generate mock client if resources are to be loaded
|
||||
// - create mock client
|
||||
// - load resources
|
||||
client := getClient(t, tc.Input.LoadResources)
|
||||
t.Logf("creating NS %s", resource.GetName())
|
||||
if err := createNamespace(client, resource); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
er = engine.Generate(client, *policy, *resource)
|
||||
t.Log(("---Generation---"))
|
||||
validateResponse(t, er.PolicyResponse, tc.Expected.Generation.PolicyResponse)
|
||||
validateGeneratedResources(t, client, *policy, tc.Expected.Generation.GeneratedResources)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func createNamespace(client *client.Client, ns *unstructured.Unstructured) error {
|
||||
_, err := client.CreateResource("Namespace", "", ns, false)
|
||||
return err
|
||||
}
|
||||
func validateGeneratedResources(t *testing.T, client *client.Client, policy kyverno.Policy, expected []kyverno.ResourceSpec) {
|
||||
t.Log("--validate if resources are generated---")
|
||||
// list of expected generated resources
|
||||
for _, resource := range expected {
|
||||
if _, err := client.GetResource(resource.Kind, resource.Namespace, resource.Name); err != nil {
|
||||
t.Errorf("generated resource %s/%s/%s not found. %v", resource.Kind, resource.Namespace, resource.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateResource(t *testing.T, responseResource unstructured.Unstructured, expectedResourceFile string) {
|
||||
resourcePrint := func(obj unstructured.Unstructured) {
|
||||
t.Log("-----patched resource----")
|
||||
|
@ -185,7 +199,6 @@ func validateResource(t *testing.T, responseResource unstructured.Unstructured,
|
|||
t.Log(string(data))
|
||||
}
|
||||
}
|
||||
resourcePrint(responseResource)
|
||||
if expectedResourceFile == "" {
|
||||
t.Log("expected resource file not specified, wont compare resources")
|
||||
return
|
||||
|
@ -197,10 +210,12 @@ func validateResource(t *testing.T, responseResource unstructured.Unstructured,
|
|||
return
|
||||
}
|
||||
|
||||
// resourcePrint(*expectedResource)
|
||||
resourcePrint(responseResource)
|
||||
resourcePrint(*expectedResource)
|
||||
// compare the resources
|
||||
if !reflect.DeepEqual(responseResource, *expectedResource) {
|
||||
t.Log("failed: response resource returned does not match expected resource")
|
||||
t.Error("failed: response resource returned does not match expected resource")
|
||||
return
|
||||
}
|
||||
t.Log("success: response resource returned matches expected resource")
|
||||
}
|
||||
|
@ -212,27 +227,27 @@ func validateResponse(t *testing.T, er engine.PolicyResponse, expected engine.Po
|
|||
}
|
||||
// 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")
|
||||
t.Errorf("Policy name: expected %s, recieved %s", expected.Policy, er.Policy)
|
||||
}
|
||||
// compare resource spec
|
||||
compareResourceSpec(t, er.Resource, expected.Resource)
|
||||
// // stats
|
||||
// //TODO 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(er.Rules) != len(expected.Rules) {
|
||||
t.Error("rule count: error")
|
||||
}
|
||||
if len(expected.Rules) == len(expected.Rules) {
|
||||
if len(er.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])
|
||||
compareRules(t, er.Rules[index], r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,46 +255,47 @@ func validateResponse(t *testing.T, er engine.PolicyResponse, expected engine.Po
|
|||
func compareResourceSpec(t *testing.T, resource engine.ResourceSpec, expectedResource engine.ResourceSpec) {
|
||||
// kind
|
||||
if resource.Kind != expectedResource.Kind {
|
||||
t.Error("error: kind")
|
||||
t.Errorf("kind: expected %s, recieved %s", expectedResource.Kind, resource.Kind)
|
||||
}
|
||||
// // apiVersion
|
||||
// //TODO apiVersion
|
||||
// if resource.APIVersion != expectedResource.APIVersion {
|
||||
// t.Error("error: apiVersion")
|
||||
// }
|
||||
|
||||
// namespace
|
||||
if resource.Namespace != expectedResource.Namespace {
|
||||
t.Error("error: namespace")
|
||||
t.Errorf("namespace: expected %s, recieved %s", expectedResource.Namespace, resource.Namespace)
|
||||
}
|
||||
// name
|
||||
if resource.Name != expectedResource.Name {
|
||||
t.Error("error: name")
|
||||
t.Errorf("name: expected %s, recieved %s", expectedResource.Name, resource.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)
|
||||
t.Errorf("rule name: expected %s, recieved %s", expectedRule.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")
|
||||
t.Errorf("rule type: expected %s, recieved %s", expectedRule.Type, rule.Type)
|
||||
}
|
||||
// message
|
||||
if rule.Message != expectedRule.Message {
|
||||
t.Error("error: message")
|
||||
t.Errorf("rule message: expected %s, recieved %s", expectedRule.Message, rule.Message)
|
||||
}
|
||||
// // patches
|
||||
// //TODO patches
|
||||
// if reflect.DeepEqual(rule.Patches, expectedRule.Patches) {
|
||||
// t.Log("error: patches")
|
||||
// }
|
||||
|
||||
// success
|
||||
if rule.Success != expectedRule.Success {
|
||||
t.Error("error: success")
|
||||
t.Errorf("rule success: expected %t, recieved %t", expectedRule.Success, rule.Success)
|
||||
}
|
||||
// statistics
|
||||
}
|
||||
|
||||
func loadPolicyResource(t *testing.T, file string) *unstructured.Unstructured {
|
||||
|
@ -297,13 +313,9 @@ func loadPolicyResource(t *testing.T, file string) *unstructured.Unstructured {
|
|||
}
|
||||
|
||||
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:")
|
||||
glog.V(4).Infof("loading resources: %v", files)
|
||||
for _, file := range files {
|
||||
objects = loadObjects(t, file)
|
||||
}
|
||||
|
@ -316,10 +328,25 @@ func getClient(t *testing.T, files []string) *client.Client {
|
|||
t.Errorf("failed to create client. %v", err)
|
||||
return nil
|
||||
}
|
||||
// get GVR from GVK
|
||||
gvrs := getGVRForResources(objects)
|
||||
c.SetDiscovery(client.NewFakeDiscoveryClient(gvrs))
|
||||
t.Log("created mock client with pre-loaded resources")
|
||||
return c
|
||||
}
|
||||
|
||||
func getGVRForResources(objects []runtime.Object) []schema.GroupVersionResource {
|
||||
var gvrs []schema.GroupVersionResource
|
||||
for _, obj := range objects {
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
gv := gvk.GroupVersion()
|
||||
// maintain a static map for kind -> Resource
|
||||
gvr := gv.WithResource(getResourceFromKind(gvk.Kind))
|
||||
gvrs = append(gvrs, gvr)
|
||||
}
|
||||
return gvrs
|
||||
}
|
||||
|
||||
func loadResource(t *testing.T, path string) []*unstructured.Unstructured {
|
||||
var unstrResources []*unstructured.Unstructured
|
||||
t.Logf("loading resource from %s", path)
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
package testrunner
|
||||
|
||||
// import (
|
||||
// "fmt"
|
||||
// "reflect"
|
||||
// "strconv"
|
||||
// "testing"
|
||||
|
||||
// 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"
|
||||
// )
|
||||
|
||||
// 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)
|
||||
// }
|
||||
|
||||
// }
|
||||
// // 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")
|
||||
// }
|
||||
|
||||
// // 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) 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 {
|
||||
|
||||
// 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
|
||||
// }
|
||||
|
||||
// // 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")
|
||||
// }
|
||||
|
||||
// // 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
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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
|
||||
// }
|
||||
|
||||
// // 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
|
||||
// }
|
||||
|
||||
// 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
|
||||
// }
|
||||
|
||||
// //---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 +0,0 @@
|
|||
package testrunner
|
||||
|
||||
// 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"
|
||||
// )
|
||||
|
||||
// //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"`
|
||||
// }
|
||||
|
||||
// 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 tValidation struct {
|
||||
// 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 tResult struct {
|
||||
// Reason string `yaml:"reason, omitempty"`
|
||||
// }
|
||||
|
||||
// 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) 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) 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
|
||||
|
||||
// }
|
||||
|
||||
// // 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
|
||||
// }
|
||||
|
||||
// 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
|
||||
// }
|
|
@ -1,98 +0,0 @@
|
|||
package testrunner
|
||||
|
||||
// import (
|
||||
// "bytes"
|
||||
// "io/ioutil"
|
||||
// "path/filepath"
|
||||
// "testing"
|
||||
|
||||
// "os"
|
||||
// ospath "path"
|
||||
|
||||
// "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()
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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 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
|
||||
// }
|
||||
|
||||
// for _, tc := range sc.tcs {
|
||||
// t, err := NewTest(sc.ap, sc.t, tc)
|
||||
// if err != nil {
|
||||
// sc.t.Error(err)
|
||||
// continue
|
||||
// }
|
||||
// t.run()
|
||||
// }
|
||||
// }
|
|
@ -2,11 +2,6 @@ package testrunner
|
|||
|
||||
import "testing"
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
@ -35,4 +30,6 @@ func Test_validate_nonRootUsers(t *testing.T) {
|
|||
testScenario(t, "/test/scenarios/test/scenario_validate_nonRootUser.yaml")
|
||||
}
|
||||
|
||||
//TODO add tests for Generation
|
||||
func Test_generate_networkPolicy(t *testing.T) {
|
||||
testScenario(t, "/test/scenarios/test/scenario_generate_networkPolicy.yaml")
|
||||
}
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
package testrunner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,58 +21,6 @@ func LoadFile(path string) ([]byte, error) {
|
|||
return ioutil.ReadFile(path)
|
||||
}
|
||||
|
||||
type resourceInfo struct {
|
||||
rawResource []byte
|
||||
gvk *metav1.GroupVersionKind
|
||||
}
|
||||
|
||||
func (ri resourceInfo) isSame(other resourceInfo) bool {
|
||||
// compare gvk
|
||||
if *ri.gvk != *other.gvk {
|
||||
return false
|
||||
}
|
||||
// compare rawResource
|
||||
return bytes.Equal(ri.rawResource, other.rawResource)
|
||||
}
|
||||
|
||||
// compare patched resources
|
||||
func compareResource(er *resourceInfo, pr *resourceInfo) bool {
|
||||
if !er.isSame(*pr) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func createClient(resources []*resourceInfo) (*client.Client, error) {
|
||||
scheme := runtime.NewScheme()
|
||||
objects := []runtime.Object{}
|
||||
// registered group versions
|
||||
regResources := []schema.GroupVersionResource{}
|
||||
|
||||
for _, r := range resources {
|
||||
// registered gvr
|
||||
gv := schema.GroupVersion{Group: r.gvk.Group, Version: r.gvk.Version}
|
||||
gvr := gv.WithResource(getResourceFromKind(r.gvk.Kind))
|
||||
regResources = append(regResources, gvr)
|
||||
decode := kscheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, _, err := decode([]byte(r.rawResource), nil, nil)
|
||||
rdata, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to load resource. err %s", err)
|
||||
}
|
||||
unstr := unstructured.Unstructured{Object: rdata}
|
||||
objects = append(objects, &unstr)
|
||||
}
|
||||
// Mock Client
|
||||
c, err := client.NewMockClient(scheme, objects...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.SetDiscovery(client.NewFakeDiscoveryClient(regResources))
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
var kindToResource = map[string]string{
|
||||
"ConfigMap": "configmaps",
|
||||
"Endpoints": "endpoints",
|
||||
|
@ -96,32 +37,6 @@ func getResourceFromKind(kind string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
//ParseNameFromObject extracts resource name from JSON obj
|
||||
func ParseNameFromObject(bytes []byte) string {
|
||||
var objectJSON map[string]interface{}
|
||||
json.Unmarshal(bytes, &objectJSON)
|
||||
|
||||
meta := objectJSON["metadata"].(map[string]interface{})
|
||||
|
||||
if name, ok := meta["name"].(string); ok {
|
||||
return name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ParseNamespaceFromObject extracts the namespace from the JSON obj
|
||||
func ParseNamespaceFromObject(bytes []byte) string {
|
||||
var objectJSON map[string]interface{}
|
||||
json.Unmarshal(bytes, &objectJSON)
|
||||
|
||||
meta := objectJSON["metadata"].(map[string]interface{})
|
||||
|
||||
if namespace, ok := meta["namespace"].(string); ok {
|
||||
return namespace
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
|
||||
resource := &unstructured.Unstructured{}
|
||||
err := resource.UnmarshalJSON(data)
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
creationTimestamp:
|
||||
labels:
|
||||
app: nginxlatest
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginxlatest
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp:
|
||||
labels:
|
||||
app: nginxlatest
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
- image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: nginx
|
||||
resources: {}
|
||||
status: {}
|
22
test/scenarios/test/scenario_generate_networkPolicy.yaml
Normal file
22
test/scenarios/test/scenario_generate_networkPolicy.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
# file path relative to project root
|
||||
input:
|
||||
policy: examples/policy_generate_networkPolicy.yaml
|
||||
resource: examples/resource_generate_networkPolicy.yaml
|
||||
expected:
|
||||
generation:
|
||||
generatedResources:
|
||||
- name: defaultnetworkpolicy
|
||||
kind: NetworkPolicy
|
||||
namespace: devtest
|
||||
policyresponse:
|
||||
policy: defaultgeneratenetworkpolicy
|
||||
resource:
|
||||
kind: Namespace
|
||||
apiVersion: v1
|
||||
namespace: ''
|
||||
name: devtest
|
||||
rules:
|
||||
- name: default-networkpolicy
|
||||
type: Generation
|
||||
success: true
|
||||
message: created resource NetworkPolicy/devtest/defaultnetworkpolicy
|
|
@ -4,7 +4,7 @@ input:
|
|||
resource: examples/resource_mutate_imagePullPolicy.yaml
|
||||
expected:
|
||||
mutation:
|
||||
patchedresource: test/output/output_mutate_endpoint.yaml
|
||||
patchedresource: test/output/output_mutate_imagePullPolicy.yaml
|
||||
policyresponse:
|
||||
policy: image-pull-policy
|
||||
resource:
|
||||
|
@ -16,4 +16,4 @@ expected:
|
|||
- name: image-pull-policy
|
||||
type: Mutation
|
||||
success: true
|
||||
message: succesfully process JSON patches
|
||||
message: succesfully process overlay
|
||||
|
|
|
@ -16,7 +16,7 @@ expected:
|
|||
- name: add-memory-limit
|
||||
type: Mutation
|
||||
success: true
|
||||
message: succesfully process JSON patches
|
||||
message: succesfully process overlay
|
||||
validation:
|
||||
policyresponse:
|
||||
policy: policy-qos
|
||||
|
@ -28,5 +28,5 @@ expected:
|
|||
rules:
|
||||
- name: check-cpu-memory-limits
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
message: validation pattern succesfully validated
|
||||
success: true
|
|
@ -14,5 +14,5 @@ expected:
|
|||
rules:
|
||||
- name: validate-user-privilege
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
message: validation pattern succesfully validated
|
||||
success: true
|
|
@ -14,9 +14,9 @@ expected:
|
|||
rules:
|
||||
- name: check-readinessProbe-exists
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
message: validation pattern succesfully validated
|
||||
success: true
|
||||
- name: check-livenessProbe-exists
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
message: validation pattern succesfully validated
|
||||
success: true
|
||||
|
|
|
@ -14,5 +14,5 @@ expected:
|
|||
rules:
|
||||
- name: check-registries
|
||||
type: Validation
|
||||
meesage: validation pattern succesfully validated
|
||||
message: validation pattern succesfully validated
|
||||
success: true
|
|
@ -14,5 +14,5 @@ expected:
|
|||
rules:
|
||||
- name: check-root-user
|
||||
type: Validation
|
||||
meesage: 1/2 patterns succesfully validated
|
||||
message: 1/2 patterns succesfully validated
|
||||
success: true
|
Loading…
Add table
Reference in a new issue