pkg/webhook/ndb_admission_controller.go (69 lines of code) (raw):
// Copyright (c) 2021, 2022, Oracle and/or its affiliates.
//
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
package webhook
import (
v1 "github.com/mysql/ndb-operator/pkg/apis/ndbcontroller/v1"
admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
)
// ndbAdmissionController implements admissionController for Ndb resource
type ndbAdmissionController struct{}
func newNdbAdmissionController() admissionController {
return &ndbAdmissionController{}
}
func (nv *ndbAdmissionController) getGVR() *metav1.GroupVersionResource {
return &metav1.GroupVersionResource{
Group: "mysql.oracle.com",
Version: "v1",
Resource: "ndbclusters",
}
}
func (nv *ndbAdmissionController) getGVK() *schema.GroupVersionKind {
return &schema.GroupVersionKind{
Group: "mysql.oracle.com",
Version: "v1",
Kind: "ndbcluster",
}
}
func (nv *ndbAdmissionController) newObject() runtime.Object {
return &v1.NdbCluster{}
}
func (nv *ndbAdmissionController) validateCreate(reqUID types.UID, obj runtime.Object) *admissionv1.AdmissionResponse {
nc := obj.(*v1.NdbCluster)
if isValid, errList := nc.HasValidSpec(); !isValid {
// ndb does not define a valid configuration
return requestDeniedNdbInvalid(reqUID, nc, errList)
}
return requestAllowed(reqUID)
}
func (nv *ndbAdmissionController) validateUpdate(
reqUID types.UID, newObj runtime.Object, oldObj runtime.Object) *admissionv1.AdmissionResponse {
oldNC := oldObj.(*v1.NdbCluster)
// The Operator can handle only one update at a moment, so disallow
// any update when the previous update has not completed yet.
// In case of previous update failing due to an error, allow the
// new update as it might be attempting to fix the error.
if oldNC.Status.ProcessedGeneration != oldNC.Generation && !oldNC.HasSyncError() {
// The previous update is still being applied, and the sync has
// not encountered any errors so far - disallow new update.
return requestDenied(reqUID,
errors.NewTooManyRequestsError("previous update to the NdbCluster resource is still being applied"))
}
newNC := newObj.(*v1.NdbCluster)
if isValid, errList := oldNC.IsValidSpecUpdate(newNC); !isValid {
// new ndb does not define a valid configuration
return requestDeniedNdbInvalid(reqUID, newNC, errList)
}
return requestAllowed(reqUID)
}
func (nv *ndbAdmissionController) mutate(obj runtime.Object) *jsonPatchOperations {
nc := obj.(*v1.NdbCluster)
var patchOps jsonPatchOperations
// Always attach atleast one MySQL Server to the MySQL Cluster setup
if nc.Spec.MysqlNode == nil {
patchOps.add("/spec/mysqlNode", map[string]interface{}{
"nodeCount": 1,
"maxNodeCount": 1,
})
} else if nc.Spec.MysqlNode.NodeCount == 0 {
// 0 MySQL Servers required - update to start atleast 1 MySQL Server
patchOps.replace("/spec/mysqlNode/nodeCount", 1)
if nc.Spec.MysqlNode.MaxNodeCount == 0 {
patchOps.replace("/spec/mysqlNode/maxNodeCount", 1)
}
} else if nc.Spec.MysqlNode.MaxNodeCount == 0 {
// NodeCount specified but MaxNodeCount unspecified - default it to nodeCount + 2
patchOps.replace("/spec/mysqlNode/maxNodeCount", nc.Spec.MysqlNode.NodeCount+2)
}
return &patchOps
}