Files
my-rook-config/pkg/daemon/admission/controller.go
T
Vineet Badrinath ce1003aef8 ceph: adds scripts and components to support admission controllers
adds deploy.sh script to deploy validatingwebhookconfiguration and create secrets.
adds new command ceph admission-controller to start webhook servers.
adds validation for various rook custom resources

Signed-off-by: Vineet Badrinath <vbadrina@redhat.com>
2020-06-24 14:59:00 +05:30

120 lines
4.5 KiB
Go

/*
Copyright 2020 The Rook Authors. All rights reserved.
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"
"github.com/pkg/errors"
"github.com/rook/rook/pkg/clusterd"
"io/ioutil"
"k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"net/http"
)
const (
jsonContentType = `application/json`
)
var (
universalDeserializer = serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer()
)
// admitFunc is a callback for admission controller logic. Given an AdmissionRequest, it returns an error that will be shown when the operation is rejected.
type admitFunc func(*v1beta1.AdmissionRequest, *clusterd.Context) error
// doServeAdmitFunc parses the HTTP request for an admission controller webhook, and -- in case of a well-formed
// request -- delegates the admission control logic to the given admitFunc. The response body is then returned as raw
// bytes.
func doServeAdmitFunc(w http.ResponseWriter, r *http.Request, a *AdmissionController) ([]byte, error) {
// Step 1: Request validation. Only handle POST requests with a body and json content type.
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return nil, errors.Errorf("invalid method %q, only POST requess are allowed", r.Method)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return nil, errors.Wrap(err, "could not read request body")
}
if contentType := r.Header.Get("Content-Type"); contentType != jsonContentType {
w.WriteHeader(http.StatusBadRequest)
return nil, errors.Errorf("unsupported content type %q, only %q is supported", contentType, jsonContentType)
}
// Step 2: Parse the AdmissionReview request.
var admissionReviewReq v1beta1.AdmissionReview
if _, _, err := universalDeserializer.Decode(body, nil, &admissionReviewReq); err != nil {
w.WriteHeader(http.StatusBadRequest)
return nil, errors.Wrap(err, "failed to deserialize request")
} else if admissionReviewReq.Request == nil {
w.WriteHeader(http.StatusBadRequest)
return nil, errors.New("malformed admission review. request is nil")
}
logger.Infof("processing webhook request for resource type %s", admissionReviewReq.Request.Kind.Kind)
// Step 3: Construct the AdmissionReview response.
admissionReviewResponse := v1beta1.AdmissionReview{
Response: &v1beta1.AdmissionResponse{
UID: admissionReviewReq.Request.UID,
},
}
err = a.validator(admissionReviewReq.Request, a.context)
if err != nil {
// If the handler returned an error, incorporate the error message into the response and deny the object
// creation.
admissionReviewResponse.Response.Allowed = false
admissionReviewResponse.Response.Result = &metav1.Status{
Message: err.Error(),
}
} else {
admissionReviewResponse.Response.Allowed = true
}
// Return the AdmissionReview with a response as JSON.
bytes, err := json.Marshal(&admissionReviewResponse)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal response to AdmissionReview")
}
return bytes, nil
}
// serveAdmitFunc is a wrapper around doServeAdmitFunc that adds error handling and logging.
func serveAdmitFunc(w http.ResponseWriter, r *http.Request, a *AdmissionController) {
logger.Info("handling webhook request")
var writeErr error
if bytes, err := doServeAdmitFunc(w, r, a); err != nil {
logger.Errorf("failed to handle webhook request. %v", err)
w.WriteHeader(http.StatusInternalServerError)
_, writeErr = w.Write([]byte(err.Error()))
} else {
logger.Info("webhook request handled successfully")
_, writeErr = w.Write(bytes)
}
if writeErr != nil {
logger.Errorf("failed to write response to webhook request. %v", writeErr)
}
}
// admitFuncHandler takes an admitFunc and wraps it into a http.Handler by means of calling serveAdmitFunc.
func admitFuncHandler(a *AdmissionController) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serveAdmitFunc(w, r, a)
})
}