Skip to content

Commit cd0058a

Browse files
authored
Merge pull request #1952 from k8s-infra-cherrypick-robot/cherry-pick-1950-to-release-0.12
✨ Provide access to admission.Request in custom validator/defaulter
2 parents f561596 + b698f2b commit cd0058a

File tree

5 files changed

+75
-3
lines changed

5 files changed

+75
-3
lines changed

pkg/builder/webhook_test.go

+38-2
Original file line numberDiff line numberDiff line change
@@ -539,11 +539,13 @@ func runTests(admissionReviewVersion string) {
539539
// TestDefaulter.
540540
var _ runtime.Object = &TestDefaulter{}
541541

542+
const testDefaulterKind = "TestDefaulter"
543+
542544
type TestDefaulter struct {
543545
Replica int `json:"replica,omitempty"`
544546
}
545547

546-
var testDefaulterGVK = schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestDefaulter"}
548+
var testDefaulterGVK = schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: testDefaulterKind}
547549

548550
func (d *TestDefaulter) GetObjectKind() schema.ObjectKind { return d }
549551
func (d *TestDefaulter) DeepCopyObject() runtime.Object {
@@ -574,11 +576,13 @@ func (d *TestDefaulter) Default() {
574576
// TestValidator.
575577
var _ runtime.Object = &TestValidator{}
576578

579+
const testValidatorKind = "TestValidator"
580+
577581
type TestValidator struct {
578582
Replica int `json:"replica,omitempty"`
579583
}
580584

581-
var testValidatorGVK = schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: "TestValidator"}
585+
var testValidatorGVK = schema.GroupVersionKind{Group: "foo.test.org", Version: "v1", Kind: testValidatorKind}
582586

583587
func (v *TestValidator) GetObjectKind() schema.ObjectKind { return v }
584588
func (v *TestValidator) DeepCopyObject() runtime.Object {
@@ -694,6 +698,14 @@ func (dv *TestDefaultValidator) ValidateDelete() error {
694698
type TestCustomDefaulter struct{}
695699

696700
func (*TestCustomDefaulter) Default(ctx context.Context, obj runtime.Object) error {
701+
req, err := admission.RequestFromContext(ctx)
702+
if err != nil {
703+
return fmt.Errorf("expected admission.Request in ctx: %w", err)
704+
}
705+
if req.Kind.Kind != testDefaulterKind {
706+
return fmt.Errorf("expected Kind TestDefaulter got %q", req.Kind.Kind)
707+
}
708+
697709
d := obj.(*TestDefaulter) //nolint:ifshort
698710
if d.Replica < 2 {
699711
d.Replica = 2
@@ -708,6 +720,14 @@ var _ admission.CustomDefaulter = &TestCustomDefaulter{}
708720
type TestCustomValidator struct{}
709721

710722
func (*TestCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error {
723+
req, err := admission.RequestFromContext(ctx)
724+
if err != nil {
725+
return fmt.Errorf("expected admission.Request in ctx: %w", err)
726+
}
727+
if req.Kind.Kind != testValidatorKind {
728+
return fmt.Errorf("expected Kind TestValidator got %q", req.Kind.Kind)
729+
}
730+
711731
v := obj.(*TestValidator) //nolint:ifshort
712732
if v.Replica < 0 {
713733
return errors.New("number of replica should be greater than or equal to 0")
@@ -716,6 +736,14 @@ func (*TestCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Obje
716736
}
717737

718738
func (*TestCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error {
739+
req, err := admission.RequestFromContext(ctx)
740+
if err != nil {
741+
return fmt.Errorf("expected admission.Request in ctx: %w", err)
742+
}
743+
if req.Kind.Kind != testValidatorKind {
744+
return fmt.Errorf("expected Kind TestValidator got %q", req.Kind.Kind)
745+
}
746+
719747
v := newObj.(*TestValidator)
720748
old := oldObj.(*TestValidator) //nolint:ifshort
721749
if v.Replica < 0 {
@@ -728,6 +756,14 @@ func (*TestCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj r
728756
}
729757

730758
func (*TestCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error {
759+
req, err := admission.RequestFromContext(ctx)
760+
if err != nil {
761+
return fmt.Errorf("expected admission.Request in ctx: %w", err)
762+
}
763+
if req.Kind.Kind != testValidatorKind {
764+
return fmt.Errorf("expected Kind TestValidator got %q", req.Kind.Kind)
765+
}
766+
731767
v := obj.(*TestValidator) //nolint:ifshort
732768
if v.Replica > 0 {
733769
return errors.New("number of replica should be less than or equal to 0 to delete")

pkg/webhook/admission/defaulter_custom.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package admission
1919
import (
2020
"context"
2121
"encoding/json"
22-
2322
"errors"
2423
"net/http"
2524

@@ -61,6 +60,8 @@ func (h *defaulterForType) Handle(ctx context.Context, req Request) Response {
6160
panic("object should never be nil")
6261
}
6362

63+
ctx = NewContextWithRequest(ctx, req)
64+
6465
// Get the object in the request
6566
obj := h.object.DeepCopyObject()
6667
if err := h.decoder.Decode(req, obj); err != nil {

pkg/webhook/admission/validator_custom.go

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ func (h *validatorForType) Handle(ctx context.Context, req Request) Response {
6464
panic("object should never be nil")
6565
}
6666

67+
ctx = NewContextWithRequest(ctx, req)
68+
6769
// Get the object in the request
6870
obj := h.object.DeepCopyObject()
6971

pkg/webhook/admission/webhook.go

+18
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,21 @@ func StandaloneWebhook(hook *Webhook, opts StandaloneOptions) (http.Handler, err
253253
}
254254
return metrics.InstrumentedHook(opts.MetricsPath, hook), nil
255255
}
256+
257+
// requestContextKey is how we find the admission.Request in a context.Context.
258+
type requestContextKey struct{}
259+
260+
// RequestFromContext returns an admission.Request from ctx.
261+
func RequestFromContext(ctx context.Context) (Request, error) {
262+
if v, ok := ctx.Value(requestContextKey{}).(Request); ok {
263+
return v, nil
264+
}
265+
266+
return Request{}, errors.New("admission.Request not found in context")
267+
}
268+
269+
// NewContextWithRequest returns a new Context, derived from ctx, which carries the
270+
// provided admission.Request.
271+
func NewContextWithRequest(ctx context.Context, req Request) context.Context {
272+
return context.WithValue(ctx, requestContextKey{}, req)
273+
}

pkg/webhook/admission/webhook_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,21 @@ var _ = Describe("Admission Webhooks", func() {
194194
})
195195
})
196196

197+
var _ = Describe("Should be able to write/read admission.Request to/from context", func() {
198+
ctx := context.Background()
199+
testRequest := Request{
200+
admissionv1.AdmissionRequest{
201+
UID: "test-uid",
202+
},
203+
}
204+
205+
ctx = NewContextWithRequest(ctx, testRequest)
206+
207+
gotRequest, err := RequestFromContext(ctx)
208+
Expect(err).To(Not(HaveOccurred()))
209+
Expect(gotRequest).To(Equal(testRequest))
210+
})
211+
197212
type stringInjector interface {
198213
InjectString(s string) error
199214
}

0 commit comments

Comments
 (0)