Skip to content
This repository has been archived by the owner on Jun 8, 2022. It is now read-only.

Commit

Permalink
add validation handler test
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanzhang-oss committed Sep 9, 2020
1 parent 0096ecd commit ad1378a
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 128 deletions.
6 changes: 3 additions & 3 deletions pkg/oam/util/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,10 @@ func Object2Map(obj interface{}) (map[string]interface{}, error) {
return res, err
}

// DumpJSON returns the JSON encoding
func DumpJSON(o interface{}) string {
// JsonMarshal returns the JSON encoding
func JsonMarshal(o interface{}) []byte {
j, _ := json.Marshal(o)
return string(j)
return j
}

// GenTraitName generate trait name
Expand Down
67 changes: 61 additions & 6 deletions pkg/webhook/v1alpha2/component/component_suite_test.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,85 @@
package component_test

import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"os"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"github.com/crossplane/oam-kubernetes-runtime/apis/core"
)

var scheme = runtime.NewScheme()
var crd crdv1.CustomResourceDefinition
var reqResource metav1.GroupVersionResource
var decoder *admission.Decoder

func TestMetrics(t *testing.T) {
func TestComponentWebHandler(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Metrics Suite")
RunSpecs(t, "Component Web handler")
}

var _ = BeforeSuite(func(done Done) {
By("Bootstrapping test environment")
err := clientgoscheme.AddToScheme(scheme)
ctrl.SetLogger(zap.New(func(o *zap.Options) {
o.Development = true
o.DestWritter = os.Stdout
}))
By("Setup scheme")
err := core.AddToScheme(scheme)
Expect(err).Should(BeNil())
err = core.AddToScheme(scheme)
err = clientgoscheme.AddToScheme(scheme)
Expect(err).Should(BeNil())
err = crdv1.AddToScheme(scheme)
// the crd we will refer to
crd = crdv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "foo.example.com",
Labels: map[string]string{"crd": "dependency"},
},
Spec: crdv1.CustomResourceDefinitionSpec{
Group: "example.com",
Names: crdv1.CustomResourceDefinitionNames{
Kind: "Foo",
ListKind: "FooList",
Plural: "foo",
Singular: "foo",
},
Versions: []crdv1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: &crdv1.CustomResourceValidation{
OpenAPIV3Schema: &crdv1.JSONSchemaProps{
Type: "object",
Properties: map[string]crdv1.JSONSchemaProps{
"status": {
Type: "object",
Properties: map[string]crdv1.JSONSchemaProps{
"key": {Type: "string"},
},
},
},
},
},
},
},
Scope: crdv1.NamespaceScoped,
},
}
By("Prepare for the admission resource")
reqResource = metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1alpha2", Resource: "components"}
By("Prepare for the admission decoder")
decoder, err = admission.NewDecoder(scheme)
Expect(err).Should(BeNil())
By("Finished test bootstrap")
close(done)
})
207 changes: 115 additions & 92 deletions pkg/webhook/v1alpha2/component/component_test.go
Original file line number Diff line number Diff line change
@@ -1,98 +1,31 @@
package component_test

import (
"context"
"fmt"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
admissionv1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/crossplane/oam-kubernetes-runtime/pkg/oam/util"
. "github.com/crossplane/oam-kubernetes-runtime/pkg/webhook/v1alpha2/component"
)

