Skip to content

inject client and decoder for webhook #135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func main() {
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
WithManager(mgr).
ForType(&corev1.Pod{}).
Build(&podAnnotator{client: mgr.GetClient(), decoder: mgr.GetAdmissionDecoder()})
Handlers(&podAnnotator{}).
Build()
if err != nil {
entryLog.Error(err, "unable to setup mutating webhook")
os.Exit(1)
Expand All @@ -94,7 +95,8 @@ func main() {
Operations(admissionregistrationv1beta1.Create, admissionregistrationv1beta1.Update).
WithManager(mgr).
ForType(&corev1.Pod{}).
Build(&podValidator{client: mgr.GetClient(), decoder: mgr.GetAdmissionDecoder()})
Handlers(&podValidator{}).
Build()
if err != nil {
entryLog.Error(err, "unable to setup validating webhook")
os.Exit(1)
Expand Down
5 changes: 3 additions & 2 deletions example/mutatingwebhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@ import (
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

// podAnnotator annotates Pods
type podAnnotator struct {
client client.Client
decoder admission.Decoder
decoder types.Decoder
}

// Implement admission.Handler so the controller can handle admission request.
var _ admission.Handler = &podAnnotator{}

// podAnnotator adds an annotation to every incoming pods.
func (a *podAnnotator) Handle(ctx context.Context, req admission.Request) admission.Response {
func (a *podAnnotator) Handle(ctx context.Context, req types.Request) types.Response {
pod := &corev1.Pod{}

err := a.decoder.Decode(req, pod)
Expand Down
5 changes: 3 additions & 2 deletions example/validatingwebhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@ import (
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

// podValidator validates Pods
type podValidator struct {
client client.Client
decoder admission.Decoder
decoder types.Decoder
}

// Implement admission.Handler so the controller can handle admission request.
var _ admission.Handler = &podValidator{}

// podValidator admits a pod iff a specific annotation exists.
func (v *podValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
func (v *podValidator) Handle(ctx context.Context, req types.Request) types.Response {
pod := &corev1.Pod{}

err := v.decoder.Decode(req, pod)
Expand Down
9 changes: 6 additions & 3 deletions pkg/manager/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/recorder"
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

var log = logf.KBLog.WithName("manager")
Expand All @@ -41,7 +41,7 @@ type controllerManager struct {
// to scheme.scheme.
scheme *runtime.Scheme
// admissionDecoder is used to decode an admission.Request.
admissionDecoder admission.Decoder
admissionDecoder types.Decoder

// runnables is the set of Controllers that the controllerManager injects deps into and Starts.
runnables []Runnable
Expand Down Expand Up @@ -112,6 +112,9 @@ func (cm *controllerManager) SetFields(i interface{}) error {
if _, err := inject.StopChannelInto(cm.stop, i); err != nil {
return err
}
if _, err := inject.DecoderInto(cm.admissionDecoder, i); err != nil {
return err
}
return nil
}

Expand All @@ -127,7 +130,7 @@ func (cm *controllerManager) GetScheme() *runtime.Scheme {
return cm.scheme
}

func (cm *controllerManager) GetAdmissionDecoder() admission.Decoder {
func (cm *controllerManager) GetAdmissionDecoder() types.Decoder {
return cm.admissionDecoder
}

Expand Down
6 changes: 4 additions & 2 deletions pkg/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

"github.com/go-logr/logr"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
Expand All @@ -32,6 +33,7 @@ import (
internalrecorder "sigs.k8s.io/controller-runtime/pkg/internal/recorder"
"sigs.k8s.io/controller-runtime/pkg/recorder"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

// Manager initializes shared dependencies such as Caches and Clients, and provides them to Runnables.
Expand All @@ -57,7 +59,7 @@ type Manager interface {
GetScheme() *runtime.Scheme

// GetAdmissionDecoder returns the runtime.Decoder based on the scheme.
GetAdmissionDecoder() admission.Decoder
GetAdmissionDecoder() types.Decoder

// GetClient returns a client configured with the Config
GetClient() client.Client
Expand Down Expand Up @@ -94,7 +96,7 @@ type Options struct {
newCache func(config *rest.Config, opts cache.Options) (cache.Cache, error)
newClient func(config *rest.Config, options client.Options) (client.Client, error)
newRecorderProvider func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger) (recorder.Provider, error)
newAdmissionDecoder func(scheme *runtime.Scheme) (admission.Decoder, error)
newAdmissionDecoder func(scheme *runtime.Scheme) (types.Decoder, error)
}

// Runnable allows a component to be started.
Expand Down
19 changes: 17 additions & 2 deletions pkg/runtime/inject/inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

// Cache is used by the ControllerManager to inject Cache into Sources, EventHandlers, Predicates, and
Expand Down Expand Up @@ -59,15 +60,29 @@ type Client interface {
InjectClient(client.Client) error
}

// ClientInto will set client on i and return the result if it implements client. Returns
//// false if i does not implement client.
// ClientInto will set client on i and return the result if it implements Client. Returns
// false if i does not implement Client.
func ClientInto(client client.Client, i interface{}) (bool, error) {
if s, ok := i.(Client); ok {
return true, s.InjectClient(client)
}
return false, nil
}

// Decoder is used by the ControllerManager to inject decoder into webhook handlers.
type Decoder interface {
InjectDecoder(types.Decoder) error
}

// DecoderInto will set decoder on i and return the result if it implements Decoder. Returns
// false if i does not implement Decoder.
func DecoderInto(decoder types.Decoder, i interface{}) (bool, error) {
if s, ok := i.(Decoder); ok {
return true, s.InjectDecoder(decoder)
}
return false, nil
}

// Scheme is used by the ControllerManager to inject Scheme into Sources, EventHandlers, Predicates, and
// Reconciles
type Scheme interface {
Expand Down
19 changes: 14 additions & 5 deletions pkg/webhook/admission/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ import (

// WebhookBuilder builds a webhook based on the provided options.
type WebhookBuilder struct {
// Name specifies the Name of the webhook. It must be unique in the http
// name specifies the Name of the webhook. It must be unique in the http
// server that serves all the webhooks.
name string

// Path is the URL Path to register this webhook. e.g. "/feature-foo-mutating-pods".
// path is the URL Path to register this webhook. e.g. "/feature-foo-mutating-pods".
path string

// Type specifies the type of the webhook
// handlers are handlers for handling admission request.
handlers []admission.Handler

// t specifies the type of the webhook
t *types.WebhookType
// only one of operations and Rules can be set.
operations []admissionregistrationv1beta1.OperationType
Expand Down Expand Up @@ -128,6 +131,12 @@ func (b *WebhookBuilder) WithManager(mgr manager.Manager) *WebhookBuilder {
return b
}

// Handlers sets the handlers of the webhook.
func (b *WebhookBuilder) Handlers(handlers ...admission.Handler) *WebhookBuilder {
b.handlers = handlers
return b
}

func (b *WebhookBuilder) validate() error {
if b.t == nil {
return errors.New("webhook type cannot be nil")
Expand All @@ -142,7 +151,7 @@ func (b *WebhookBuilder) validate() error {
}

// Build creates the Webhook based on the options provided.
func (b *WebhookBuilder) Build(handlers ...admission.Handler) (*admission.Webhook, error) {
func (b *WebhookBuilder) Build() (*admission.Webhook, error) {
err := b.validate()
if err != nil {
return nil, err
Expand All @@ -153,7 +162,7 @@ func (b *WebhookBuilder) Build(handlers ...admission.Handler) (*admission.Webhoo
Type: *b.t,
FailurePolicy: b.failurePolicy,
NamespaceSelector: b.namespaceSelector,
Handlers: handlers,
Handlers: b.handlers,
}

if len(b.path) == 0 {
Expand Down
17 changes: 6 additions & 11 deletions pkg/webhook/admission/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,16 @@ package admission
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

// Decoder is used to decode AdmissionRequest.
type Decoder interface {
// Decode decodes the raw byte object from the AdmissionRequest to the passed-in runtime.Object.
Decode(Request, runtime.Object) error
}

// DecodeFunc is a function that implements the Decoder interface.
type DecodeFunc func(Request, runtime.Object) error
type DecodeFunc func(types.Request, runtime.Object) error

var _ Decoder = DecodeFunc(nil)
var _ types.Decoder = DecodeFunc(nil)

// Decode implements the Decoder interface.
func (f DecodeFunc) Decode(req Request, obj runtime.Object) error {
func (f DecodeFunc) Decode(req types.Request, obj runtime.Object) error {
return f(req, obj)
}

Expand All @@ -42,12 +37,12 @@ type decoder struct {
}

// NewDecoder creates a Decoder given the runtime.Scheme
func NewDecoder(scheme *runtime.Scheme) (Decoder, error) {
func NewDecoder(scheme *runtime.Scheme) (types.Decoder, error) {
return decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
}

// Decode decodes the inlined object in the AdmissionRequest into the passed-in runtime.Object.
func (d decoder) Decode(req Request, into runtime.Object) error {
func (d decoder) Decode(req types.Request, into runtime.Object) error {
deserializer := d.codecs.UniversalDeserializer()
return runtime.DecodeInto(deserializer, req.AdmissionRequest.Object.Raw, into)
}
5 changes: 3 additions & 2 deletions pkg/webhook/admission/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

var _ = Describe("admission webhook decoder", func() {
var decoder Decoder
var decoder types.Decoder
BeforeEach(func(done Done) {
var err error
decoder, err = NewDecoder(scheme.Scheme)
Expand All @@ -45,7 +46,7 @@ var _ = Describe("admission webhook decoder", func() {
})

Describe("Decode", func() {
req := Request{
req := types.Request{
AdmissionRequest: &admissionv1beta1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: []byte(`{
Expand Down
7 changes: 4 additions & 3 deletions pkg/webhook/admission/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
admissionv1beta1 "k8s.io/api/admission/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
)

var admissionv1beta1scheme = runtime.NewScheme()
Expand Down Expand Up @@ -57,7 +58,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var body []byte
var err error

var reviewResponse Response
var reviewResponse types.Response
if r.Body != nil {
if body, err = ioutil.ReadAll(r.Body); err != nil {
log.Error(err, "unable to read the body from the incoming request")
Expand Down Expand Up @@ -96,11 +97,11 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for k := range wh.KVMap {
ctx = context.WithValue(ctx, ContextKey(k), wh.KVMap[k])
}
reviewResponse = wh.Handle(ctx, Request{AdmissionRequest: ar.Request})
reviewResponse = wh.Handle(ctx, types.Request{AdmissionRequest: ar.Request})
writeResponse(w, reviewResponse)
}

func writeResponse(w io.Writer, response Response) {
func writeResponse(w io.Writer, response types.Response) {
encoder := json.NewEncoder(w)
responseAdmissionReview := v1beta1.AdmissionReview{
Response: response.Response,
Expand Down
7 changes: 4 additions & 3 deletions pkg/webhook/admission/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
. "github.com/onsi/gomega"

admissionv1beta1 "k8s.io/api/admission/v1beta1"
atypes "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
"sigs.k8s.io/controller-runtime/pkg/webhook/types"
)

Expand Down Expand Up @@ -154,10 +155,10 @@ func (nopCloser) Close() error { return nil }
type fakeHandler struct {
invoked bool
valueFromContext string
fn func(context.Context, Request) Response
fn func(context.Context, atypes.Request) atypes.Response
}

func (h *fakeHandler) Handle(ctx context.Context, req Request) Response {
func (h *fakeHandler) Handle(ctx context.Context, req atypes.Request) atypes.Response {
v := ctx.Value(ContextKey("foo"))
if v != nil {
typed, ok := v.(string)
Expand All @@ -169,7 +170,7 @@ func (h *fakeHandler) Handle(ctx context.Context, req Request) Response {
if h.fn != nil {
return h.fn(ctx, req)
}
return Response{Response: &admissionv1beta1.AdmissionResponse{
return atypes.Response{Response: &admissionv1beta1.AdmissionResponse{
Allowed: true,
}}
}
Loading