mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
add validate helper functions
This commit is contained in:
parent
116203282d
commit
20e2f639eb
7 changed files with 925 additions and 504 deletions
|
@ -18,13 +18,13 @@ type EngineResponseNew struct {
|
|||
//PolicyResponse policy application response
|
||||
type PolicyResponse struct {
|
||||
// policy name
|
||||
Policy string
|
||||
Policy string `json:"policy"`
|
||||
// resource details
|
||||
Resource ResourceSpec
|
||||
Resource ResourceSpec `json:"resource"`
|
||||
// policy statistics
|
||||
PolicyStats
|
||||
// rule response
|
||||
Rules []RuleResponse
|
||||
Rules []RuleResponse `json:"rules"`
|
||||
// ValidationFailureAction: audit,enforce(default)
|
||||
ValidationFailureAction string
|
||||
}
|
||||
|
@ -32,32 +32,32 @@ type PolicyResponse struct {
|
|||
//ResourceSpec resource action applied on
|
||||
type ResourceSpec struct {
|
||||
//TODO: support ApiVersion
|
||||
Kind string
|
||||
APIVersion string
|
||||
Namespace string
|
||||
Name string
|
||||
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
|
||||
ProcessingTime time.Duration `json:"processingTime"`
|
||||
// Count of rules that were applied succesfully
|
||||
RulesAppliedCount int
|
||||
RulesAppliedCount int `json:"rulesAppliedCount"`
|
||||
}
|
||||
|
||||
//RuleResponse details for each rule applicatino
|
||||
type RuleResponse struct {
|
||||
// rule name specified in policy
|
||||
Name string
|
||||
Name string `json:"name"`
|
||||
// rule type (Mutation,Generation,Validation) for Kyverno Policy
|
||||
Type string
|
||||
Type string `json:"type"`
|
||||
// message response from the rule application
|
||||
Message string
|
||||
Message string `json:"message"`
|
||||
// JSON patches, for mutation rules
|
||||
Patches [][]byte
|
||||
Patches [][]byte `json:"patches,omitempty"`
|
||||
// success/fail
|
||||
Success bool
|
||||
Success bool `json:"success"`
|
||||
// statistics
|
||||
RuleStats
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func (rr RuleResponse) ToString() string {
|
|||
//RuleStats stores the statisctis for the single rule application
|
||||
type RuleStats struct {
|
||||
// time required to appliy the rule on the resource
|
||||
ProcessingTime time.Duration
|
||||
ProcessingTime time.Duration `json:"processingTime"`
|
||||
}
|
||||
|
||||
//IsSuccesful checks if any rule has failed or not
|
||||
|
|
391
pkg/testrunner/scenario.go
Normal file
391
pkg/testrunner/scenario.go
Normal file
|
@ -0,0 +1,391 @@
|
|||
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)
|
||||
t.Log(client)
|
||||
// apply policy
|
||||
// convert policy -> kyverno.Policy
|
||||
policy := loadPolicy(t, tc.Input.Policy)
|
||||
fmt.Println(policy)
|
||||
// convert resource -> unstructured.Unstructured
|
||||
resource := loadPolicyResource(t, tc.Input.Resource)
|
||||
glog.Info(resource)
|
||||
|
||||
var er engine.EngineResponseNew
|
||||
// Mutation
|
||||
er = engine.MutateNew(*policy, *resource)
|
||||
func() {
|
||||
if data, err := json.Marshal(er.PolicyResponse); err == nil {
|
||||
t.Log(string(data))
|
||||
fmt.Println(string(data))
|
||||
for _, r := range er.PolicyResponse.Rules {
|
||||
for _, p := range r.Patches {
|
||||
fmt.Println(string(p))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource)
|
||||
validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse)
|
||||
|
||||
// Validation
|
||||
// only compare the parametes specified ?
|
||||
return true
|
||||
}
|
||||
|
||||
func validateResource(t *testing.T, responseResource unstructured.Unstructured, expectedResourceFile string) {
|
||||
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 := func(obj unstructured.Unstructured) {
|
||||
if data, err := obj.MarshalJSON(); err == nil {
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}
|
||||
resourcePrint(responseResource)
|
||||
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) {
|
||||
// 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.Log("Policy: error")
|
||||
}
|
||||
// compare resource spec
|
||||
if er.Resource != expected.Resource {
|
||||
t.Log("Resource: error")
|
||||
}
|
||||
// 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.Logf("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.Log("error: typw")
|
||||
}
|
||||
// message
|
||||
if rule.Message != expectedRule.Message {
|
||||
t.Log("error: message")
|
||||
}
|
||||
// // patches
|
||||
// if reflect.DeepEqual(rule.Patches, expectedRule.Patches) {
|
||||
// t.Log("error: patches")
|
||||
// }
|
||||
// success
|
||||
if rule.Success != expectedRule.Success {
|
||||
t.Log("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]
|
||||
}
|
|
@ -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,17 @@ 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_Devlop(t *testing.T) {
|
||||
//load scenario
|
||||
scenario, err := loadScenario(t, "/test/scenarios/test/s1.yaml")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
runScenario(t, scenario)
|
||||
|
||||
}
|
||||
|
|
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"}]`
|
Loading…
Reference in a new issue