Initial commit
This commit is contained in:
48
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
generated
vendored
Normal file
48
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
|
||||
)
|
||||
|
||||
// DecodeFunc is a function that implements the Decoder interface.
|
||||
type DecodeFunc func(types.Request, runtime.Object) error
|
||||
|
||||
var _ types.Decoder = DecodeFunc(nil)
|
||||
|
||||
// Decode implements the Decoder interface.
|
||||
func (f DecodeFunc) Decode(req types.Request, obj runtime.Object) error {
|
||||
return f(req, obj)
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
codecs serializer.CodecFactory
|
||||
}
|
||||
|
||||
// NewDecoder creates a Decoder given the runtime.Scheme
|
||||
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 types.Request, into runtime.Object) error {
|
||||
deserializer := d.codecs.UniversalDeserializer()
|
||||
return runtime.DecodeInto(deserializer, req.AdmissionRequest.Object.Raw, into)
|
||||
}
|
||||
101
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
generated
vendored
Normal file
101
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright 2018 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 provides implementation for admission webhook and methods to implement admission webhook handlers.
|
||||
|
||||
The following snippet is an example implementation of mutating handler.
|
||||
|
||||
type Mutator struct {
|
||||
client client.Client
|
||||
decoder types.Decoder
|
||||
}
|
||||
|
||||
func (m *Mutator) mutatePodsFn(ctx context.Context, pod *corev1.Pod) error {
|
||||
// your logic to mutate the passed-in pod.
|
||||
}
|
||||
|
||||
func (m *Mutator) Handle(ctx context.Context, req types.Request) types.Response {
|
||||
pod := &corev1.Pod{}
|
||||
err := m.decoder.Decode(req, pod)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusBadRequest, err)
|
||||
}
|
||||
// Do deepcopy before actually mutate the object.
|
||||
copy := pod.DeepCopy()
|
||||
err = m.mutatePodsFn(ctx, copy)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
return admission.PatchResponse(pod, copy)
|
||||
}
|
||||
|
||||
// InjectClient is called by the Manager and provides a client.Client to the Mutator instance.
|
||||
func (m *Mutator) InjectClient(c client.Client) error {
|
||||
h.client = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// InjectDecoder is called by the Manager and provides a types.Decoder to the Mutator instance.
|
||||
func (m *Mutator) InjectDecoder(d types.Decoder) error {
|
||||
h.decoder = d
|
||||
return nil
|
||||
}
|
||||
|
||||
The following snippet is an example implementation of validating handler.
|
||||
|
||||
type Handler struct {
|
||||
client client.Client
|
||||
decoder types.Decoder
|
||||
}
|
||||
|
||||
func (v *Validator) validatePodsFn(ctx context.Context, pod *corev1.Pod) (bool, string, error) {
|
||||
// your business logic
|
||||
}
|
||||
|
||||
func (v *Validator) Handle(ctx context.Context, req types.Request) types.Response {
|
||||
pod := &corev1.Pod{}
|
||||
err := h.decoder.Decode(req, pod)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
allowed, reason, err := h.validatePodsFn(ctx, pod)
|
||||
if err != nil {
|
||||
return admission.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
return admission.ValidationResponse(allowed, reason)
|
||||
}
|
||||
|
||||
// InjectClient is called by the Manager and provides a client.Client to the Validator instance.
|
||||
func (v *Validator) InjectClient(c client.Client) error {
|
||||
h.client = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// InjectDecoder is called by the Manager and provides a types.Decoder to the Validator instance.
|
||||
func (v *Validator) InjectDecoder(d types.Decoder) error {
|
||||
h.decoder = d
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
package admission
|
||||
|
||||
import (
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
)
|
||||
|
||||
var log = logf.KBLog.WithName("admission")
|
||||
115
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
generated
vendored
Normal file
115
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
|
||||
)
|
||||
|
||||
var admissionv1beta1scheme = runtime.NewScheme()
|
||||
var admissionv1beta1schemecodecs = serializer.NewCodecFactory(admissionv1beta1scheme)
|
||||
|
||||
func init() {
|
||||
addToScheme(admissionv1beta1scheme)
|
||||
}
|
||||
|
||||
func addToScheme(scheme *runtime.Scheme) {
|
||||
utilruntime.Must(admissionv1beta1.AddToScheme(scheme))
|
||||
}
|
||||
|
||||
var _ http.Handler = &Webhook{}
|
||||
|
||||
func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
startTS := time.Now()
|
||||
defer metrics.RequestLatency.WithLabelValues(wh.Name).Observe(time.Now().Sub(startTS).Seconds())
|
||||
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
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")
|
||||
reviewResponse = ErrorResponse(http.StatusBadRequest, err)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = errors.New("request body is empty")
|
||||
log.Error(err, "bad request")
|
||||
reviewResponse = ErrorResponse(http.StatusBadRequest, err)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
return
|
||||
}
|
||||
|
||||
// verify the content type is accurate
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if contentType != "application/json" {
|
||||
err = fmt.Errorf("contentType=%s, expect application/json", contentType)
|
||||
log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
|
||||
reviewResponse = ErrorResponse(http.StatusBadRequest, err)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
return
|
||||
}
|
||||
|
||||
ar := v1beta1.AdmissionReview{}
|
||||
if _, _, err := admissionv1beta1schemecodecs.UniversalDeserializer().Decode(body, nil, &ar); err != nil {
|
||||
log.Error(err, "unable to decode the request")
|
||||
reviewResponse = ErrorResponse(http.StatusBadRequest, err)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add panic-recovery for Handle
|
||||
reviewResponse = wh.Handle(context.Background(), types.Request{AdmissionRequest: ar.Request})
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
}
|
||||
|
||||
func (wh *Webhook) writeResponse(w io.Writer, response types.Response) {
|
||||
if response.Response.Result.Code != 0 {
|
||||
if response.Response.Result.Code == http.StatusOK {
|
||||
metrics.TotalRequests.WithLabelValues(wh.Name, "true").Inc()
|
||||
} else {
|
||||
metrics.TotalRequests.WithLabelValues(wh.Name, "false").Inc()
|
||||
}
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(w)
|
||||
responseAdmissionReview := v1beta1.AdmissionReview{
|
||||
Response: response.Response,
|
||||
}
|
||||
err := encoder.Encode(responseAdmissionReview)
|
||||
if err != nil {
|
||||
log.Error(err, "unable to encode the response")
|
||||
wh.writeResponse(w, ErrorResponse(http.StatusInternalServerError, err))
|
||||
}
|
||||
}
|
||||
70
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
generated
vendored
Normal file
70
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"net/http"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/patch"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
|
||||
)
|
||||
|
||||
// ErrorResponse creates a new Response for error-handling a request.
|
||||
func ErrorResponse(code int32, err error) types.Response {
|
||||
return types.Response{
|
||||
Response: &admissionv1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Code: code,
|
||||
Message: err.Error(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ValidationResponse returns a response for admitting a request.
|
||||
func ValidationResponse(allowed bool, reason string) types.Response {
|
||||
resp := types.Response{
|
||||
Response: &admissionv1beta1.AdmissionResponse{
|
||||
Allowed: allowed,
|
||||
},
|
||||
}
|
||||
if len(reason) > 0 {
|
||||
resp.Response.Result = &metav1.Status{
|
||||
Reason: metav1.StatusReason(reason),
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
// PatchResponse returns a new response with json patch.
|
||||
func PatchResponse(original, current runtime.Object) types.Response {
|
||||
patches, err := patch.NewJSONPatch(original, current)
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
return types.Response{
|
||||
Patches: patches,
|
||||
Response: &admissionv1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
|
||||
},
|
||||
}
|
||||
}
|
||||
44
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/types/types.go
generated
vendored
Normal file
44
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/types/types.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2018 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 types
|
||||
|
||||
import (
|
||||
"github.com/mattbaird/jsonpatch"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Request is the input of Handler
|
||||
type Request struct {
|
||||
AdmissionRequest *admissionv1beta1.AdmissionRequest
|
||||
}
|
||||
|
||||
// Response is the output of admission.Handler
|
||||
type Response struct {
|
||||
// Patches are the JSON patches for mutating webhooks.
|
||||
// Using this instead of setting Response.Patch to minimize the overhead of serialization and deserialization.
|
||||
Patches []jsonpatch.JsonPatchOperation
|
||||
// Response is the admission response. Don't set the Patch field in it.
|
||||
Response *admissionv1beta1.AdmissionResponse
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
259
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
generated
vendored
Normal file
259
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mattbaird/jsonpatch"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
atypes "sigs.k8s.io/controller-runtime/pkg/webhook/admission/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/types"
|
||||
)
|
||||
|
||||
// Handler can handle an AdmissionRequest.
|
||||
type Handler interface {
|
||||
Handle(context.Context, atypes.Request) atypes.Response
|
||||
}
|
||||
|
||||
// HandlerFunc implements Handler interface using a single function.
|
||||
type HandlerFunc func(context.Context, atypes.Request) atypes.Response
|
||||
|
||||
var _ Handler = HandlerFunc(nil)
|
||||
|
||||
// Handle process the AdmissionRequest by invoking the underlying function.
|
||||
func (f HandlerFunc) Handle(ctx context.Context, req atypes.Request) atypes.Response {
|
||||
return f(ctx, req)
|
||||
}
|
||||
|
||||
// Webhook represents each individual webhook.
|
||||
type Webhook struct {
|
||||
// Name is the name of the webhook
|
||||
Name string
|
||||
// Type is the webhook type, i.e. mutating, validating
|
||||
Type types.WebhookType
|
||||
// Path is the path this webhook will serve.
|
||||
Path string
|
||||
// Rules maps to the Rules field in admissionregistrationv1beta1.Webhook
|
||||
Rules []admissionregistrationv1beta1.RuleWithOperations
|
||||
// FailurePolicy maps to the FailurePolicy field in admissionregistrationv1beta1.Webhook
|
||||
// This optional. If not set, will be defaulted to Ignore (fail-open) by the server.
|
||||
// More details: https://github.com/kubernetes/api/blob/f5c295feaba2cbc946f0bbb8b535fc5f6a0345ee/admissionregistration/v1beta1/types.go#L144-L147
|
||||
FailurePolicy *admissionregistrationv1beta1.FailurePolicyType
|
||||
// NamespaceSelector maps to the NamespaceSelector field in admissionregistrationv1beta1.Webhook
|
||||
// This optional.
|
||||
NamespaceSelector *metav1.LabelSelector
|
||||
// Handlers contains a list of handlers. Each handler may only contains the business logic for its own feature.
|
||||
// For example, feature foo and bar can be in the same webhook if all the other configurations are the same.
|
||||
// The handler will be invoked sequentially as the order in the list.
|
||||
// Note: if you are using mutating webhook with multiple handlers, it's your responsibility to
|
||||
// ensure the handlers are not generating conflicting JSON patches.
|
||||
Handlers []Handler
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (w *Webhook) setDefaults() {
|
||||
if len(w.Path) == 0 {
|
||||
if len(w.Rules) == 0 || len(w.Rules[0].Resources) == 0 {
|
||||
// can't do defaulting, skip it.
|
||||
return
|
||||
}
|
||||
if w.Type == types.WebhookTypeMutating {
|
||||
w.Path = "/mutate-" + w.Rules[0].Resources[0]
|
||||
} else if w.Type == types.WebhookTypeValidating {
|
||||
w.Path = "/validate-" + w.Rules[0].Resources[0]
|
||||
}
|
||||
}
|
||||
if len(w.Name) == 0 {
|
||||
reg := regexp.MustCompile("[^a-zA-Z0-9]+")
|
||||
processedPath := strings.ToLower(reg.ReplaceAllString(w.Path, ""))
|
||||
w.Name = processedPath + ".example.com"
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds additional handler(s) in the webhook
|
||||
func (w *Webhook) Add(handlers ...Handler) {
|
||||
w.Handlers = append(w.Handlers, handlers...)
|
||||
}
|
||||
|
||||
// Webhook implements Handler interface.
|
||||
var _ Handler = &Webhook{}
|
||||
|
||||
// Handle processes AdmissionRequest.
|
||||
// If the webhook is mutating type, it delegates the AdmissionRequest to each handler and merge the patches.
|
||||
// If the webhook is validating type, it delegates the AdmissionRequest to each handler and
|
||||
// deny the request if anyone denies.
|
||||
func (w *Webhook) Handle(ctx context.Context, req atypes.Request) atypes.Response {
|
||||
if req.AdmissionRequest == nil {
|
||||
return ErrorResponse(http.StatusBadRequest, errors.New("got an empty AdmissionRequest"))
|
||||
}
|
||||
var resp atypes.Response
|
||||
switch w.Type {
|
||||
case types.WebhookTypeMutating:
|
||||
resp = w.handleMutating(ctx, req)
|
||||
case types.WebhookTypeValidating:
|
||||
resp = w.handleValidating(ctx, req)
|
||||
default:
|
||||
return ErrorResponse(http.StatusInternalServerError, errors.New("you must specify your webhook type"))
|
||||
}
|
||||
resp.Response.UID = req.AdmissionRequest.UID
|
||||
return resp
|
||||
}
|
||||
|
||||
func (w *Webhook) handleMutating(ctx context.Context, req atypes.Request) atypes.Response {
|
||||
patches := []jsonpatch.JsonPatchOperation{}
|
||||
for _, handler := range w.Handlers {
|
||||
resp := handler.Handle(ctx, req)
|
||||
if !resp.Response.Allowed {
|
||||
setStatusOKInAdmissionResponse(resp.Response)
|
||||
return resp
|
||||
}
|
||||
if resp.Response.PatchType != nil && *resp.Response.PatchType != admissionv1beta1.PatchTypeJSONPatch {
|
||||
return ErrorResponse(http.StatusInternalServerError,
|
||||
fmt.Errorf("unexpected patch type returned by the handler: %v, only allow: %v",
|
||||
resp.Response.PatchType, admissionv1beta1.PatchTypeJSONPatch))
|
||||
}
|
||||
patches = append(patches, resp.Patches...)
|
||||
}
|
||||
var err error
|
||||
marshaledPatch, err := json.Marshal(patches)
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusBadRequest, fmt.Errorf("error when marshaling the patch: %v", err))
|
||||
}
|
||||
return atypes.Response{
|
||||
Response: &admissionv1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Code: http.StatusOK,
|
||||
},
|
||||
Patch: marshaledPatch,
|
||||
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Webhook) handleValidating(ctx context.Context, req atypes.Request) atypes.Response {
|
||||
for _, handler := range w.Handlers {
|
||||
resp := handler.Handle(ctx, req)
|
||||
if !resp.Response.Allowed {
|
||||
setStatusOKInAdmissionResponse(resp.Response)
|
||||
return resp
|
||||
}
|
||||
}
|
||||
return atypes.Response{
|
||||
Response: &admissionv1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Code: http.StatusOK,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setStatusOKInAdmissionResponse(resp *admissionv1beta1.AdmissionResponse) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
if resp.Result == nil {
|
||||
resp.Result = &metav1.Status{}
|
||||
}
|
||||
if resp.Result.Code == 0 {
|
||||
resp.Result.Code = http.StatusOK
|
||||
}
|
||||
}
|
||||
|
||||
// GetName returns the name of the webhook.
|
||||
func (w *Webhook) GetName() string {
|
||||
w.once.Do(w.setDefaults)
|
||||
return w.Name
|
||||
}
|
||||
|
||||
// GetPath returns the path that the webhook registered.
|
||||
func (w *Webhook) GetPath() string {
|
||||
w.once.Do(w.setDefaults)
|
||||
return w.Path
|
||||
}
|
||||
|
||||
// GetType returns the type of the webhook.
|
||||
func (w *Webhook) GetType() types.WebhookType {
|
||||
w.once.Do(w.setDefaults)
|
||||
return w.Type
|
||||
}
|
||||
|
||||
// Handler returns a http.Handler for the webhook
|
||||
func (w *Webhook) Handler() http.Handler {
|
||||
w.once.Do(w.setDefaults)
|
||||
return w
|
||||
}
|
||||
|
||||
// Validate validates if the webhook is valid.
|
||||
func (w *Webhook) Validate() error {
|
||||
w.once.Do(w.setDefaults)
|
||||
if len(w.Rules) == 0 {
|
||||
return errors.New("field Rules should not be empty")
|
||||
}
|
||||
if len(w.Name) == 0 {
|
||||
return errors.New("field Name should not be empty")
|
||||
}
|
||||
if w.Type != types.WebhookTypeMutating && w.Type != types.WebhookTypeValidating {
|
||||
return fmt.Errorf("unsupported Type: %v, only WebhookTypeMutating and WebhookTypeValidating are supported", w.Type)
|
||||
}
|
||||
if len(w.Path) == 0 {
|
||||
return errors.New("field Path should not be empty")
|
||||
}
|
||||
if len(w.Handlers) == 0 {
|
||||
return errors.New("field Handler should not be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ inject.Client = &Webhook{}
|
||||
|
||||
// InjectClient injects the client into the handlers
|
||||
func (w *Webhook) InjectClient(c client.Client) error {
|
||||
for _, handler := range w.Handlers {
|
||||
if _, err := inject.ClientInto(c, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ inject.Decoder = &Webhook{}
|
||||
|
||||
// InjectDecoder injects the decoder into the handlers
|
||||
func (w *Webhook) InjectDecoder(d atypes.Decoder) error {
|
||||
for _, handler := range w.Handlers {
|
||||
if _, err := inject.DecoderInto(d, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user