Skip to content

Commit

Permalink
Add tests for internal handler
Browse files Browse the repository at this point in the history
  • Loading branch information
snorwin committed Aug 15, 2021
1 parent 96fd25a commit 62601ec
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 3 deletions.
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
33 changes: 33 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
name: Test
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.16

- name: Checkout
uses: actions/checkout@v2

- name: Test
run: go test ./... -coverprofile cover.out -timeout 30m
env:
CGO_ENABLED: 0
GO111MODULE: on
GOOS: linux
GOARCH: amd64

- name: Send coverage
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: cover.out
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module github.com/snorwin/k8s-generic-webhook
go 1.16

require (
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.14.0
k8s.io/api v0.21.3
k8s.io/apimachinery v0.21.3
sigs.k8s.io/controller-runtime v0.9.6
Expand Down
6 changes: 3 additions & 3 deletions pkg/webhook/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ type handler struct {
func (h *handler) Handle(ctx context.Context, req admission.Request) admission.Response {
// add metadata to context's logger
logger := log.FromContext(ctx).
WithValues("Webhook", req.Kind).
WithValues("uid", req.UID).
WithValues("name", req.Name).
WithValues("namespace", req.Namespace)
WithValues("namespace", req.Namespace).
WithValues("gvk", req.Kind.String()).
WithValues("uid", req.UID)
ctx = log.IntoContext(ctx, logger)

// decode object
Expand Down
213 changes: 213 additions & 0 deletions pkg/webhook/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package webhook

import (
"context"
"encoding/json"

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

admissionv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

var _ = Describe("Handler", func() {
Context("Handle", func() {
var (
decoder *admission.Decoder
)
BeforeEach(func() {
scheme := runtime.NewScheme()
err := corev1.AddToScheme(scheme)
Ω(err).ShouldNot(HaveOccurred())
decoder, err = admission.NewDecoder(scheme)
Ω(err).ShouldNot(HaveOccurred())

})
It("should deny by default", func() {
result := (&handler{}).Handle(context.TODO(), admission.Request{})
Ω(result.Allowed).Should(BeFalse())
})
It("should validate", func() {
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
},
}
raw, err := json.Marshal(pod)
Ω(err).ShouldNot(HaveOccurred())

h := handler{
Handler: &ValidateFuncs{
CreateFunc: func(ctx context.Context, request admission.Request) admission.Response {
return admission.Allowed("")
},
UpdateFunc: func(ctx context.Context, request admission.Request) admission.Response {
return admission.Denied("")
},
DeleteFunc: func(ctx context.Context, request admission.Request) admission.Response {
return admission.Denied("")
},
},
Object: &corev1.Pod{},
}
err = h.InjectDecoder(decoder)
Ω(err).ShouldNot(HaveOccurred())

result := h.Handle(context.TODO(), admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: raw,
},
Operation: admissionv1.Create,
},
})
Ω(result.Allowed).Should(BeTrue())
result = h.Handle(context.TODO(), admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: raw,
},
Operation: admissionv1.Update,
},
})
Ω(result.Allowed).Should(BeFalse())
result = h.Handle(context.TODO(), admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: raw,
},
Operation: admissionv1.Delete,
},
})
Ω(result.Allowed).Should(BeFalse())

})
It("should decode object", func() {
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "bar",
},
Spec: corev1.PodSpec{
NodeName: "jin",
},
}
raw, err := json.Marshal(pod)
Ω(err).ShouldNot(HaveOccurred())

h := handler{
Handler: &MutateFunc{
Func: func(ctx context.Context, request admission.Request) admission.Response {
if len(request.Object.Raw) > 0 {
Ω(request.Object.Object).Should(Equal(pod))
}
if len(request.OldObject.Raw) > 0 {
Ω(request.OldObject.Object).Should(Equal(pod))
}
return admission.Allowed("")
},
},
Object: &corev1.Pod{},
}
err = h.InjectDecoder(decoder)
Ω(err).ShouldNot(HaveOccurred())

result := h.Handle(context.TODO(), admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: raw,
},
},
})
Ω(result.Allowed).Should(BeTrue())

result = h.Handle(context.TODO(), admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
OldObject: runtime.RawExtension{
Raw: raw,
},
},
})
Ω(result.Allowed).Should(BeTrue())
})
It("should not decode invalid object", func() {
h := handler{
Handler: &MutateFunc{
Func: func(ctx context.Context, request admission.Request) admission.Response {
return admission.Allowed("")
},
},
Object: &corev1.Pod{},
}
err := h.InjectDecoder(decoder)
Ω(err).ShouldNot(HaveOccurred())

result := h.Handle(context.TODO(), admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: []byte{1, 2, 3, 4, 5},
},
},
})
Ω(result.Allowed).Should(BeFalse())

result = h.Handle(context.TODO(), admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
OldObject: runtime.RawExtension{
Raw: []byte{1, 2, 3, 4, 5},
},
},
})
Ω(result.Allowed).Should(BeFalse())
})
})
Context("InjectDecoder", func() {
var (
decoder *admission.Decoder
)
BeforeEach(func() {
decoder = &admission.Decoder{}
})
It("should pass decoder to validating webhook", func() {
webhook := ValidatingWebhook{}
Ω((&handler{Handler: &webhook}).InjectDecoder(decoder)).ShouldNot(HaveOccurred())
Ω(webhook.Decoder).Should(Equal(decoder))
})
It("should pass decoder to mutating webhook", func() {
webhook := MutatingWebhook{}
Ω((&handler{Handler: &webhook}).InjectDecoder(decoder)).ShouldNot(HaveOccurred())
Ω(webhook.Decoder).Should(Equal(decoder))
})
It("should never fail if handler not set", func() {
Ω((&handler{}).InjectDecoder(decoder)).ShouldNot(HaveOccurred())
})
})
Context("InjectClient", func() {
var (
client client.Client
)
BeforeEach(func() {
client = fake.NewClientBuilder().Build()
})
It("should pass client to validating webhook", func() {
webhook := ValidatingWebhook{}
Ω((&handler{Handler: &webhook}).InjectClient(client)).ShouldNot(HaveOccurred())
Ω(webhook.Client).Should(Equal(client))
})
It("should pass client to mutating webhook", func() {
webhook := MutatingWebhook{}
Ω((&handler{Handler: &webhook}).InjectClient(client)).ShouldNot(HaveOccurred())
Ω(webhook.Client).Should(Equal(client))
})
It("should never fail if handler not set", func() {
Ω((&handler{}).InjectClient(client)).ShouldNot(HaveOccurred())
})
})
})
28 changes: 28 additions & 0 deletions pkg/webhook/mutating_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package webhook_test

import (
"context"

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

"github.com/snorwin/k8s-generic-webhook/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

var _ = Describe("Mutating Webhook", func() {
Context("MutateFunc", func() {
It("should by default allow all", func() {
result := (&webhook.MutateFunc{}).Mutate(context.TODO(), admission.Request{})
Ω(result.Allowed).Should(BeTrue())
})
It("should use defined functions", func() {
result := (&webhook.MutateFunc{
Func: func(ctx context.Context, _ admission.Request) admission.Response {
return admission.Denied("")
},
}).Mutate(context.TODO(), admission.Request{})
Ω(result.Allowed).Should(BeFalse())
})
})
})
32 changes: 32 additions & 0 deletions pkg/webhook/validating_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package webhook_test

import (
"context"

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

"github.com/snorwin/k8s-generic-webhook/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

var _ = Describe("Validating Webhook", func() {
Context("ValidateFuncs", func() {
It("should by default allow all", func() {
result := (&webhook.ValidateFuncs{}).ValidateCreate(context.TODO(), admission.Request{})
Ω(result.Allowed).Should(BeTrue())
result = (&webhook.ValidateFuncs{}).ValidateUpdate(context.TODO(), admission.Request{})
Ω(result.Allowed).Should(BeTrue())
result = (&webhook.ValidateFuncs{}).ValidateDelete(context.TODO(), admission.Request{})
Ω(result.Allowed).Should(BeTrue())
})
It("should use defined functions", func() {
result := (&webhook.ValidateFuncs{
CreateFunc: func(ctx context.Context, _ admission.Request) admission.Response {
return admission.Denied("")
},
}).ValidateCreate(context.TODO(), admission.Request{})
Ω(result.Allowed).Should(BeFalse())
})
})
})
13 changes: 13 additions & 0 deletions pkg/webhook/webhook_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package webhook_test

import (
"testing"

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

func TestWebhook(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Webhook Test Suite")
}

0 comments on commit 62601ec

Please sign in to comment.