Skip to content

Commit 86c19e8

Browse files
committed
feat: new features about support warning with webhook
Signed-off-by: STRRL <[email protected]>
1 parent 7399a3a commit 86c19e8

File tree

5 files changed

+634
-0
lines changed

5 files changed

+634
-0
lines changed

pkg/webhook/admission/admissiontest/util.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ package admissiontest
1919
import (
2020
"k8s.io/apimachinery/pkg/runtime"
2121
"k8s.io/apimachinery/pkg/runtime/schema"
22+
"sigs.k8s.io/controller-runtime/pkg/webhook"
2223
)
2324

25+
var _ runtime.Object = (*FakeValidator)(nil)
26+
var _ schema.ObjectKind = (*FakeValidator)(nil)
27+
var _ webhook.Validator = (*FakeValidator)(nil)
28+
2429
// FakeValidator provides fake validating webhook functionality for testing
2530
// It implements the admission.Validator interface and
2631
// rejects all requests with the same configured error
@@ -64,3 +69,52 @@ func (v *FakeValidator) GroupVersionKind() schema.GroupVersionKind {
6469
func (v *FakeValidator) SetGroupVersionKind(gvk schema.GroupVersionKind) {
6570
v.GVKToReturn = gvk
6671
}
72+
73+
var _ runtime.Object = (*FakeValidatorWarn)(nil)
74+
var _ schema.ObjectKind = (*FakeValidatorWarn)(nil)
75+
var _ webhook.ValidatorWarn = (*FakeValidatorWarn)(nil)
76+
77+
// FakeValidatorWarn provides fake validating webhook functionality for testing
78+
// It implements the admission.ValidatorWarn interface and
79+
// rejects all requests with the same configured error
80+
// or passes if ErrorToReturn is nil.
81+
// And it would always return configured warning messages WarningsToReturn.
82+
type FakeValidatorWarn struct {
83+
// ErrorToReturn is the error for which the FakeValidatorWarn rejects all requests
84+
ErrorToReturn error `json:"ErrorToReturn,omitempty"`
85+
// GVKToReturn is the GroupVersionKind that the webhook operates on
86+
GVKToReturn schema.GroupVersionKind
87+
// WarningsToReturn is the warnings for FakeValidatorWarn returns to all requests
88+
WarningsToReturn []string
89+
}
90+
91+
func (v *FakeValidatorWarn) ValidateCreate() (err error, warnings []string) {
92+
return v.ErrorToReturn, v.WarningsToReturn
93+
}
94+
95+
func (v *FakeValidatorWarn) ValidateUpdate(old runtime.Object) (err error, warnings []string) {
96+
return v.ErrorToReturn, v.WarningsToReturn
97+
}
98+
99+
func (v *FakeValidatorWarn) ValidateDelete() (err error, warnings []string) {
100+
return v.ErrorToReturn, v.WarningsToReturn
101+
}
102+
103+
func (v *FakeValidatorWarn) SetGroupVersionKind(kind schema.GroupVersionKind) {
104+
v.GVKToReturn = kind
105+
}
106+
107+
func (v *FakeValidatorWarn) GroupVersionKind() schema.GroupVersionKind {
108+
return v.GVKToReturn
109+
}
110+
111+
func (v *FakeValidatorWarn) GetObjectKind() schema.ObjectKind {
112+
return v
113+
}
114+
115+
func (v *FakeValidatorWarn) DeepCopyObject() runtime.Object {
116+
return &FakeValidatorWarn{ErrorToReturn: v.ErrorToReturn,
117+
GVKToReturn: v.GVKToReturn,
118+
WarningsToReturn: v.WarningsToReturn,
119+
}
120+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package admission
18+
19+
import (
20+
"context"
21+
goerrors "errors"
22+
"net/http"
23+
24+
v1 "k8s.io/api/admission/v1"
25+
apierrors "k8s.io/apimachinery/pkg/api/errors"
26+
"k8s.io/apimachinery/pkg/runtime"
27+
)
28+
29+
// ValidatorWarn works like Validator, but it allows to return warnings.
30+
type ValidatorWarn interface {
31+
runtime.Object
32+
ValidateCreate() (err error, warnings []string)
33+
ValidateUpdate(old runtime.Object) (err error, warnings []string)
34+
ValidateDelete() (err error, warnings []string)
35+
}
36+
37+
func ValidatingWebhookWithWarningFor(validatorWarning ValidatorWarn) *Webhook {
38+
return nil
39+
}
40+
41+
var _ Handler = &validatingWarnHandler{}
42+
var _ DecoderInjector = &validatingWarnHandler{}
43+
44+
type validatingWarnHandler struct {
45+
validatorWarn ValidatorWarn
46+
decoder *Decoder
47+
}
48+
49+
// InjectDecoder injects the decoder into a validatingWarnHandler.
50+
func (h *validatingWarnHandler) InjectDecoder(decoder *Decoder) error {
51+
h.decoder = decoder
52+
return nil
53+
}
54+
55+
// Handle handles admission requests.
56+
func (h *validatingWarnHandler) Handle(ctx context.Context, req Request) Response {
57+
if h.validatorWarn == nil {
58+
panic("validatorWarn should never be nil")
59+
}
60+
61+
var allWarnings []string
62+
63+
// Get the object in the request
64+
obj := h.validatorWarn.DeepCopyObject().(ValidatorWarn)
65+
if req.Operation == v1.Create {
66+
err := h.decoder.Decode(req, obj)
67+
if err != nil {
68+
return Errored(http.StatusBadRequest, err)
69+
}
70+
71+
err, warnings := obj.ValidateCreate()
72+
allWarnings = append(allWarnings, warnings...)
73+
if err != nil {
74+
var apiStatus apierrors.APIStatus
75+
if goerrors.As(err, &apiStatus) {
76+
return validationResponseFromStatus(false, apiStatus.Status())
77+
}
78+
return Denied(err.Error()).WithWarnings(allWarnings...)
79+
}
80+
}
81+
82+
if req.Operation == v1.Update {
83+
oldObj := obj.DeepCopyObject()
84+
85+
err := h.decoder.DecodeRaw(req.Object, obj)
86+
if err != nil {
87+
return Errored(http.StatusBadRequest, err)
88+
}
89+
err = h.decoder.DecodeRaw(req.OldObject, oldObj)
90+
if err != nil {
91+
return Errored(http.StatusBadRequest, err)
92+
}
93+
err, warnings := obj.ValidateUpdate(oldObj)
94+
allWarnings = append(allWarnings, warnings...)
95+
if err != nil {
96+
var apiStatus apierrors.APIStatus
97+
if goerrors.As(err, &apiStatus) {
98+
return validationResponseFromStatus(false, apiStatus.Status())
99+
}
100+
return Denied(err.Error()).WithWarnings(allWarnings...)
101+
}
102+
}
103+
104+
if req.Operation == v1.Delete {
105+
// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
106+
// OldObject contains the object being deleted
107+
err := h.decoder.DecodeRaw(req.OldObject, obj)
108+
if err != nil {
109+
return Errored(http.StatusBadRequest, err)
110+
}
111+
112+
err, warnings := obj.ValidateDelete()
113+
allWarnings = append(allWarnings, warnings...)
114+
if err != nil {
115+
var apiStatus apierrors.APIStatus
116+
if goerrors.As(err, &apiStatus) {
117+
return validationResponseFromStatus(false, apiStatus.Status())
118+
}
119+
return Denied(err.Error()).WithWarnings(allWarnings...)
120+
}
121+
}
122+
return Allowed("").WithWarnings(allWarnings...)
123+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Copyright 2022 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package admission
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
"net/http"
24+
25+
v1 "k8s.io/api/admission/v1"
26+
apierrors "k8s.io/apimachinery/pkg/api/errors"
27+
"k8s.io/apimachinery/pkg/runtime"
28+
)
29+
30+
// CustomValidatorWarn works like CustomValidator, but it allows to return warnings.
31+
type CustomValidatorWarn interface {
32+
ValidateCreate(ctx context.Context, obj runtime.Object) (err error, warnings []string)
33+
ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (err error, warnings []string)
34+
ValidateDelete(ctx context.Context, obj runtime.Object) (err error, warnings []string)
35+
}
36+
37+
// WithCustomValidatorWarn creates a new Webhook for validating the provided type.
38+
func WithCustomValidatorWarn(obj runtime.Object, validatorWarn CustomValidatorWarn) *Webhook {
39+
return &Webhook{
40+
Handler: &validatorWarnForType{object: obj, validatorWarn: validatorWarn},
41+
}
42+
}
43+
44+
var _ Handler = (*validatorWarnForType)(nil)
45+
var _ DecoderInjector = (*validatorWarnForType)(nil)
46+
47+
type validatorWarnForType struct {
48+
validatorWarn CustomValidatorWarn
49+
object runtime.Object
50+
decoder *Decoder
51+
}
52+
53+
func (h *validatorWarnForType) InjectDecoder(d *Decoder) error {
54+
h.decoder = d
55+
return nil
56+
}
57+
58+
func (h *validatorWarnForType) Handle(ctx context.Context, req Request) Response {
59+
if h.validatorWarn == nil {
60+
panic("validatorWarn should never be nil")
61+
}
62+
if h.object == nil {
63+
panic("object should never be nil")
64+
}
65+
66+
ctx = NewContextWithRequest(ctx, req)
67+
68+
obj := h.object.DeepCopyObject()
69+
70+
var err error
71+
var warnings []string
72+
switch req.Operation {
73+
case v1.Create:
74+
if err := h.decoder.Decode(req, obj); err != nil {
75+
return Errored(http.StatusBadRequest, err)
76+
}
77+
78+
err, warnings = h.validatorWarn.ValidateCreate(ctx, obj)
79+
case v1.Update:
80+
oldObj := obj.DeepCopyObject()
81+
if err := h.decoder.DecodeRaw(req.Object, obj); err != nil {
82+
return Errored(http.StatusBadRequest, err)
83+
}
84+
if err := h.decoder.DecodeRaw(req.OldObject, oldObj); err != nil {
85+
return Errored(http.StatusBadRequest, err)
86+
}
87+
88+
err, warnings = h.validatorWarn.ValidateUpdate(ctx, oldObj, obj)
89+
case v1.Delete:
90+
// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
91+
// OldObject contains the object being deleted
92+
if err := h.decoder.DecodeRaw(req.OldObject, obj); err != nil {
93+
return Errored(http.StatusBadRequest, err)
94+
}
95+
96+
err, warnings = h.validatorWarn.ValidateDelete(ctx, obj)
97+
default:
98+
return Errored(http.StatusBadRequest, fmt.Errorf("unknown operation request %q", req.Operation))
99+
}
100+
101+
// Check the error message first.
102+
if err != nil {
103+
var apiStatus apierrors.APIStatus
104+
if errors.As(err, &apiStatus) {
105+
return validationResponseFromStatus(false, apiStatus.Status())
106+
}
107+
return Denied(err.Error()).WithWarnings(warnings...)
108+
}
109+
110+
// Return allowed if everything succeeded.
111+
return Allowed("").WithWarnings(warnings...)
112+
}

0 commit comments

Comments
 (0)