mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
generate resources
This commit is contained in:
parent
b5782d25d9
commit
0089eb348e
9 changed files with 178 additions and 41 deletions
examples/generate
pkg
controller
dclient
testutils
|
@ -1,7 +0,0 @@
|
|||
kind: Namespace
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: "ns2"
|
||||
labels:
|
||||
LabelForSelector : "namespace2"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: game-config
|
||||
name: config-template
|
||||
namespace: default
|
||||
labels:
|
||||
originalLabel : isHere
|
|
@ -2,28 +2,39 @@
|
|||
policy: basic-policy
|
||||
resource: ns2
|
||||
initResources:
|
||||
- default/config-template
|
||||
# expected
|
||||
mutation:
|
||||
reason: Success
|
||||
validation:
|
||||
reason: Success
|
||||
generation:
|
||||
resource:
|
||||
- name: default-config
|
||||
namespace: ns2
|
||||
kind: ConfigMap
|
||||
- name: mongo-creds
|
||||
namespace: ns2
|
||||
kind: Secret
|
||||
---
|
||||
# input
|
||||
policy: zk-kafka-address
|
||||
resource: ns2
|
||||
initResources:
|
||||
- default/game-config
|
||||
# expected
|
||||
mutation:
|
||||
reason: Success
|
||||
validation:
|
||||
reason: Failed
|
||||
generation:
|
||||
resource:
|
||||
- name: copied-cm
|
||||
namespace: ns2
|
||||
kind: ConfigMap
|
||||
- name: zk-kafka-address
|
||||
namespace: ns2
|
||||
kind: ConfigMap
|
||||
---
|
||||
# input
|
||||
policy: default
|
||||
resource: ns2
|
||||
initResources:
|
||||
# expected
|
||||
mutation:
|
||||
reason: Success
|
||||
validation:
|
||||
reason: Failed
|
||||
generation:
|
||||
resource:
|
||||
- name: deny-all-traffic
|
||||
namespace: ns2
|
||||
kind: NetworkPolicy
|
|
@ -10,6 +10,7 @@ import (
|
|||
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"
|
||||
"k8s.io/sample-controller/pkg/signals"
|
||||
)
|
||||
|
||||
|
@ -78,8 +79,10 @@ func (f *fixture) setupFixture() {
|
|||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
regresource := map[string]string{"kyverno.io/v1alpha1": "policys"}
|
||||
fclient.SetDiscovery(client.NewFakeDiscoveryClient(regresource))
|
||||
regResources := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Group: "kyverno.io/", Version: "v1alpha1", Resource: "policys"}}
|
||||
|
||||
fclient.SetDiscovery(client.NewFakeDiscoveryClient(regResources))
|
||||
}
|
||||
|
||||
func newPolicy(name string) *types.Policy {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
meta "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"
|
||||
)
|
||||
|
||||
// GetResource
|
||||
|
@ -43,10 +44,12 @@ func newUnstructuredWithSpec(apiVersion, kind, namespace, name string, spec map[
|
|||
func TestClient(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
// init groupversion
|
||||
regresource := map[string]string{"group/version": "thekinds",
|
||||
"group2/version": "thekinds",
|
||||
"v1": "namespaces",
|
||||
"apps/v1": "deployments"}
|
||||
regResource := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"},
|
||||
schema.GroupVersionResource{Group: "group2", Version: "version", Resource: "thekinds"},
|
||||
schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||
}
|
||||
// init resources
|
||||
objects := []runtime.Object{newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
|
||||
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"),
|
||||
|
@ -63,7 +66,7 @@ func TestClient(t *testing.T) {
|
|||
}
|
||||
|
||||
// set discovery Client
|
||||
client.SetDiscovery(NewFakeDiscoveryClient(regresource))
|
||||
client.SetDiscovery(NewFakeDiscoveryClient(regResource))
|
||||
// Get Resource
|
||||
res, err := client.GetResource("thekinds", "ns-foo", "name-foo")
|
||||
if err != nil {
|
||||
|
|
|
@ -36,16 +36,21 @@ func NewMockClient(scheme *runtime.Scheme, objects ...runtime.Object) (*Client,
|
|||
}
|
||||
|
||||
// NewFakeDiscoveryClient returns a fakediscovery client
|
||||
func NewFakeDiscoveryClient(regResources map[string]string) *fakeDiscoveryClient {
|
||||
registeredResources := make([]schema.GroupVersionResource, len(regResources))
|
||||
for groupVersion, resource := range regResources {
|
||||
gv, err := schema.ParseGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
registeredResources = append(registeredResources, gv.WithResource(resource))
|
||||
func NewFakeDiscoveryClient(registeredResouces []schema.GroupVersionResource) *fakeDiscoveryClient {
|
||||
// Load some-preregistd resources
|
||||
res := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "configmaps"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "endpoints"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "namespaces"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "resourcequotas"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "secrets"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "serviceaccounts"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"},
|
||||
}
|
||||
return &fakeDiscoveryClient{registeredResouces: registeredResources}
|
||||
registeredResouces = append(registeredResouces, res...)
|
||||
return &fakeDiscoveryClient{registeredResouces: registeredResouces}
|
||||
}
|
||||
|
||||
type fakeDiscoveryClient struct {
|
||||
|
|
|
@ -9,8 +9,14 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
policytypes "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/result"
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func NewTestBundle(path string) *testBundle {
|
||||
|
@ -143,11 +149,57 @@ type testBundle struct {
|
|||
scenarios []*tScenario
|
||||
}
|
||||
|
||||
func (tb *testBundle) createClient(t *testing.T, resources []string) *dclient.Client {
|
||||
scheme := runtime.NewScheme()
|
||||
objects := []runtime.Object{}
|
||||
// registered group versions
|
||||
regResources := []schema.GroupVersionResource{}
|
||||
for _, resource := range resources {
|
||||
// get resources
|
||||
r, ok := tb.resources[resource]
|
||||
if !ok {
|
||||
glog.Warningf("Resource %s not found", resource)
|
||||
continue
|
||||
}
|
||||
// get group version resource
|
||||
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)
|
||||
if err != nil {
|
||||
glog.Warning("Unable to deocde")
|
||||
continue
|
||||
}
|
||||
// create unstructured
|
||||
rdata, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
unstr := unstructured.Unstructured{Object: rdata}
|
||||
objects = append(objects, &unstr)
|
||||
}
|
||||
// new mock client
|
||||
// Mock Client
|
||||
c, err := dclient.NewMockClient(scheme, objects...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// set discovery Client
|
||||
c.SetDiscovery(dclient.NewFakeDiscoveryClient(regResources))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (tb *testBundle) run(t *testing.T, testingapplyTest IApplyTest) {
|
||||
glog.Infof("Start: test on test bundles %s", tb.path)
|
||||
// run each scenario
|
||||
for _, ts := range tb.scenarios {
|
||||
fmt.Println(tb.path)
|
||||
// TODO create client only for generate
|
||||
// If there are init resources defined then load them
|
||||
c := tb.createClient(t, ts.InitResources)
|
||||
// get policy
|
||||
p, ok := tb.policies[ts.Policy]
|
||||
if !ok {
|
||||
|
@ -161,17 +213,42 @@ func (tb *testBundle) run(t *testing.T, testingapplyTest IApplyTest) {
|
|||
continue
|
||||
}
|
||||
// TODO: handle generate
|
||||
mPatchedResource, mResult, vResult, err := testingapplyTest.applyPolicy(p, r, nil)
|
||||
if ts.Generation != nil {
|
||||
// assuming its namespaces creation
|
||||
decode := kscheme.Codecs.UniversalDeserializer().Decode
|
||||
obj, _, err := decode([]byte(r.rawResource), nil, nil)
|
||||
_, err = c.CreateResource(getResourceFromKind(r.gvk.Kind), "", obj)
|
||||
if err != nil {
|
||||
t.Fatalf("error while creating namespace %s", ts.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
mPatchedResource, mResult, vResult, err := testingapplyTest.applyPolicy(p, r, c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// check the expected scenario
|
||||
tb.checkMutationResult(t, ts.Mutation, mPatchedResource, mResult)
|
||||
tb.checkValidationResult(t, ts.Validation, vResult)
|
||||
tb.checkGeneration(t, ts.Generation, c)
|
||||
}
|
||||
glog.Infof("Done: test on test bundles %s", tb.path)
|
||||
}
|
||||
|
||||
func (tb *testBundle) checkGeneration(t *testing.T, expect *tGeneration, c *dclient.Client) {
|
||||
if expect == nil {
|
||||
glog.Info("No Generatin check defined")
|
||||
return
|
||||
}
|
||||
// iterate throught the expected resources and check if the client has them
|
||||
for _, r := range expect.Resources {
|
||||
_, err := c.GetResource(getResourceFromKind(r.Kind), r.Namespace, r.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Resource %s/%s of kind %s not found", r.Namespace, r.Name, r.Kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tb *testBundle) checkValidationResult(t *testing.T, expect *tValidation, vResult result.Result) {
|
||||
if expect == nil {
|
||||
glog.Info("No Validation check defined")
|
||||
|
|
|
@ -28,7 +28,7 @@ func TestExamples(t *testing.T) {
|
|||
// "/Users/shiv/nirmata/code/go/src/github.com/nirmata/kyverno/examples/cli",
|
||||
// }
|
||||
folders := []string{
|
||||
"/Users/shiv/nirmata/code/go/src/github.com/nirmata/kyverno/examples",
|
||||
"/Users/shiv/nirmata/code/go/src/github.com/nirmata/kyverno/examples/generate",
|
||||
}
|
||||
for _, folder := range folders {
|
||||
runTest(t, folder)
|
||||
|
|
|
@ -114,6 +114,24 @@ func extractResource(resource string) (map[string]*resourceInfo, error) {
|
|||
return resources, nil
|
||||
}
|
||||
|
||||
func ParseApiVersionFromObject(bytes []byte) string {
|
||||
var objectJSON map[string]interface{}
|
||||
json.Unmarshal(bytes, &objectJSON)
|
||||
if apiVersion, ok := objectJSON["apiVersion"].(string); ok {
|
||||
return apiVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ParseKindFromObject(bytes []byte) string {
|
||||
var objectJSON map[string]interface{}
|
||||
json.Unmarshal(bytes, &objectJSON)
|
||||
if kind, ok := objectJSON["kind"].(string); ok {
|
||||
return kind
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
//ParseNameFromObject extracts resource name from JSON obj
|
||||
func ParseNameFromObject(bytes []byte) string {
|
||||
var objectJSON map[string]interface{}
|
||||
|
@ -164,8 +182,8 @@ func (tp *testPolicy) applyPolicy(policy *policytypes.Policy, resource *resource
|
|||
vResult = engine.Validate(*policy, patchedResource, *resource.gvk)
|
||||
}
|
||||
// Generate
|
||||
if client == nil {
|
||||
glog.Warning("Client is required to test generate")
|
||||
if client != nil {
|
||||
engine.Generate(client, *policy, resource.rawResource, *resource.gvk)
|
||||
}
|
||||
|
||||
// transform the patched Resource into resource Info
|
||||
|
@ -181,6 +199,17 @@ type tScenario struct {
|
|||
InitResources []string `yaml:"initResources,omitempty"`
|
||||
Mutation *tMutation `yaml:"mutation,omitempty"`
|
||||
Validation *tValidation `yaml:"validation,omitempty"`
|
||||
Generation *tGeneration `yaml:"generation,omitempty"`
|
||||
}
|
||||
|
||||
type tGeneration struct {
|
||||
Resources []tResource `yaml:"resource"`
|
||||
}
|
||||
|
||||
type tResource struct {
|
||||
Name string `yaml:"name"`
|
||||
Namespace string `yaml:"namespace,omitempty"`
|
||||
Kind string `yaml:"kind"`
|
||||
}
|
||||
|
||||
type tValidation struct {
|
||||
|
@ -263,3 +292,19 @@ func getYAMLfiles(path string) (yamls []string) {
|
|||
}
|
||||
return yamls
|
||||
}
|
||||
|
||||
var kindToResource = map[string]string{
|
||||
"ConfigMap": "configmaps",
|
||||
"Endpoints": "endpoints",
|
||||
"Namespace": "namespaces",
|
||||
"Secret": "secrets",
|
||||
"Deployment": "deployments",
|
||||
"NetworkPolicy": "networkpolicies",
|
||||
}
|
||||
|
||||
func getResourceFromKind(kind string) string {
|
||||
if resource, ok := kindToResource[kind]; ok {
|
||||
return resource
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue