Skip to content

Commit 2c1aa26

Browse files
committed
Implement object interfaces as functional only and provide helpers
1 parent 09626e5 commit 2c1aa26

16 files changed

+280
-399
lines changed

pkg/webhook/base.go

Lines changed: 0 additions & 35 deletions
This file was deleted.

pkg/webhook/handler.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,11 @@ func (h *handler) Handle(ctx context.Context, req admission.Request) admission.R
7070
}
7171

7272
// invoke mutator
73-
if req.Object.Object != nil {
74-
if mutator, ok := h.Handler.(Mutator); ok {
75-
// invoke mutator
73+
if mutator, ok := h.Handler.(Mutator); ok {
74+
if req.Object.Object != nil {
7675
return mutator.Mutate(ctx, req)
76+
} else {
77+
return admission.Allowed("")
7778
}
7879
}
7980

pkg/webhook/handler_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,38 @@ var _ = Describe("Handler", func() {
3333
result := (&handler{}).Handle(context.TODO(), admission.Request{})
3434
Ω(result.Allowed).Should(BeFalse())
3535
})
36+
It("should mutate", func() {
37+
pod := &corev1.Pod{
38+
ObjectMeta: metav1.ObjectMeta{
39+
Name: "foo",
40+
Namespace: "bar",
41+
},
42+
}
43+
raw, err := json.Marshal(pod)
44+
Ω(err).ShouldNot(HaveOccurred())
45+
46+
h := handler{
47+
Handler: &MutateFunc{
48+
Func: func(ctx context.Context, request admission.Request) admission.Response {
49+
return admission.Allowed("")
50+
},
51+
},
52+
Object: &corev1.Pod{},
53+
}
54+
err = h.InjectDecoder(decoder)
55+
Ω(err).ShouldNot(HaveOccurred())
56+
result := h.Handle(context.TODO(), admission.Request{
57+
AdmissionRequest: admissionv1.AdmissionRequest{
58+
Object: runtime.RawExtension{
59+
Raw: raw,
60+
},
61+
Operation: admissionv1.Create,
62+
},
63+
})
64+
Ω(result.Allowed).Should(BeTrue())
65+
result = h.Handle(context.TODO(), admission.Request{})
66+
Ω(result.Allowed).Should(BeTrue())
67+
})
3668
It("should validate", func() {
3769
pod := &corev1.Pod{
3870
ObjectMeta: metav1.ObjectMeta{

pkg/webhook/injection.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package webhook
2+
3+
import (
4+
"sigs.k8s.io/controller-runtime/pkg/client"
5+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
6+
)
7+
8+
// InjectedClient holds an injected client.Client
9+
type InjectedClient struct {
10+
Client client.Client
11+
}
12+
13+
// InjectClient implements the inject.Client interface.
14+
func (i *InjectedClient) InjectClient(client client.Client) error {
15+
i.Client = client
16+
return nil
17+
}
18+
19+
// InjectedDecoder holds an injected admission.Decoder
20+
type InjectedDecoder struct {
21+
Decoder *admission.Decoder
22+
}
23+
24+
// InjectDecoder implements the admission.DecoderInjector interface.
25+
func (i *InjectedDecoder) InjectDecoder(decoder *admission.Decoder) error {
26+
i.Decoder = decoder
27+
return nil
28+
}

pkg/webhook/mutating_simple_webhook.go

Lines changed: 0 additions & 71 deletions
This file was deleted.

pkg/webhook/mutating_simple_webhook_private_test.go

Lines changed: 0 additions & 36 deletions
This file was deleted.

pkg/webhook/mutating_simple_webhook_test.go

Lines changed: 0 additions & 30 deletions
This file was deleted.

pkg/webhook/mutating_webhook.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package webhook
22

33
import (
44
"context"
5+
"encoding/json"
6+
"net/http"
57

8+
"k8s.io/apimachinery/pkg/runtime"
69
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
710
)
811

9-
// Mutator specifies the interface for a mutating webhook.
12+
// Mutator specifies the interface for a generic mutating webhook.
1013
type Mutator interface {
1114
// Mutate yields a response to an mutating AdmissionRequest.
1215
Mutate(context.Context, admission.Request) admission.Response
@@ -17,7 +20,8 @@ var _ Mutator = &MutatingWebhook{}
1720

1821
// MutatingWebhook is a generic mutating admission webhook.
1922
type MutatingWebhook struct {
20-
baseHandler
23+
InjectedClient
24+
InjectedDecoder
2125
}
2226

2327
// Mutate implements the Mutator interface.
@@ -40,3 +44,34 @@ func (m *MutateFunc) Mutate(ctx context.Context, req admission.Request) admissio
4044

4145
return m.MutatingWebhook.Mutate(ctx, req)
4246
}
47+
48+
// MutateObjectFunc is a functional interface for an object mutating admission webhook.
49+
type MutateObjectFunc struct {
50+
MutatingWebhook
51+
52+
Func func(context.Context, admission.Request, runtime.Object) error
53+
}
54+
55+
// Mutate implements the Mutator interface by calling the Func using the request's runtime.Object.
56+
func (m *MutateObjectFunc) Mutate(ctx context.Context, req admission.Request) admission.Response {
57+
if m.Func != nil {
58+
return MutateObjectByFunc(ctx, req, m.Func)
59+
}
60+
61+
return m.MutatingWebhook.Mutate(ctx, req)
62+
}
63+
64+
func MutateObjectByFunc(ctx context.Context, req admission.Request, f func(context.Context, admission.Request, runtime.Object) error) admission.Response {
65+
obj := req.Object.Object
66+
err := f(ctx, req, obj)
67+
if err != nil {
68+
return admission.Errored(http.StatusInternalServerError, err)
69+
}
70+
71+
marshalled, err := json.Marshal(obj)
72+
if err != nil {
73+
return admission.Errored(http.StatusInternalServerError, err)
74+
}
75+
76+
return admission.PatchResponseFromRaw(req.Object.Raw, marshalled)
77+
}

pkg/webhook/mutating_webhook_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ package webhook_test
22

33
import (
44
"context"
5+
"encoding/json"
6+
"errors"
57
. "github.com/onsi/ginkgo"
68
. "github.com/onsi/gomega"
79

810
"github.com/snorwin/k8s-generic-webhook/pkg/webhook"
11+
admissionv1 "k8s.io/api/admission/v1"
12+
corev1 "k8s.io/api/core/v1"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/runtime"
915
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
1016
)
1117

@@ -24,4 +30,55 @@ var _ = Describe("Mutating Webhook", func() {
2430
Ω(result.Allowed).Should(BeFalse())
2531
})
2632
})
33+
Context("MutateObjectFunc", func() {
34+
var (
35+
n *corev1.Namespace
36+
raw []byte
37+
)
38+
BeforeEach(func() {
39+
var err error
40+
n = &corev1.Namespace{
41+
ObjectMeta: metav1.ObjectMeta{
42+
Name: "foo",
43+
},
44+
}
45+
raw, err = json.Marshal(n)
46+
Ω(err).ShouldNot(HaveOccurred())
47+
})
48+
It("should by default allow all", func() {
49+
result := (&webhook.MutateObjectFunc{}).Mutate(context.TODO(), admission.Request{})
50+
Ω(result.Allowed).Should(BeTrue())
51+
})
52+
It("should use defined functions", func() {
53+
result := (&webhook.MutateObjectFunc{
54+
Func: func(ctx context.Context, _ admission.Request, object runtime.Object) error {
55+
Ω(object).Should(Equal(n))
56+
return nil
57+
},
58+
}).Mutate(context.TODO(), admission.Request{
59+
AdmissionRequest: admissionv1.AdmissionRequest{
60+
Object: runtime.RawExtension{
61+
Object: n,
62+
Raw: raw,
63+
},
64+
},
65+
})
66+
Ω(result.Allowed).Should(BeTrue())
67+
})
68+
It("should deny if error", func() {
69+
result := (&webhook.MutateObjectFunc{
70+
Func: func(ctx context.Context, _ admission.Request, _ runtime.Object) error {
71+
return errors.New("")
72+
},
73+
}).Mutate(context.TODO(), admission.Request{
74+
AdmissionRequest: admissionv1.AdmissionRequest{
75+
Object: runtime.RawExtension{
76+
Object: n,
77+
Raw: raw,
78+
},
79+
},
80+
})
81+
Ω(result.Allowed).Should(BeFalse())
82+
})
83+
})
2784
})

0 commit comments

Comments
 (0)