1
0
Fork 0
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:
shivkumar dudhani 2019-08-30 14:06:47 -07:00
parent d43b4d93c2
commit 0a132054e1
14 changed files with 120 additions and 696 deletions

View file

@ -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)

View file

@ -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
// }

View file

@ -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
// }

View file

@ -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()
// }
// }

View file

@ -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")
}

View file

@ -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)

View file

@ -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: {}

View 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

View file

@ -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

View file

@ -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

View file

@ -14,5 +14,5 @@ expected:
rules:
- name: validate-user-privilege
type: Validation
meesage: validation pattern succesfully validated
message: validation pattern succesfully validated
success: true

View file

@ -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

View file

@ -14,5 +14,5 @@ expected:
rules:
- name: check-registries
type: Validation
meesage: validation pattern succesfully validated
message: validation pattern succesfully validated
success: true

View file

@ -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