Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit d88b962

Browse files
committed
Introduce internal request struct in objects validator
Refactoring; While trying to fix callers to the deprecated webhooks.Deny, I noticed that the objects validator does not have an internal request struct - as all other validators have. This PR fixes this, and is more or less required to remove the last callers the deprecated func. Tested: Ran both unit-tests ('make test') and integration test ('make test-e2e') successfully.
1 parent 281b24f commit d88b962

File tree

2 files changed

+68
-32
lines changed

2 files changed

+68
-32
lines changed

internal/objects/validator.go

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ type Validator struct {
4949
decoder *admission.Decoder
5050
}
5151

52+
type request struct {
53+
obj *unstructured.Unstructured
54+
oldObj *unstructured.Unstructured
55+
op k8sadm.Operation
56+
}
57+
5258
func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response {
5359
log := v.Log.WithValues("resource", req.Resource, "ns", req.Namespace, "nm", req.Name, "op", req.Operation, "user", req.UserInfo.Username)
5460

@@ -74,29 +80,13 @@ func (v *Validator) Handle(ctx context.Context, req admission.Request) admission
7480
return webhooks.Allow("HNC SA")
7581
}
7682

77-
// Decode the old and new object, if we expect them to exist ("old" won't exist for creations,
78-
// while "new" won't exist for deletions).
79-
inst := &unstructured.Unstructured{}
80-
oldInst := &unstructured.Unstructured{}
81-
if req.Operation != k8sadm.Delete {
82-
if err := v.decoder.Decode(req, inst); err != nil {
83-
log.Error(err, "Couldn't decode req.Object", "raw", req.Object)
84-
return webhooks.Deny(metav1.StatusReasonBadRequest, err.Error())
85-
}
86-
}
87-
if req.Operation != k8sadm.Create {
88-
// See issue #688 and #889
89-
if req.Operation == k8sadm.Delete && req.OldObject.Raw == nil {
90-
return webhooks.Allow("cannot validate deletions in K8s 1.14")
91-
}
92-
if err := v.decoder.DecodeRaw(req.OldObject, oldInst); err != nil {
93-
log.Error(err, "Couldn't decode req.OldObject", "raw", req.OldObject)
94-
return webhooks.Deny(metav1.StatusReasonBadRequest, err.Error())
95-
}
83+
decoded, err := v.decodeRequest(log, req)
84+
if err != nil {
85+
return webhooks.Deny(metav1.StatusReasonBadRequest, err.Error())
9686
}
9787

9888
// Run the actual logic.
99-
resp := v.handle(ctx, req.Operation, inst, oldInst)
89+
resp := v.handle(ctx, decoded)
10090
if !resp.Allowed {
10191
log.Info("Denied", "code", resp.Result.Code, "reason", resp.Result.Reason, "message", resp.Result.Message)
10292
} else {
@@ -115,7 +105,10 @@ func (v *Validator) isPropagateType(gvk metav1.GroupVersionKind) bool {
115105

116106
// handle implements the non-webhook-y businesss logic of this validator, allowing it to be more
117107
// easily unit tested (ie without constructing an admission.Request, setting up user infos, etc).
118-
func (v *Validator) handle(ctx context.Context, op k8sadm.Operation, inst, oldInst *unstructured.Unstructured) admission.Response {
108+
func (v *Validator) handle(ctx context.Context, req *request) admission.Response {
109+
inst := req.obj
110+
oldInst := req.oldObj
111+
119112
// Find out if the object was/is inherited, and where it's inherited from.
120113
oldSource, oldInherited := metadata.GetLabel(oldInst, api.LabelInheritedFrom)
121114
newSource, newInherited := metadata.GetLabel(inst, api.LabelInheritedFrom)
@@ -152,7 +145,7 @@ func (v *Validator) handle(ctx context.Context, op k8sadm.Operation, inst, oldIn
152145
return webhooks.Allow("source object")
153146
}
154147
// This is a propagated object.
155-
return v.handleInherited(ctx, op, newSource, oldSource, inst, oldInst)
148+
return v.handleInherited(ctx, req, newSource, oldSource)
156149
}
157150

158151
func validateSelectorAnnot(inst *unstructured.Unstructured) string {
@@ -242,7 +235,11 @@ func validateNoneSelectorChange(inst, oldInst *unstructured.Unstructured) error
242235
return err
243236
}
244237

245-
func (v *Validator) handleInherited(ctx context.Context, op k8sadm.Operation, newSource, oldSource string, inst, oldInst *unstructured.Unstructured) admission.Response {
238+
func (v *Validator) handleInherited(ctx context.Context, req *request, newSource, oldSource string) admission.Response {
239+
op := req.op
240+
inst := req.obj
241+
oldInst := req.oldObj
242+
246243
// Propagated objects cannot be created or deleted (except by the HNC SA, but the HNC SA
247244
// never gets this far in the validation). They *can* have their statuses updated, so
248245
// if this is an update, make sure that the canonical form of the object hasn't changed.
@@ -347,6 +344,31 @@ func (v *Validator) hasConflict(inst *unstructured.Unstructured) (bool, []string
347344
return len(conflicts) != 0, conflicts
348345
}
349346

347+
func (v *Validator) decodeRequest(log logr.Logger, req admission.Request) (*request, error) {
348+
// Decode the old and new object, if we expect them to exist ("old" won't exist for creations,
349+
// while "new" won't exist for deletions).
350+
inst := &unstructured.Unstructured{}
351+
oldInst := &unstructured.Unstructured{}
352+
if req.Operation != k8sadm.Delete {
353+
if err := v.decoder.Decode(req, inst); err != nil {
354+
log.Error(err, "Couldn't decode req.Object", "raw", req.Object)
355+
return nil, fmt.Errorf("while decoding object: %w", err)
356+
}
357+
}
358+
if req.Operation != k8sadm.Create {
359+
if err := v.decoder.DecodeRaw(req.OldObject, oldInst); err != nil {
360+
log.Error(err, "Couldn't decode req.OldObject", "raw", req.OldObject)
361+
return nil, fmt.Errorf("while decoding old object: %w", err)
362+
}
363+
}
364+
365+
return &request{
366+
obj: inst,
367+
oldObj: oldInst,
368+
op: req.Operation,
369+
}, nil
370+
}
371+
350372
func (v *Validator) InjectClient(c client.Client) error {
351373
v.client = c
352374
return nil

internal/objects/validator_test.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package objects
22

33
import (
44
"context"
5-
"sigs.k8s.io/hierarchical-namespaces/internal/config"
65
"testing"
76
"time"
87

@@ -20,6 +19,7 @@ import (
2019
"sigs.k8s.io/hierarchical-namespaces/internal/foresttest"
2120

2221
api "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2"
22+
"sigs.k8s.io/hierarchical-namespaces/internal/config"
2323
"sigs.k8s.io/hierarchical-namespaces/internal/forest"
2424
"sigs.k8s.io/hierarchical-namespaces/internal/metadata"
2525
)
@@ -135,7 +135,12 @@ func TestInheritedFromLabel(t *testing.T) {
135135
metadata.SetLabel(inst, tc.newLabel, tc.newValue)
136136

137137
// Test
138-
got := v.handle(context.Background(), k8sadm.Update, inst, oldInst)
138+
req := &request{
139+
obj: inst,
140+
oldObj: oldInst,
141+
op: k8sadm.Update,
142+
}
143+
got := v.handle(context.Background(), req)
139144

140145
// Report
141146
code := got.AdmissionResponse.Result.Code
@@ -616,19 +621,23 @@ func TestUserChanges(t *testing.T) {
616621
t.Run(tc.name, func(t *testing.T) {
617622
// Setup
618623
g := NewWithT(t)
619-
op := k8sadm.Update
624+
req := &request{
625+
obj: tc.inst,
626+
oldObj: tc.oldInst,
627+
op: k8sadm.Update,
628+
}
620629
if tc.inst == nil {
621-
op = k8sadm.Delete
622-
tc.inst = &unstructured.Unstructured{}
630+
req.op = k8sadm.Delete
631+
req.obj = &unstructured.Unstructured{}
623632
} else if tc.oldInst == nil {
624-
op = k8sadm.Create
625-
tc.oldInst = &unstructured.Unstructured{}
633+
req.op = k8sadm.Create
634+
req.oldObj = &unstructured.Unstructured{}
626635
}
627636

628637
c := fakeNSClient{isDeleting: tc.isDeleting}
629638
v.client = c
630639
// Test
631-
got := v.handle(context.Background(), op, tc.inst, tc.oldInst)
640+
got := v.handle(context.Background(), req)
632641
// Report
633642
code := got.AdmissionResponse.Result.Code
634643
reason := got.AdmissionResponse.Result.Reason
@@ -754,7 +763,12 @@ func TestCreatingConflictSource(t *testing.T) {
754763
inst.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Secret"})
755764
inst.SetAnnotations(tc.newInstAnnotation)
756765
// Test
757-
got := v.handle(context.Background(), op, inst, &unstructured.Unstructured{})
766+
req := &request{
767+
obj: inst,
768+
oldObj: &unstructured.Unstructured{},
769+
op: op,
770+
}
771+
got := v.handle(context.Background(), req)
758772
// Report
759773
code := got.AdmissionResponse.Result.Code
760774
reason := got.AdmissionResponse.Result.Reason

0 commit comments

Comments
 (0)