mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
rebase: add controller runtime dependency
this commits add the controller runtime and its dependency to the vendor. Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
committed by
mergify[bot]
parent
14700b89d1
commit
5af3fe5deb
76
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
generated
vendored
Normal file
76
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
)
|
||||
|
||||
// Decoder knows how to decode the contents of an admission
|
||||
// request into a concrete object.
|
||||
type Decoder struct {
|
||||
codecs serializer.CodecFactory
|
||||
}
|
||||
|
||||
// NewDecoder creates a Decoder given the runtime.Scheme
|
||||
func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) {
|
||||
return &Decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
|
||||
}
|
||||
|
||||
// Decode decodes the inlined object in the AdmissionRequest into the passed-in runtime.Object.
|
||||
// If you want decode the OldObject in the AdmissionRequest, use DecodeRaw.
|
||||
// It errors out if req.Object.Raw is empty i.e. containing 0 raw bytes.
|
||||
func (d *Decoder) Decode(req Request, into runtime.Object) error {
|
||||
// we error out if rawObj is an empty object.
|
||||
if len(req.Object.Raw) == 0 {
|
||||
return fmt.Errorf("there is no content to decode")
|
||||
}
|
||||
return d.DecodeRaw(req.Object, into)
|
||||
}
|
||||
|
||||
// DecodeRaw decodes a RawExtension object into the passed-in runtime.Object.
|
||||
// It errors out if rawObj is empty i.e. containing 0 raw bytes.
|
||||
func (d *Decoder) DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) error {
|
||||
// NB(directxman12): there's a bug/weird interaction between decoders and
|
||||
// the API server where the API server doesn't send a GVK on the embedded
|
||||
// objects, which means the unstructured decoder refuses to decode. It
|
||||
// also means we can't pass the unstructured directly in, since it'll try
|
||||
// and call unstructured's special Unmarshal implementation, which calls
|
||||
// back into that same decoder :-/
|
||||
// See kubernetes/kubernetes#74373.
|
||||
|
||||
// we error out if rawObj is an empty object.
|
||||
if len(rawObj.Raw) == 0 {
|
||||
return fmt.Errorf("there is no content to decode")
|
||||
}
|
||||
if unstructuredInto, isUnstructured := into.(*unstructured.Unstructured); isUnstructured {
|
||||
// unmarshal into unstructured's underlying object to avoid calling the decoder
|
||||
if err := json.Unmarshal(rawObj.Raw, &unstructuredInto.Object); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
deserializer := d.codecs.UniversalDeserializer()
|
||||
return runtime.DecodeInto(deserializer, rawObj.Raw, into)
|
||||
}
|
75
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
generated
vendored
Normal file
75
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
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"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Defaulter defines functions for setting defaults on resources
|
||||
type Defaulter interface {
|
||||
runtime.Object
|
||||
Default()
|
||||
}
|
||||
|
||||
// DefaultingWebhookFor creates a new Webhook for Defaulting the provided type.
|
||||
func DefaultingWebhookFor(defaulter Defaulter) *Webhook {
|
||||
return &Webhook{
|
||||
Handler: &mutatingHandler{defaulter: defaulter},
|
||||
}
|
||||
}
|
||||
|
||||
type mutatingHandler struct {
|
||||
defaulter Defaulter
|
||||
decoder *Decoder
|
||||
}
|
||||
|
||||
var _ DecoderInjector = &mutatingHandler{}
|
||||
|
||||
// InjectDecoder injects the decoder into a mutatingHandler.
|
||||
func (h *mutatingHandler) InjectDecoder(d *Decoder) error {
|
||||
h.decoder = d
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle handles admission requests.
|
||||
func (h *mutatingHandler) Handle(ctx context.Context, req Request) Response {
|
||||
if h.defaulter == nil {
|
||||
panic("defaulter should never be nil")
|
||||
}
|
||||
|
||||
// Get the object in the request
|
||||
obj := h.defaulter.DeepCopyObject().(Defaulter)
|
||||
err := h.decoder.Decode(req, obj)
|
||||
if err != nil {
|
||||
return Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
// Default the object
|
||||
obj.Default()
|
||||
marshalled, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return Errored(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
// Create the patch
|
||||
return PatchResponseFromRaw(req.Object.Raw, marshalled)
|
||||
}
|
28
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
generated
vendored
Normal file
28
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/doc.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
See examples/mutatingwebhook.go and examples/validatingwebhook.go for examples of admission webhooks.
|
||||
*/
|
||||
package admission
|
||||
|
||||
import (
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
)
|
||||
|
||||
var log = logf.RuntimeLog.WithName("admission")
|
104
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
generated
vendored
Normal file
104
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/http.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var admissionScheme = runtime.NewScheme()
|
||||
var admissionCodecs = serializer.NewCodecFactory(admissionScheme)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(admissionv1beta1.AddToScheme(admissionScheme))
|
||||
}
|
||||
|
||||
var _ http.Handler = &Webhook{}
|
||||
|
||||
func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
var reviewResponse Response
|
||||
if r.Body != nil {
|
||||
if body, err = ioutil.ReadAll(r.Body); err != nil {
|
||||
wh.log.Error(err, "unable to read the body from the incoming request")
|
||||
reviewResponse = Errored(http.StatusBadRequest, err)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = errors.New("request body is empty")
|
||||
wh.log.Error(err, "bad request")
|
||||
reviewResponse = Errored(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, expected application/json", contentType)
|
||||
wh.log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
|
||||
reviewResponse = Errored(http.StatusBadRequest, err)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
return
|
||||
}
|
||||
|
||||
req := Request{}
|
||||
ar := v1beta1.AdmissionReview{
|
||||
// avoid an extra copy
|
||||
Request: &req.AdmissionRequest,
|
||||
}
|
||||
if _, _, err := admissionCodecs.UniversalDeserializer().Decode(body, nil, &ar); err != nil {
|
||||
wh.log.Error(err, "unable to decode the request")
|
||||
reviewResponse = Errored(http.StatusBadRequest, err)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
return
|
||||
}
|
||||
wh.log.V(1).Info("received request", "UID", req.UID, "kind", req.Kind, "resource", req.Resource)
|
||||
|
||||
// TODO: add panic-recovery for Handle
|
||||
reviewResponse = wh.Handle(r.Context(), req)
|
||||
wh.writeResponse(w, reviewResponse)
|
||||
}
|
||||
|
||||
func (wh *Webhook) writeResponse(w io.Writer, response Response) {
|
||||
encoder := json.NewEncoder(w)
|
||||
responseAdmissionReview := v1beta1.AdmissionReview{
|
||||
Response: &response.AdmissionResponse,
|
||||
}
|
||||
err := encoder.Encode(responseAdmissionReview)
|
||||
if err != nil {
|
||||
wh.log.Error(err, "unable to encode the response")
|
||||
wh.writeResponse(w, Errored(http.StatusInternalServerError, err))
|
||||
} else {
|
||||
res := responseAdmissionReview.Response
|
||||
wh.log.V(1).Info("wrote response", "UID", res.UID, "allowed", res.Allowed, "result", res.Result)
|
||||
}
|
||||
}
|
31
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go
generated
vendored
Normal file
31
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/inject.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2019 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
|
||||
|
||||
// DecoderInjector is used by the ControllerManager to inject decoder into webhook handlers.
|
||||
type DecoderInjector interface {
|
||||
InjectDecoder(*Decoder) error
|
||||
}
|
||||
|
||||
// InjectDecoderInto will set decoder on i and return the result if it implements Decoder. Returns
|
||||
// false if i does not implement Decoder.
|
||||
func InjectDecoderInto(decoder *Decoder, i interface{}) (bool, error) {
|
||||
if s, ok := i.(DecoderInjector); ok {
|
||||
return true, s.InjectDecoder(decoder)
|
||||
}
|
||||
return false, nil
|
||||
}
|
126
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go
generated
vendored
Normal file
126
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/multi.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
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"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
)
|
||||
|
||||
type multiMutating []Handler
|
||||
|
||||
func (hs multiMutating) Handle(ctx context.Context, req Request) Response {
|
||||
patches := []jsonpatch.JsonPatchOperation{}
|
||||
for _, handler := range hs {
|
||||
resp := handler.Handle(ctx, req)
|
||||
if !resp.Allowed {
|
||||
return resp
|
||||
}
|
||||
if resp.PatchType != nil && *resp.PatchType != admissionv1beta1.PatchTypeJSONPatch {
|
||||
return Errored(http.StatusInternalServerError,
|
||||
fmt.Errorf("unexpected patch type returned by the handler: %v, only allow: %v",
|
||||
resp.PatchType, admissionv1beta1.PatchTypeJSONPatch))
|
||||
}
|
||||
patches = append(patches, resp.Patches...)
|
||||
}
|
||||
var err error
|
||||
marshaledPatch, err := json.Marshal(patches)
|
||||
if err != nil {
|
||||
return Errored(http.StatusBadRequest, fmt.Errorf("error when marshaling the patch: %w", err))
|
||||
}
|
||||
return Response{
|
||||
AdmissionResponse: admissionv1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Code: http.StatusOK,
|
||||
},
|
||||
Patch: marshaledPatch,
|
||||
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// InjectFunc injects the field setter into the handlers.
|
||||
func (hs multiMutating) InjectFunc(f inject.Func) error {
|
||||
// inject directly into the handlers. It would be more correct
|
||||
// to do this in a sync.Once in Handle (since we don't have some
|
||||
// other start/finalize-type method), but it's more efficient to
|
||||
// do it here, presumably.
|
||||
for _, handler := range hs {
|
||||
if err := f(handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultiMutatingHandler combines multiple mutating webhook handlers into a single
|
||||
// mutating webhook handler. Handlers are called in sequential order, and the first
|
||||
// `allowed: false` response may short-circuit the rest. Users must take care to
|
||||
// ensure patches are disjoint.
|
||||
func MultiMutatingHandler(handlers ...Handler) Handler {
|
||||
return multiMutating(handlers)
|
||||
}
|
||||
|
||||
type multiValidating []Handler
|
||||
|
||||
func (hs multiValidating) Handle(ctx context.Context, req Request) Response {
|
||||
for _, handler := range hs {
|
||||
resp := handler.Handle(ctx, req)
|
||||
if !resp.Allowed {
|
||||
return resp
|
||||
}
|
||||
}
|
||||
return Response{
|
||||
AdmissionResponse: admissionv1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Code: http.StatusOK,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MultiValidatingHandler combines multiple validating webhook handlers into a single
|
||||
// validating webhook handler. Handlers are called in sequential order, and the first
|
||||
// `allowed: false` response may short-circuit the rest.
|
||||
func MultiValidatingHandler(handlers ...Handler) Handler {
|
||||
return multiValidating(handlers)
|
||||
}
|
||||
|
||||
// InjectFunc injects the field setter into the handlers.
|
||||
func (hs multiValidating) InjectFunc(f inject.Func) error {
|
||||
// inject directly into the handlers. It would be more correct
|
||||
// to do this in a sync.Once in Handle (since we don't have some
|
||||
// other start/finalize-type method), but it's more efficient to
|
||||
// do it here, presumably.
|
||||
for _, handler := range hs {
|
||||
if err := f(handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
98
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
generated
vendored
Normal file
98
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/response.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Allowed constructs a response indicating that the given operation
|
||||
// is allowed (without any patches).
|
||||
func Allowed(reason string) Response {
|
||||
return ValidationResponse(true, reason)
|
||||
}
|
||||
|
||||
// Denied constructs a response indicating that the given operation
|
||||
// is not allowed.
|
||||
func Denied(reason string) Response {
|
||||
return ValidationResponse(false, reason)
|
||||
}
|
||||
|
||||
// Patched constructs a response indicating that the given operation is
|
||||
// allowed, and that the target object should be modified by the given
|
||||
// JSONPatch operations.
|
||||
func Patched(reason string, patches ...jsonpatch.JsonPatchOperation) Response {
|
||||
resp := Allowed(reason)
|
||||
resp.Patches = patches
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// Errored creates a new Response for error-handling a request.
|
||||
func Errored(code int32, err error) Response {
|
||||
return Response{
|
||||
AdmissionResponse: 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) Response {
|
||||
code := http.StatusForbidden
|
||||
if allowed {
|
||||
code = http.StatusOK
|
||||
}
|
||||
resp := Response{
|
||||
AdmissionResponse: admissionv1beta1.AdmissionResponse{
|
||||
Allowed: allowed,
|
||||
Result: &metav1.Status{
|
||||
Code: int32(code),
|
||||
},
|
||||
},
|
||||
}
|
||||
if len(reason) > 0 {
|
||||
resp.Result.Reason = metav1.StatusReason(reason)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
// PatchResponseFromRaw takes 2 byte arrays and returns a new response with json patch.
|
||||
// The original object should be passed in as raw bytes to avoid the roundtripping problem
|
||||
// described in https://github.com/kubernetes-sigs/kubebuilder/issues/510.
|
||||
func PatchResponseFromRaw(original, current []byte) Response {
|
||||
patches, err := jsonpatch.CreatePatch(original, current)
|
||||
if err != nil {
|
||||
return Errored(http.StatusInternalServerError, err)
|
||||
}
|
||||
return Response{
|
||||
Patches: patches,
|
||||
AdmissionResponse: admissionv1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
PatchType: func() *admissionv1beta1.PatchType { pt := admissionv1beta1.PatchTypeJSONPatch; return &pt }(),
|
||||
},
|
||||
}
|
||||
}
|
108
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
generated
vendored
Normal file
108
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
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"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Validator defines functions for validating an operation
|
||||
type Validator interface {
|
||||
runtime.Object
|
||||
ValidateCreate() error
|
||||
ValidateUpdate(old runtime.Object) error
|
||||
ValidateDelete() error
|
||||
}
|
||||
|
||||
// ValidatingWebhookFor creates a new Webhook for validating the provided type.
|
||||
func ValidatingWebhookFor(validator Validator) *Webhook {
|
||||
return &Webhook{
|
||||
Handler: &validatingHandler{validator: validator},
|
||||
}
|
||||
}
|
||||
|
||||
type validatingHandler struct {
|
||||
validator Validator
|
||||
decoder *Decoder
|
||||
}
|
||||
|
||||
var _ DecoderInjector = &validatingHandler{}
|
||||
|
||||
// InjectDecoder injects the decoder into a validatingHandler.
|
||||
func (h *validatingHandler) InjectDecoder(d *Decoder) error {
|
||||
h.decoder = d
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle handles admission requests.
|
||||
func (h *validatingHandler) Handle(ctx context.Context, req Request) Response {
|
||||
if h.validator == nil {
|
||||
panic("validator should never be nil")
|
||||
}
|
||||
|
||||
// Get the object in the request
|
||||
obj := h.validator.DeepCopyObject().(Validator)
|
||||
if req.Operation == v1beta1.Create {
|
||||
err := h.decoder.Decode(req, obj)
|
||||
if err != nil {
|
||||
return Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
err = obj.ValidateCreate()
|
||||
if err != nil {
|
||||
return Denied(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if req.Operation == v1beta1.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 = obj.ValidateUpdate(oldObj)
|
||||
if err != nil {
|
||||
return Denied(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if req.Operation == v1beta1.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 = obj.ValidateDelete()
|
||||
if err != nil {
|
||||
return Denied(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return Allowed("")
|
||||
}
|
200
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
generated
vendored
Normal file
200
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/webhook.go
generated
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
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"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnableToEncodeResponse = errors.New("unable to encode response")
|
||||
)
|
||||
|
||||
// Request defines the input for an admission handler.
|
||||
// It contains information to identify the object in
|
||||
// question (group, version, kind, resource, subresource,
|
||||
// name, namespace), as well as the operation in question
|
||||
// (e.g. Get, Create, etc), and the object itself.
|
||||
type Request struct {
|
||||
admissionv1beta1.AdmissionRequest
|
||||
}
|
||||
|
||||
// Response is the output of an admission handler.
|
||||
// It contains a response indicating if a given
|
||||
// operation is allowed, as well as a set of patches
|
||||
// to mutate the object in the case of a mutating admission handler.
|
||||
type Response struct {
|
||||
// Patches are the JSON patches for mutating webhooks.
|
||||
// Using this instead of setting Response.Patch to minimize
|
||||
// overhead of serialization and deserialization.
|
||||
// Patches set here will override any patches in the response,
|
||||
// so leave this empty if you want to set the patch response directly.
|
||||
Patches []jsonpatch.JsonPatchOperation
|
||||
// AdmissionResponse is the raw admission response.
|
||||
// The Patch field in it will be overwritten by the listed patches.
|
||||
admissionv1beta1.AdmissionResponse
|
||||
}
|
||||
|
||||
// Complete populates any fields that are yet to be set in
|
||||
// the underlying AdmissionResponse, It mutates the response.
|
||||
func (r *Response) Complete(req Request) error {
|
||||
r.UID = req.UID
|
||||
|
||||
// ensure that we have a valid status code
|
||||
if r.Result == nil {
|
||||
r.Result = &metav1.Status{}
|
||||
}
|
||||
if r.Result.Code == 0 {
|
||||
r.Result.Code = http.StatusOK
|
||||
}
|
||||
// TODO(directxman12): do we need to populate this further, and/or
|
||||
// is code actually necessary (the same webhook doesn't use it)
|
||||
|
||||
if len(r.Patches) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
r.Patch, err = json.Marshal(r.Patches)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patchType := admissionv1beta1.PatchTypeJSONPatch
|
||||
r.PatchType = &patchType
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handler can handle an AdmissionRequest.
|
||||
type Handler interface {
|
||||
// Handle yields a response to an AdmissionRequest.
|
||||
//
|
||||
// The supplied context is extracted from the received http.Request, allowing wrapping
|
||||
// http.Handlers to inject values into and control cancelation of downstream request processing.
|
||||
Handle(context.Context, Request) Response
|
||||
}
|
||||
|
||||
// HandlerFunc implements Handler interface using a single function.
|
||||
type HandlerFunc func(context.Context, Request) Response
|
||||
|
||||
var _ Handler = HandlerFunc(nil)
|
||||
|
||||
// Handle process the AdmissionRequest by invoking the underlying function.
|
||||
func (f HandlerFunc) Handle(ctx context.Context, req Request) Response {
|
||||
return f(ctx, req)
|
||||
}
|
||||
|
||||
// Webhook represents each individual webhook.
|
||||
type Webhook struct {
|
||||
// Handler actually processes an admission request returning whether it was allowed or denied,
|
||||
// and potentially patches to apply to the handler.
|
||||
Handler Handler
|
||||
|
||||
// decoder is constructed on receiving a scheme and passed down to then handler
|
||||
decoder *Decoder
|
||||
|
||||
log logr.Logger
|
||||
}
|
||||
|
||||
// InjectLogger gets a handle to a logging instance, hopefully with more info about this particular webhook.
|
||||
func (w *Webhook) InjectLogger(l logr.Logger) error {
|
||||
w.log = l
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 Request) Response {
|
||||
resp := w.Handler.Handle(ctx, req)
|
||||
if err := resp.Complete(req); err != nil {
|
||||
w.log.Error(err, "unable to encode response")
|
||||
return Errored(http.StatusInternalServerError, errUnableToEncodeResponse)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// InjectScheme injects a scheme into the webhook, in order to construct a Decoder.
|
||||
func (w *Webhook) InjectScheme(s *runtime.Scheme) error {
|
||||
// TODO(directxman12): we should have a better way to pass this down
|
||||
|
||||
var err error
|
||||
w.decoder, err = NewDecoder(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// inject the decoder here too, just in case the order of calling this is not
|
||||
// scheme first, then inject func
|
||||
if w.Handler != nil {
|
||||
if _, err := InjectDecoderInto(w.GetDecoder(), w.Handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDecoder returns a decoder to decode the objects embedded in admission requests.
|
||||
// It may be nil if we haven't received a scheme to use to determine object types yet.
|
||||
func (w *Webhook) GetDecoder() *Decoder {
|
||||
return w.decoder
|
||||
}
|
||||
|
||||
// InjectFunc injects the field setter into the webhook.
|
||||
func (w *Webhook) InjectFunc(f inject.Func) error {
|
||||
// inject directly into the handlers. It would be more correct
|
||||
// to do this in a sync.Once in Handle (since we don't have some
|
||||
// other start/finalize-type method), but it's more efficient to
|
||||
// do it here, presumably.
|
||||
|
||||
// also inject a decoder, and wrap this so that we get a setFields
|
||||
// that injects a decoder (hopefully things don't ignore the duplicate
|
||||
// InjectorInto call).
|
||||
|
||||
var setFields inject.Func
|
||||
setFields = func(target interface{}) error {
|
||||
if err := f(target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := inject.InjectorInto(setFields, target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := InjectDecoderInto(w.GetDecoder(), target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return setFields(w.Handler)
|
||||
}
|
73
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go
generated
vendored
Normal file
73
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2019 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 webhook
|
||||
|
||||
import (
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
)
|
||||
|
||||
// define some aliases for common bits of the webhook functionality
|
||||
|
||||
// Defaulter defines functions for setting defaults on resources
|
||||
type Defaulter = admission.Defaulter
|
||||
|
||||
// Validator defines functions for validating an operation
|
||||
type Validator = admission.Validator
|
||||
|
||||
// AdmissionRequest defines the input for an admission handler.
|
||||
// It contains information to identify the object in
|
||||
// question (group, version, kind, resource, subresource,
|
||||
// name, namespace), as well as the operation in question
|
||||
// (e.g. Get, Create, etc), and the object itself.
|
||||
type AdmissionRequest = admission.Request
|
||||
|
||||
// AdmissionResponse is the output of an admission handler.
|
||||
// It contains a response indicating if a given
|
||||
// operation is allowed, as well as a set of patches
|
||||
// to mutate the object in the case of a mutating admission handler.
|
||||
type AdmissionResponse = admission.Response
|
||||
|
||||
// Admission is webhook suitable for registration with the server
|
||||
// an admission webhook that validates API operations and potentially
|
||||
// mutates their contents.
|
||||
type Admission = admission.Webhook
|
||||
|
||||
// AdmissionHandler knows how to process admission requests, validating them,
|
||||
// and potentially mutating the objects they contain.
|
||||
type AdmissionHandler = admission.Handler
|
||||
|
||||
// AdmissionDecoder knows how to decode objects from admission requests.
|
||||
type AdmissionDecoder = admission.Decoder
|
||||
|
||||
// JSONPatchOp represents a single JSONPatch patch operation.
|
||||
type JSONPatchOp = jsonpatch.Operation
|
||||
|
||||
var (
|
||||
// Allowed indicates that the admission request should be allowed for the given reason.
|
||||
Allowed = admission.Allowed
|
||||
|
||||
// Denied indicates that the admission request should be denied for the given reason.
|
||||
Denied = admission.Denied
|
||||
|
||||
// Patched indicates that the admission request should be allowed for the given reason,
|
||||
// and that the contained object should be mutated using the given patches.
|
||||
Patched = admission.Patched
|
||||
|
||||
// Errored indicates that an error occurred in the admission request.
|
||||
Errored = admission.Errored
|
||||
)
|
28
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go
generated
vendored
Normal file
28
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/doc.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
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 webhook provides methods to build and bootstrap a webhook server.
|
||||
|
||||
Currently, it only supports admission webhooks. It will support CRD conversion webhooks in the near future.
|
||||
*/
|
||||
package webhook
|
||||
|
||||
import (
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
)
|
||||
|
||||
var log = logf.RuntimeLog.WithName("webhook")
|
162
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/certwatcher/certwatcher.go
generated
vendored
Normal file
162
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/certwatcher/certwatcher.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
Copyright 2019 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 certwatcher
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/fsnotify.v1"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
||||
)
|
||||
|
||||
var log = logf.RuntimeLog.WithName("certwatcher")
|
||||
|
||||
// CertWatcher watches certificate and key files for changes. When either file
|
||||
// changes, it reads and parses both and calls an optional callback with the new
|
||||
// certificate.
|
||||
type CertWatcher struct {
|
||||
sync.Mutex
|
||||
|
||||
currentCert *tls.Certificate
|
||||
watcher *fsnotify.Watcher
|
||||
|
||||
certPath string
|
||||
keyPath string
|
||||
}
|
||||
|
||||
// New returns a new CertWatcher watching the given certificate and key.
|
||||
func New(certPath, keyPath string) (*CertWatcher, error) {
|
||||
var err error
|
||||
|
||||
cw := &CertWatcher{
|
||||
certPath: certPath,
|
||||
keyPath: keyPath,
|
||||
}
|
||||
|
||||
// Initial read of certificate and key.
|
||||
if err := cw.ReadCertificate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cw.watcher, err = fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cw, nil
|
||||
}
|
||||
|
||||
// GetCertificate fetches the currently loaded certificate, which may be nil.
|
||||
func (cw *CertWatcher) GetCertificate(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
cw.Lock()
|
||||
defer cw.Unlock()
|
||||
return cw.currentCert, nil
|
||||
}
|
||||
|
||||
// Start starts the watch on the certificate and key files.
|
||||
func (cw *CertWatcher) Start(stopCh <-chan struct{}) error {
|
||||
files := []string{cw.certPath, cw.keyPath}
|
||||
|
||||
for _, f := range files {
|
||||
if err := cw.watcher.Add(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
go cw.Watch()
|
||||
|
||||
log.Info("Starting certificate watcher")
|
||||
|
||||
// Block until the stop channel is closed.
|
||||
<-stopCh
|
||||
|
||||
return cw.watcher.Close()
|
||||
}
|
||||
|
||||
// Watch reads events from the watcher's channel and reacts to changes.
|
||||
func (cw *CertWatcher) Watch() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-cw.watcher.Events:
|
||||
// Channel is closed.
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
cw.handleEvent(event)
|
||||
|
||||
case err, ok := <-cw.watcher.Errors:
|
||||
// Channel is closed.
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
log.Error(err, "certificate watch error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadCertificate reads the certificate and key files from disk, parses them,
|
||||
// and updates the current certificate on the watcher. If a callback is set, it
|
||||
// is invoked with the new certificate.
|
||||
func (cw *CertWatcher) ReadCertificate() error {
|
||||
cert, err := tls.LoadX509KeyPair(cw.certPath, cw.keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cw.Lock()
|
||||
cw.currentCert = &cert
|
||||
cw.Unlock()
|
||||
|
||||
log.Info("Updated current TLS certificate")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cw *CertWatcher) handleEvent(event fsnotify.Event) {
|
||||
// Only care about events which may modify the contents of the file.
|
||||
if !(isWrite(event) || isRemove(event) || isCreate(event)) {
|
||||
return
|
||||
}
|
||||
|
||||
log.V(1).Info("certificate event", "event", event)
|
||||
|
||||
// If the file was removed, re-add the watch.
|
||||
if isRemove(event) {
|
||||
if err := cw.watcher.Add(event.Name); err != nil {
|
||||
log.Error(err, "error re-watching file")
|
||||
}
|
||||
}
|
||||
|
||||
if err := cw.ReadCertificate(); err != nil {
|
||||
log.Error(err, "error re-reading certificate")
|
||||
}
|
||||
}
|
||||
|
||||
func isWrite(event fsnotify.Event) bool {
|
||||
return event.Op&fsnotify.Write == fsnotify.Write
|
||||
}
|
||||
|
||||
func isCreate(event fsnotify.Event) bool {
|
||||
return event.Op&fsnotify.Create == fsnotify.Create
|
||||
}
|
||||
|
||||
func isRemove(event fsnotify.Event) bool {
|
||||
return event.Op&fsnotify.Remove == fsnotify.Remove
|
||||
}
|
40
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go
generated
vendored
Normal file
40
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 metrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
// RequestLatency is a prometheus metric which is a histogram of the latency
|
||||
// of processing admission requests.
|
||||
RequestLatency = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "controller_runtime_webhook_latency_seconds",
|
||||
Help: "Histogram of the latency of processing admission requests",
|
||||
},
|
||||
[]string{"webhook"},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
metrics.Registry.MustRegister(
|
||||
RequestLatency)
|
||||
}
|
231
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
generated
vendored
Normal file
231
vendor/sigs.k8s.io/controller-runtime/pkg/webhook/server.go
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
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 webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/certwatcher"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics"
|
||||
)
|
||||
|
||||
// DefaultPort is the default port that the webhook server serves.
|
||||
var DefaultPort = 443
|
||||
|
||||
// Server is an admission webhook server that can serve traffic and
|
||||
// generates related k8s resources for deploying.
|
||||
type Server struct {
|
||||
// Host is the address that the server will listen on.
|
||||
// Defaults to "" - all addresses.
|
||||
Host string
|
||||
|
||||
// Port is the port number that the server will serve.
|
||||
// It will be defaulted to 443 if unspecified.
|
||||
Port int
|
||||
|
||||
// CertDir is the directory that contains the server key and certificate. The
|
||||
// server key and certificate.
|
||||
CertDir string
|
||||
|
||||
// CertName is the server certificate name. Defaults to tls.crt.
|
||||
CertName string
|
||||
|
||||
// KeyName is the server key name. Defaults to tls.key.
|
||||
KeyName string
|
||||
|
||||
// ClientCAName is the CA certificate name which server used to verify remote(client)'s certificate.
|
||||
// Defaults to "", which means server does not verify client's certificate.
|
||||
ClientCAName string
|
||||
|
||||
// WebhookMux is the multiplexer that handles different webhooks.
|
||||
WebhookMux *http.ServeMux
|
||||
|
||||
// webhooks keep track of all registered webhooks for dependency injection,
|
||||
// and to provide better panic messages on duplicate webhook registration.
|
||||
webhooks map[string]http.Handler
|
||||
|
||||
// setFields allows injecting dependencies from an external source
|
||||
setFields inject.Func
|
||||
|
||||
// defaultingOnce ensures that the default fields are only ever set once.
|
||||
defaultingOnce sync.Once
|
||||
}
|
||||
|
||||
// setDefaults does defaulting for the Server.
|
||||
func (s *Server) setDefaults() {
|
||||
s.webhooks = map[string]http.Handler{}
|
||||
if s.WebhookMux == nil {
|
||||
s.WebhookMux = http.NewServeMux()
|
||||
}
|
||||
|
||||
if s.Port <= 0 {
|
||||
s.Port = DefaultPort
|
||||
}
|
||||
|
||||
if len(s.CertDir) == 0 {
|
||||
s.CertDir = filepath.Join(os.TempDir(), "k8s-webhook-server", "serving-certs")
|
||||
}
|
||||
|
||||
if len(s.CertName) == 0 {
|
||||
s.CertName = "tls.crt"
|
||||
}
|
||||
|
||||
if len(s.KeyName) == 0 {
|
||||
s.KeyName = "tls.key"
|
||||
}
|
||||
}
|
||||
|
||||
// NeedLeaderElection implements the LeaderElectionRunnable interface, which indicates
|
||||
// the webhook server doesn't need leader election.
|
||||
func (*Server) NeedLeaderElection() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Register marks the given webhook as being served at the given path.
|
||||
// It panics if two hooks are registered on the same path.
|
||||
func (s *Server) Register(path string, hook http.Handler) {
|
||||
s.defaultingOnce.Do(s.setDefaults)
|
||||
_, found := s.webhooks[path]
|
||||
if found {
|
||||
panic(fmt.Errorf("can't register duplicate path: %v", path))
|
||||
}
|
||||
// TODO(directxman12): call setfields if we've already started the server
|
||||
s.webhooks[path] = hook
|
||||
s.WebhookMux.Handle(path, instrumentedHook(path, hook))
|
||||
log.Info("registering webhook", "path", path)
|
||||
}
|
||||
|
||||
// instrumentedHook adds some instrumentation on top of the given webhook.
|
||||
func instrumentedHook(path string, hookRaw http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
startTS := time.Now()
|
||||
defer func() { metrics.RequestLatency.WithLabelValues(path).Observe(time.Since(startTS).Seconds()) }()
|
||||
hookRaw.ServeHTTP(resp, req)
|
||||
|
||||
// TODO(directxman12): add back in metric about total requests broken down by result?
|
||||
})
|
||||
}
|
||||
|
||||
// Start runs the server.
|
||||
// It will install the webhook related resources depend on the server configuration.
|
||||
func (s *Server) Start(stop <-chan struct{}) error {
|
||||
s.defaultingOnce.Do(s.setDefaults)
|
||||
|
||||
baseHookLog := log.WithName("webhooks")
|
||||
baseHookLog.Info("starting webhook server")
|
||||
|
||||
// inject fields here as opposed to in Register so that we're certain to have our setFields
|
||||
// function available.
|
||||
for hookPath, webhook := range s.webhooks {
|
||||
if err := s.setFields(webhook); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NB(directxman12): we don't propagate this further by wrapping setFields because it's
|
||||
// unclear if this is how we want to deal with log propagation. In this specific instance,
|
||||
// we want to be able to pass a logger to webhooks because they don't know their own path.
|
||||
if _, err := inject.LoggerInto(baseHookLog.WithValues("webhook", hookPath), webhook); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
certPath := filepath.Join(s.CertDir, s.CertName)
|
||||
keyPath := filepath.Join(s.CertDir, s.KeyName)
|
||||
|
||||
certWatcher, err := certwatcher.New(certPath, keyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := certWatcher.Start(stop); err != nil {
|
||||
log.Error(err, "certificate watcher error")
|
||||
}
|
||||
}()
|
||||
|
||||
cfg := &tls.Config{
|
||||
NextProtos: []string{"h2"},
|
||||
GetCertificate: certWatcher.GetCertificate,
|
||||
}
|
||||
|
||||
// load CA to verify client certificate
|
||||
if s.ClientCAName != "" {
|
||||
certPool := x509.NewCertPool()
|
||||
clientCABytes, err := ioutil.ReadFile(filepath.Join(s.CertDir, s.ClientCAName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read client CA cert: %v", err)
|
||||
}
|
||||
|
||||
ok := certPool.AppendCertsFromPEM(clientCABytes)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to append client CA cert to CA pool")
|
||||
}
|
||||
|
||||
cfg.ClientCAs = certPool
|
||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
listener, err := tls.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(int(s.Port))), cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("serving webhook server", "host", s.Host, "port", s.Port)
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: s.WebhookMux,
|
||||
}
|
||||
|
||||
idleConnsClosed := make(chan struct{})
|
||||
go func() {
|
||||
<-stop
|
||||
log.Info("shutting down webhook server")
|
||||
|
||||
// TODO: use a context with reasonable timeout
|
||||
if err := srv.Shutdown(context.Background()); err != nil {
|
||||
// Error from closing listeners, or context timeout
|
||||
log.Error(err, "error shutting down the HTTP server")
|
||||
}
|
||||
close(idleConnsClosed)
|
||||
}()
|
||||
|
||||
err = srv.Serve(listener)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
||||
<-idleConnsClosed
|
||||
return nil
|
||||
}
|
||||
|
||||
// InjectFunc injects the field setter into the server.
|
||||
func (s *Server) InjectFunc(f inject.Func) error {
|
||||
s.setFields = f
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user