-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new features about support warning with webhook
Signed-off-by: STRRL <im@strrl.dev>
- Loading branch information
Showing
5 changed files
with
634 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
/* | ||
Copyright 2022 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package admission | ||
|
||
import ( | ||
"context" | ||
goerrors "errors" | ||
"net/http" | ||
|
||
v1 "k8s.io/api/admission/v1" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
) | ||
|
||
// ValidatorWarn works like Validator, but it allows to return warnings. | ||
type ValidatorWarn interface { | ||
runtime.Object | ||
ValidateCreate() (err error, warnings []string) | ||
ValidateUpdate(old runtime.Object) (err error, warnings []string) | ||
ValidateDelete() (err error, warnings []string) | ||
} | ||
|
||
func ValidatingWebhookWithWarningFor(validatorWarning ValidatorWarn) *Webhook { | ||
return nil | ||
} | ||
|
||
var _ Handler = &validatingWarnHandler{} | ||
var _ DecoderInjector = &validatingWarnHandler{} | ||
|
||
type validatingWarnHandler struct { | ||
validatorWarn ValidatorWarn | ||
decoder *Decoder | ||
} | ||
|
||
// InjectDecoder injects the decoder into a validatingWarnHandler. | ||
func (h *validatingWarnHandler) InjectDecoder(decoder *Decoder) error { | ||
h.decoder = decoder | ||
return nil | ||
} | ||
|
||
// Handle handles admission requests. | ||
func (h *validatingWarnHandler) Handle(ctx context.Context, req Request) Response { | ||
if h.validatorWarn == nil { | ||
panic("validatorWarn should never be nil") | ||
} | ||
|
||
var allWarnings []string | ||
|
||
// Get the object in the request | ||
obj := h.validatorWarn.DeepCopyObject().(ValidatorWarn) | ||
if req.Operation == v1.Create { | ||
err := h.decoder.Decode(req, obj) | ||
if err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
|
||
err, warnings := obj.ValidateCreate() | ||
allWarnings = append(allWarnings, warnings...) | ||
if err != nil { | ||
var apiStatus apierrors.APIStatus | ||
if goerrors.As(err, &apiStatus) { | ||
return validationResponseFromStatus(false, apiStatus.Status()) | ||
} | ||
return Denied(err.Error()).WithWarnings(allWarnings...) | ||
} | ||
} | ||
|
||
if req.Operation == v1.Update { | ||
oldObj := obj.DeepCopyObject() | ||
|
||
err := h.decoder.DecodeRaw(req.Object, obj) | ||
if err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
err = h.decoder.DecodeRaw(req.OldObject, oldObj) | ||
if err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
err, warnings := obj.ValidateUpdate(oldObj) | ||
allWarnings = append(allWarnings, warnings...) | ||
if err != nil { | ||
var apiStatus apierrors.APIStatus | ||
if goerrors.As(err, &apiStatus) { | ||
return validationResponseFromStatus(false, apiStatus.Status()) | ||
} | ||
return Denied(err.Error()).WithWarnings(allWarnings...) | ||
} | ||
} | ||
|
||
if req.Operation == v1.Delete { | ||
// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346 | ||
// OldObject contains the object being deleted | ||
err := h.decoder.DecodeRaw(req.OldObject, obj) | ||
if err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
|
||
err, warnings := obj.ValidateDelete() | ||
allWarnings = append(allWarnings, warnings...) | ||
if err != nil { | ||
var apiStatus apierrors.APIStatus | ||
if goerrors.As(err, &apiStatus) { | ||
return validationResponseFromStatus(false, apiStatus.Status()) | ||
} | ||
return Denied(err.Error()).WithWarnings(allWarnings...) | ||
} | ||
} | ||
return Allowed("").WithWarnings(allWarnings...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
Copyright 2022 The Kubernetes Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package admission | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
|
||
v1 "k8s.io/api/admission/v1" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
) | ||
|
||
// CustomValidatorWarn works like CustomValidator, but it allows to return warnings. | ||
type CustomValidatorWarn interface { | ||
ValidateCreate(ctx context.Context, obj runtime.Object) (err error, warnings []string) | ||
ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (err error, warnings []string) | ||
ValidateDelete(ctx context.Context, obj runtime.Object) (err error, warnings []string) | ||
} | ||
|
||
// WithCustomValidatorWarn creates a new Webhook for validating the provided type. | ||
func WithCustomValidatorWarn(obj runtime.Object, validatorWarn CustomValidatorWarn) *Webhook { | ||
return &Webhook{ | ||
Handler: &validatorWarnForType{object: obj, validatorWarn: validatorWarn}, | ||
} | ||
} | ||
|
||
var _ Handler = (*validatorWarnForType)(nil) | ||
var _ DecoderInjector = (*validatorWarnForType)(nil) | ||
|
||
type validatorWarnForType struct { | ||
validatorWarn CustomValidatorWarn | ||
object runtime.Object | ||
decoder *Decoder | ||
} | ||
|
||
func (h *validatorWarnForType) InjectDecoder(d *Decoder) error { | ||
h.decoder = d | ||
return nil | ||
} | ||
|
||
func (h *validatorWarnForType) Handle(ctx context.Context, req Request) Response { | ||
if h.validatorWarn == nil { | ||
panic("validatorWarn should never be nil") | ||
} | ||
if h.object == nil { | ||
panic("object should never be nil") | ||
} | ||
|
||
ctx = NewContextWithRequest(ctx, req) | ||
|
||
obj := h.object.DeepCopyObject() | ||
|
||
var err error | ||
var warnings []string | ||
switch req.Operation { | ||
case v1.Create: | ||
if err := h.decoder.Decode(req, obj); err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
|
||
err, warnings = h.validatorWarn.ValidateCreate(ctx, obj) | ||
case v1.Update: | ||
oldObj := obj.DeepCopyObject() | ||
if err := h.decoder.DecodeRaw(req.Object, obj); err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
if err := h.decoder.DecodeRaw(req.OldObject, oldObj); err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
|
||
err, warnings = h.validatorWarn.ValidateUpdate(ctx, oldObj, obj) | ||
case v1.Delete: | ||
// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346 | ||
// OldObject contains the object being deleted | ||
if err := h.decoder.DecodeRaw(req.OldObject, obj); err != nil { | ||
return Errored(http.StatusBadRequest, err) | ||
} | ||
|
||
err, warnings = h.validatorWarn.ValidateDelete(ctx, obj) | ||
default: | ||
return Errored(http.StatusBadRequest, fmt.Errorf("unknown operation request %q", req.Operation)) | ||
} | ||
|
||
// Check the error message first. | ||
if err != nil { | ||
var apiStatus apierrors.APIStatus | ||
if errors.As(err, &apiStatus) { | ||
return validationResponseFromStatus(false, apiStatus.Status()) | ||
} | ||
return Denied(err.Error()).WithWarnings(warnings...) | ||
} | ||
|
||
// Return allowed if everything succeeded. | ||
return Allowed("").WithWarnings(warnings...) | ||
} |
Oops, something went wrong.