var _ = Describe("Metrics Admission controller Test", func() {
var _ = Describe("Component Admission controller Test", func() {
var component v1alpha2.Component
var crd crdv1.CustomResourceDefinition
var componentName, namespace, workloadName string
var componentName, namespace string
var label map[string]string
BeforeEach(func() {
crd = crdv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "foo.example.com",
Labels: map[string]string{"crd": "dependency"},
},
Spec: crdv1.CustomResourceDefinitionSpec{
Group: "example.com",
Names: crdv1.CustomResourceDefinitionNames{
Kind: "Foo",
ListKind: "FooList",
Plural: "foo",
Singular: "foo",
},
Versions: []crdv1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
Schema: &crdv1.CustomResourceValidation{
OpenAPIV3Schema: &crdv1.JSONSchemaProps{
Type: "object",
Properties: map[string]crdv1.JSONSchemaProps{
"status": {
Type: "object",
Properties: map[string]crdv1.JSONSchemaProps{
"key": {Type: "string"},
},
},
},
},
},
},
},
Scope: crdv1.NamespaceScoped,
},
}
label := map[string]string{"workload": "deployment"}

wl := appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: workloadName,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
MatchLabels: label,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Labels: label,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "wordpress",
Image: "wordpress:4.6.1-apache",
Ports: []corev1.ContainerPort{
{
Name: "wordpress",
HostPort: 80,
ContainerPort: 8080,
},
},
},
},
},
},
},
}
// reflect workload gvk from scheme
gvks, _, _ := scheme.ObjectKinds(&wl)
wl.APIVersion = gvks[0].GroupVersion().String()
wl.Kind = gvks[0].Kind
namespace = "component-test"
label = map[string]string{"workload": "deployment"}
// Create a component definition
componentName = "example-deployment-workload"
component = v1alpha2.Component{
Expand All @@ -102,9 +35,6 @@ var _ = Describe("Metrics Admission controller Test", func() {
Labels: label,
},
Spec: v1alpha2.ComponentSpec{
Workload: runtime.RawExtension{
Object: &wl,
},
Parameters: []v1alpha2.ComponentParameter{
{
Name: "image",
Expand All @@ -115,20 +45,113 @@ var _ = Describe("Metrics Admission controller Test", func() {
},
}
})
/*
Context("Test Mutation Webhook", func() {
var handler MutatingHandler
var workloadName string
BeforeEach(func() {
handler = MutatingHandler{
Context("Test Mutation Webhook", func() {
It("Test with fill in the gvk", func() {
Expect(crd.Spec.Versions).Should(Equal(1))
Expect(component.Spec).NotTo(BeNil())
})
}
})
})
It("Test mutate workload as a full CR works", func() {
var workload unstructured.Unstructured
workload.SetNamespace(namespace)
workload.SetName(workloadName)
workload.SetLabels(label)
Context("Test Validating Webhook", func() {
It("Test validating CR", func() {
Expect(crd.Spec.Versions).Should(Equal(1))
Expect(component.Spec).NotTo(BeNil())
})
})
})
It("Test workload as type will fill in the gvk", func() {
Expect(crd.Spec.Versions).Should(Equal(1))
Expect(component.Spec).NotTo(BeNil())
})
*/
It("Test validating handler", func() {
var handler admission.Handler = &ValidatingHandler{}
decoderInjector := handler.(admission.DecoderInjector)
decoderInjector.InjectDecoder(decoder)
By("Creating valid workload")
validWorkload := unstructured.Unstructured{}
validWorkload.SetAPIVersion("validAPI")
validWorkload.SetKind("validKind")
By("Creating invalid workload with type")
workloadWithType := validWorkload.DeepCopy()
typeContent := make(map[string]interface{})
typeContent[TypeField] = "should not be here"
workloadWithType.SetUnstructuredContent(typeContent)
By("Creating invalid workload without kind")
noKindWorkload := validWorkload.DeepCopy()
noKindWorkload.SetKind("")
tests := map[string]struct {
workload interface{}
operation admissionv1beta1.Operation
pass bool
reason string
}{
"valid create case": {
workload: validWorkload.DeepCopyObject(),
operation: admissionv1beta1.Create,
pass: true,
reason: "",
},
"valid update case": {
workload: validWorkload.DeepCopyObject(),
operation: admissionv1beta1.Update,
pass: true,
reason: "",
},
"malformat component": {
workload: "bad format",
operation: admissionv1beta1.Create,
pass: false,
reason: "the workload is malformat",
},
"workload still has type": {
workload: workloadWithType.DeepCopyObject(),
operation: admissionv1beta1.Create,
pass: false,
reason: "the workload contains type info",
},
"no kind workload component": {
workload: noKindWorkload.DeepCopyObject(),
operation: admissionv1beta1.Update,
pass: false,
reason: "the workload data missing GVK",
},
}
for testCase, test := range tests {
By(fmt.Sprintf("start test : %s", testCase))
component.Spec.Workload = runtime.RawExtension{Raw: util.JsonMarshal(test.workload)}
req := admission.Request{
AdmissionRequest: admissionv1beta1.AdmissionRequest{
Operation: test.operation,
Resource: reqResource,
Object: runtime.RawExtension{Raw: util.JsonMarshal(component)},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(Equal(test.pass))
if !test.pass {
Expect(string(resp.Result.Reason)).Should(ContainSubstring(test.reason))
}
}
By("Test bad admission request format")
req := admission.Request{
AdmissionRequest: admissionv1beta1.AdmissionRequest{
Operation: admissionv1beta1.Create,
Resource: reqResource,
Object: runtime.RawExtension{Raw: []byte("bad request")},
},
}
resp := handler.Handle(context.TODO(), req)
Expect(resp.Allowed).Should(BeFalse())
})

})
2 changes: 1 addition & 1 deletion pkg/webhook/v1alpha2/component/mutating_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (h *MutatingHandler) Handle(ctx context.Context, req admission.Request) adm
resp := admission.PatchResponseFromRaw(req.AdmissionRequest.Object.Raw, marshalled)
if len(resp.Patches) > 0 {
mutatelog.Info("admit Component",
"namespace", obj.Namespace, "name", obj.Name, "patches", util.DumpJSON(resp.Patches))
"namespace", obj.Namespace, "name", obj.Name, "patches", util.JsonMarshal(resp.Patches))
}
return resp
}
Expand Down
Loading

0 comments on commit ad1378a

Please sign in to comment.