Skip to content

Commit

Permalink
add tests for simplified webhook interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Mengqi Yu committed Mar 12, 2019
1 parent 8810470 commit 901b6f8
Show file tree
Hide file tree
Showing 5 changed files with 323 additions and 15 deletions.
18 changes: 13 additions & 5 deletions pkg/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/client/config"
Expand Down Expand Up @@ -249,9 +250,6 @@ func (blder *Builder) doWebhook() error {
return err
}

partialPath := strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)

// TODO: When the conversion webhook lands, we need to handle all registered versions of a given group-kind.
// A potential workflow for defaulting webhook
// 1) a bespoke (non-hub) version comes in
Expand All @@ -267,7 +265,7 @@ func (blder *Builder) doWebhook() error {
if defaulter, isDefaulter := blder.apiType.(admission.Defaulter); isDefaulter {
mwh := admission.DefaultingWebhookFor(defaulter)
if mwh != nil {
path := "/mutate-" + partialPath
path := generateMutatePath(gvk)
log.Info("Registering a mutating webhook",
"GVK", gvk,
"path", path)
Expand All @@ -279,7 +277,7 @@ func (blder *Builder) doWebhook() error {
if validator, isValidator := blder.apiType.(admission.Validator); isValidator {
vwh := admission.ValidatingWebhookFor(validator)
if vwh != nil {
path := "/validate-" + partialPath
path := generateValidatePath(gvk)
log.Info("Registering a validating webhook",
"GVK", gvk,
"path", path)
Expand All @@ -289,3 +287,13 @@ func (blder *Builder) doWebhook() error {

return err
}

func generateMutatePath(gvk schema.GroupVersionKind) string {
return "/mutate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}

func generateValidatePath(gvk schema.GroupVersionKind) string {
return "/validate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}
157 changes: 152 additions & 5 deletions pkg/builder/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ import (
"fmt"
"strings"

"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/source"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
Expand All @@ -33,10 +30,13 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/scheme"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

var _ = Describe("application", func() {
Expand All @@ -47,7 +47,6 @@ var _ = Describe("application", func() {
getConfig = func() (*rest.Config, error) { return cfg, nil }
newController = controller.New
newManager = manager.New
getGvk = apiutil.GVKForObject
})

AfterEach(func() {
Expand Down Expand Up @@ -121,6 +120,93 @@ var _ = Describe("application", func() {
Expect(err.Error()).To(ContainSubstring("expected error"))
Expect(instance).To(BeNil())
})

It("should scaffold a defaulting webhook if the type implements the Defaulter interface", func() {
By("creating a controller manager")
m, err := manager.New(cfg, manager.Options{})
Expect(err).NotTo(HaveOccurred())

By("registering the type in the Scheme")
builder := scheme.Builder{GroupVersion: schema.GroupVersion{Group: "foo.test.org", Version: "v1"}}
builder.Register(&TestDefaulter{}, &TestDefaulterList{})
err = builder.AddToScheme(m.GetScheme())
Expect(err).NotTo(HaveOccurred())

instance, err := ControllerManagedBy(m).
For(&TestDefaulter{}).
Owns(&appsv1.ReplicaSet{}).
Build(noop)
Expect(err).NotTo(HaveOccurred())
Expect(instance).NotTo(BeNil())
svr := m.GetWebhookServer()
Expect(svr).NotTo(BeNil())

By("trying to register an existing mutating webhook path")
path := generateMutatePath(schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestDefaulter"})
Ω(func() { svr.Register(path, nil) }).Should(Panic())

By("registering a validating webhook path")
path = generateValidatePath(schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestDefaulter"})
Ω(func() { svr.Register(path, nil) }).ShouldNot(Panic())
})

It("should scaffold a validating webhook if the type implements the Validator interface", func() {
By("creating a controller manager")
m, err := manager.New(cfg, manager.Options{})
Expect(err).NotTo(HaveOccurred())

By("registering the type in the Scheme")
builder := scheme.Builder{GroupVersion: schema.GroupVersion{Group: "foo.test.org", Version: "v1"}}
builder.Register(&TestValidator{}, &TestValidatorList{})
err = builder.AddToScheme(m.GetScheme())
Expect(err).NotTo(HaveOccurred())

instance, err := ControllerManagedBy(m).
For(&TestValidator{}).
Owns(&appsv1.ReplicaSet{}).
Build(noop)
Expect(err).NotTo(HaveOccurred())
Expect(instance).NotTo(BeNil())
svr := m.GetWebhookServer()
Expect(svr).NotTo(BeNil())

By("registering a mutating webhook path")
path := generateMutatePath(schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestValidator"})
Ω(func() { svr.Register(path, nil) }).ShouldNot(Panic())

By("trying to register an existing validating webhook path")
path = generateValidatePath(schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestValidator"})
Ω(func() { svr.Register(path, nil) }).Should(Panic())
})

It("should scaffold defaulting and validating webhooks if the type implements both Defaulter and Validator interfaces", func() {
By("creating a controller manager")
m, err := manager.New(cfg, manager.Options{})
Expect(err).NotTo(HaveOccurred())

By("registering the type in the Scheme")
builder := scheme.Builder{GroupVersion: schema.GroupVersion{Group: "foo.test.org", Version: "v1"}}
builder.Register(&TestDefaultValidator{}, &TestDefaultValidatorList{})
err = builder.AddToScheme(m.GetScheme())
Expect(err).NotTo(HaveOccurred())

instance, err := ControllerManagedBy(m).
For(&TestDefaultValidator{}).
Owns(&appsv1.ReplicaSet{}).
Build(noop)
Expect(err).NotTo(HaveOccurred())
Expect(instance).NotTo(BeNil())
svr := m.GetWebhookServer()
Expect(svr).NotTo(BeNil())

By("trying to register an existing mutating webhook path")
path := generateMutatePath(schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestDefaultValidator"})
Ω(func() { svr.Register(path, nil) }).Should(Panic())

By("trying to register an existing validating webhook path")
path = generateValidatePath(schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestDefaultValidator"})
Ω(func() { svr.Register(path, nil) }).Should(Panic())
})
})

Describe("Start with SimpleController", func() {
Expand Down Expand Up @@ -281,3 +367,64 @@ type fakeType struct{}

func (*fakeType) GetObjectKind() schema.ObjectKind { return nil }
func (*fakeType) DeepCopyObject() runtime.Object { return nil }

// TestDefaulter
var _ runtime.Object = &TestDefaulter{}

type TestDefaulter struct{}

func (*TestDefaulter) GetObjectKind() schema.ObjectKind { return nil }
func (*TestDefaulter) DeepCopyObject() runtime.Object { return nil }

var _ runtime.Object = &TestDefaulterList{}

type TestDefaulterList struct{}

func (*TestDefaulterList) GetObjectKind() schema.ObjectKind { return nil }
func (*TestDefaulterList) DeepCopyObject() runtime.Object { return nil }

func (*TestDefaulter) Default() {}

// TestValidator
var _ runtime.Object = &TestValidator{}

type TestValidator struct{}

func (*TestValidator) GetObjectKind() schema.ObjectKind { return nil }
func (*TestValidator) DeepCopyObject() runtime.Object { return nil }

var _ runtime.Object = &TestValidatorList{}

type TestValidatorList struct{}

func (*TestValidatorList) GetObjectKind() schema.ObjectKind { return nil }
func (*TestValidatorList) DeepCopyObject() runtime.Object { return nil }

var _ admission.Validator = &TestValidator{}

func (*TestValidator) ValidateCreate() error { return nil }

func (*TestValidator) ValidateUpdate(old runtime.Object) error { return nil }

// TestDefaultValidator
var _ runtime.Object = &TestDefaultValidator{}

type TestDefaultValidator struct{}

func (*TestDefaultValidator) GetObjectKind() schema.ObjectKind { return nil }
func (*TestDefaultValidator) DeepCopyObject() runtime.Object { return nil }

var _ runtime.Object = &TestDefaultValidatorList{}

type TestDefaultValidatorList struct{}

func (*TestDefaultValidatorList) GetObjectKind() schema.ObjectKind { return nil }
func (*TestDefaultValidatorList) DeepCopyObject() runtime.Object { return nil }

func (*TestDefaultValidator) Default() {}

var _ admission.Validator = &TestDefaultValidator{}

func (*TestDefaultValidator) ValidateCreate() error { return nil }

func (*TestDefaultValidator) ValidateUpdate(old runtime.Object) error { return nil }
85 changes: 84 additions & 1 deletion pkg/builder/builder_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
Expand All @@ -42,7 +44,88 @@ var cfg *rest.Config
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))

testenv = &envtest.Environment{}
testenv = &envtest.Environment{
CRDs: []*apiextensionsv1beta1.CustomResourceDefinition{
{
TypeMeta: metav1.TypeMeta{
APIVersion: "apiextensions.k8s.io",
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "testdefaulters.foo.test.org",
},

Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: "foo.test.org",
Version: "v1",
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "testdefaulters",
Singular: "testdefaulter",
Kind: "TestDefaulter",
},
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
},
},
},
},
{
TypeMeta: metav1.TypeMeta{
APIVersion: "apiextensions.k8s.io",
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "testvalidators.foo.test.org",
},

Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: "foo.test.org",
Version: "v1",
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "testvalidators",
Singular: "testvalidator",
Kind: "TestValidator",
},
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
},
},
},
},
{
TypeMeta: metav1.TypeMeta{
APIVersion: "apiextensions.k8s.io",
Kind: "CustomResourceDefinition",
},
ObjectMeta: metav1.ObjectMeta{
Name: "testdefaultvalidators.foo.test.org",
},

Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: "foo.test.org",
Version: "v1",
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: "testdefaultvalidators",
Singular: "testdefaultvalidator",
Kind: "TestDefaultValidator",
},
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
{
Name: "v1",
Served: true,
Storage: true,
},
},
},
},
},
}

var err error
cfg, err = testenv.Start()
Expand Down
16 changes: 16 additions & 0 deletions pkg/manager/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,22 @@ var _ = Describe("manger.Manager", func() {

close(done)
})

It("should lazily initialize a webhook server if needed", func(done Done) {
By("creating a manager with options")
m, err := New(cfg, Options{Port: 9443, Host: "foo.com"})
Expect(err).NotTo(HaveOccurred())
Expect(m).NotTo(BeNil())

By("checking options are passed to the webhook server")
svr := m.GetWebhookServer()
Expect(svr).NotTo(BeNil())
Expect(svr.Port).To(Equal(9443))
Expect(svr.Host).To(Equal("foo.com"))

close(done)
})

Context("with leader election enabled", func() {
It("should default ID to controller-runtime if ID is not set", func() {
var rl resourcelock.Interface
Expand Down
Loading

0 comments on commit 901b6f8

Please sign in to comment.