Operations is the operations the admission hook cares about - CREATE, UPDATE, DELETE, CONNECT or * for all of those operations and any future admission operations that are added. If * is present, the length of the slice must be one.
cue
string
N
Cue represents validate rules defined with cue code.
Template of condition which defines validate cond, and it will be rendered to CUE and store in RenderedCue field, so if there are any data added manually will be erased.
renderedCue
string
N
RenderedCue represents validate rule defined by Template. Don’t modify the value of this field, modify Rules instead of.
这里是定义 validate 策略的执行逻辑相关信息。
targetOperations:表示生效的操作类型,即可以定义只对创建 or delete 事件生效
Template of rule which defines override rule, and it will be rendered to CUE and store in RenderedCue field, so if there are any data added manually will be erased.
renderedCue
string
N
RenderedCue represents override rule defined by Template. Don’t modify the value of this field, modify Rules instead of.
# 对当前资源进行超卖,即修改资源的request为 limit * factor,从而可以达到集群内资源超卖的效果overrideRules:- targetOperations:- CREATEoverriders:template:type:resourcesOversell# 以超售为例operation:replace# set remove to reset oversellresourcesOversell:cpuFactor:"0.5"# use half of limit / set 0 as placeholder when it needed removememoryFactor:"0.2"# use 1/5 of limitdiskFactor:"0.1"# use 1/10 of limit
// Run runs the webhook server with options. This should never exit.
funcRun(ctxcontext.Context,opts*options.Options)error{klog.InfoS("kinitiras webhook starting.","version",version.Get())config,err:=controllerruntime.GetConfig()iferr!=nil{panic(err)}config.QPS,config.Burst=opts.KubeAPIQPS,opts.KubeAPIBursthookManager,err:=controllerruntime.NewManager(config,controllerruntime.Options{// ... set options
})iferr!=nil{klog.ErrorS(err,"failed to build webhook server.")returnerr}// init clients, informer, lister
sm:=&setupManager{}iferr:=sm.init(hookManager,ctx.Done());err!=nil{klog.ErrorS(err,"init setup manager failed")returnerr}iferr:=sm.waitForCacheSync(ctx);err!=nil{klog.ErrorS(err,"wait for cache sync failed")returnerr}iferr:=sm.setupInterrupter();err!=nil{klog.ErrorS(err,"setup interrupter failed")returnerr}setupCh,err:=cert.SetupCertRotator(hookManager,cert.Options{// ... set options
})iferr!=nil{klog.ErrorS(err,"failed to setup cert rotator controller.")returnerr}gofunc(){<-setupCh// register handler here
hookServer:=hookManager.GetWebhookServer()hookServer.Register("/mutate",&webhook.Admission{Handler:pkgwebhook.NewMutatingAdmissionHandler(sm.overrideManager,sm.policyInterrupterManager)})hookServer.Register("/validate",&webhook.Admission{Handler:pkgwebhook.NewValidatingAdmissionHandler(sm.validateManager,sm.policyInterrupterManager)})hookServer.WebhookMux.Handle("/readyz",http.StripPrefix("/readyz",&healthz.Handler{}))}()// blocks until the context is done.
iferr:=hookManager.Start(ctx);err!=nil{klog.ErrorS(err,"webhook server exits unexpectedly.")returnerr}// never reach here
returnnil}
func(a*MutatingAdmission)Handle(ctxcontext.Context,reqadmission.Request)admission.Response{obj,oldObj,err:=decodeObj(a.decoder,req)iferr!=nil{returnadmission.Errored(http.StatusBadRequest,err)}newObj:=obj.DeepCopy()// if obj is known policy, then run policy interrupter
// 这里先调用拦截器逻辑,内部识别是否为已知的策略 crd 以及是否需要对其进行模版渲染等
patches,err:=a.policyInterrupterManager.OnMutating(newObj,oldObj,req.Operation)iferr!=nil{returnadmission.Errored(http.StatusInternalServerError,err)}// 如果有需要修改的信息,则以 patches 的形式返回,这里就确定是已知的 crd,打 patch
iflen(patches)!=0{klog.V(4).InfoS("patches for policy","policy",obj.GroupVersionKind(),"patchesCount",len(patches))// patch data
patchedObj,err:=json.Marshal(newObj)iferr!=nil{returnadmission.Errored(http.StatusInternalServerError,err)}returnadmission.PatchResponseFromRaw(req.Object.Raw,patchedObj)}// 其他资源或当前策略crd
// 这里是另一个核心点,匹配&&执行 策略
cops,ops,err:=a.overrideManager.ApplyOverridePolicies(newObj,oldObj,req.Operation)iferr!=nil{returnadmission.Errored(http.StatusInternalServerError,err)}ifreq.Operation==admissionv1.Delete{returnadmission.Allowed("")}patchedObj,err:=json.Marshal(newObj)iferr!=nil{returnadmission.Errored(http.StatusInternalServerError,err)}returnadmission.PatchResponseFromRaw(req.Object.Raw,patchedObj)}
// PolicyInterrupterManager manage multi PolicyInterrupter and decide which one to use by gvk.
typePolicyInterrupterManagerinterface{PolicyInterrupter// AddInterrupter add a PolicyInterrupter to manager,
// it will replace interrupter if already add with same gvk.s
AddInterrupter(gvkschema.GroupVersionKind,piPolicyInterrupter)}// PolicyInterrupter defines interrupt process for policy change
// It validate and mutate policy.
typePolicyInterrupterinterface{// OnMutating called on "/mutating" api to complete policy
// return nil means obj is not defined policy
OnMutating(obj,oldObj*unstructured.Unstructured,operationadmissionv1.Operation)([]jsonpatchv2.JsonPatchOperation,error)// OnValidating called on "/validating" api to validate policy
// return nil means obj is not defined policy or no invalid field
OnValidating(obj,oldObj*unstructured.Unstructured,operationadmissionv1.Operation)error// OnStartUp called when webhook process initialize
// return error if initial phase get any error
OnStartUp()error}
// ResourceMatchSelectors tells if the specific resource matches the selectors.
funcResourceMatchSelectors(resource*unstructured.Unstructured,selectors...policyv1alpha1.ResourceSelector)bool{for_,rs:=rangeselectors{// 一个策略可以配置多个 selector,只要其中任意一个命中即可
ifResourceMatches(resource,rs){returntrue}}returnfalse}// ResourceMatches tells if the specific resource matches the selector.
funcResourceMatches(resource*unstructured.Unstructured,rspolicyv1alpha1.ResourceSelector)bool{ifresource.GetAPIVersion()!=rs.APIVersion||resource.GetKind()!=rs.Kind||(len(rs.Namespace)>0&&resource.GetNamespace()!=rs.Namespace){returnfalse}// name not empty, don't need to consult selector.
iflen(rs.Name)>0{returnrs.Name==resource.GetName()}// all empty, matches all
ifrs.LabelSelector==nil&&rs.FieldSelector==nil{returntrue}// matches with field selector
ifrs.FieldSelector!=nil{match,err:=rs.FieldSelector.MatchObject(resource)iferr!=nil{klog.ErrorS(err,"match fields failed")returnfalse}if!match{// return false if not match
returnfalse}}// matches with selector
ifrs.LabelSelector!=nil{varslabels.Selectorvarerrerrorifs,err=metav1.LabelSelectorAsSelector(rs.LabelSelector);err!=nil{// should not happen because all resource selector should be fully validated by webhook.
klog.ErrorS(err,"match labels failed")returnfalse}returns.Matches(labels.Set(resource.GetLabels()))}returntrue}