github.com/kubewharf/katalyst-core@v0.5.3/pkg/webhook/validating/vpa/vpa.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package vpa 18 19 import ( 20 "context" 21 "fmt" 22 23 kubewebhook "github.com/slok/kubewebhook/pkg/webhook" 24 "github.com/slok/kubewebhook/pkg/webhook/validating" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/client-go/tools/cache" 27 "k8s.io/klog/v2" 28 29 apis "github.com/kubewharf/katalyst-api/pkg/apis/autoscaling/v1alpha1" 30 apiListers "github.com/kubewharf/katalyst-api/pkg/client/listers/autoscaling/v1alpha1" 31 katalystbase "github.com/kubewharf/katalyst-core/cmd/base" 32 webhookconsts "github.com/kubewharf/katalyst-core/cmd/katalyst-webhook/app/webhook" 33 "github.com/kubewharf/katalyst-core/pkg/config/generic" 34 webhookconfig "github.com/kubewharf/katalyst-core/pkg/config/webhook" 35 "github.com/kubewharf/katalyst-core/pkg/metrics" 36 ) 37 38 const ( 39 vpaWebhookName = "vpa" 40 ) 41 42 // WebhookVPA is the implementation of Kubernetes Webhook 43 // any implementation should at least implement the interface of mutating.Mutator of validating.Validator 44 type WebhookVPA struct { 45 ctx context.Context 46 dryRun bool 47 48 validators []WebhookVPAValidator 49 metricEmitter metrics.MetricEmitter 50 51 // vpaListerSynced returns true if the VerticalPodAutoscaler store has been synced at least once. 52 vpaListerSynced cache.InformerSynced 53 // vpaLister can list/get VerticalPodAutoscaler from the shared informer's store 54 vpaLister apiListers.KatalystVerticalPodAutoscalerLister 55 } 56 57 type WebhookVPAValidator interface { 58 ValidateVPA(vpa *apis.KatalystVerticalPodAutoscaler) (valid bool, message string, err error) 59 } 60 61 func NewWebhookVPA(ctx context.Context, webhookCtx *katalystbase.GenericContext, 62 genericConf *generic.GenericConfiguration, _ *webhookconfig.GenericWebhookConfiguration, 63 _ *webhookconfig.WebhooksConfiguration, metricsEmitter metrics.MetricEmitter, 64 ) (kubewebhook.Webhook, webhookconsts.GenericStartFunc, error) { 65 wa := &WebhookVPA{ 66 ctx: ctx, 67 dryRun: genericConf.DryRun, 68 } 69 70 wa.metricEmitter = metricsEmitter 71 if metricsEmitter == nil { 72 wa.metricEmitter = metrics.DummyMetrics{} 73 } 74 75 wa.vpaListerSynced = webhookCtx.InternalInformerFactory.Autoscaling().V1alpha1().KatalystVerticalPodAutoscalers().Informer().HasSynced 76 wa.vpaLister = webhookCtx.InternalInformerFactory.Autoscaling().V1alpha1().KatalystVerticalPodAutoscalers().Lister() 77 78 wa.validators = []WebhookVPAValidator{ 79 NewWebhookVPAPodNameContainerNameValidator(), 80 NewWebhookVPAOverlapValidator(wa.vpaLister), 81 NewWebhookVPAPolicyValidator(), 82 } 83 84 cfg := validating.WebhookConfig{ 85 Name: "vpaValidator", 86 Obj: &apis.KatalystVerticalPodAutoscaler{}, 87 } 88 89 webhook, err := validating.NewWebhook(cfg, wa, nil, nil, nil) 90 if err != nil { 91 return nil, wa.Run, err 92 } 93 return webhook, wa.Run, nil 94 } 95 96 func (wa *WebhookVPA) Run() bool { 97 if !cache.WaitForCacheSync(wa.ctx.Done(), wa.vpaListerSynced) { 98 klog.Errorf("unable to sync caches for %s webhook", vpaWebhookName) 99 return false 100 } 101 klog.Infof("Caches are synced for %s webhook", vpaWebhookName) 102 103 return true 104 } 105 106 func (wa *WebhookVPA) Validate(_ context.Context, obj metav1.Object) (bool, validating.ValidatorResult, error) { 107 klog.V(5).Info("notice an obj to be validated") 108 vpa, ok := obj.(*apis.KatalystVerticalPodAutoscaler) 109 if !ok { 110 err := fmt.Errorf("failed to convert obj to vpa: %v", obj) 111 klog.Error(err.Error()) 112 return false, validating.ValidatorResult{}, err 113 } 114 if vpa == nil { 115 err := fmt.Errorf("vpa can't be nil") 116 klog.Error(err.Error()) 117 return false, validating.ValidatorResult{}, err 118 } 119 120 klog.V(5).Infof("begin to validate vpa %s", vpa.Name) 121 122 for _, validator := range wa.validators { 123 succeed, msg, err := validator.ValidateVPA(vpa) 124 if err != nil { 125 klog.Errorf("an err occurred when validating vpa %s", vpa.Name) 126 _ = wa.metricEmitter.StoreInt64("vpa_webhook_error", 1, metrics.MetricTypeNameCount, 127 metrics.MetricTag{Key: "name", Val: vpa.Name}) 128 return false, validating.ValidatorResult{}, err 129 } else if !succeed { 130 klog.Infof("vpa %s didn't pass the webhook", vpa.Name) 131 _ = wa.metricEmitter.StoreInt64("vpa_webhook_fail", 1, metrics.MetricTypeNameCount, 132 metrics.MetricTag{Key: "name", Val: vpa.Name}) 133 if !wa.dryRun { 134 return false, validating.ValidatorResult{Valid: false, Message: msg}, nil 135 } 136 } 137 } 138 139 _ = wa.metricEmitter.StoreInt64("vpa_webhook_succeed", 1, metrics.MetricTypeNameCount, 140 metrics.MetricTag{Key: "name", Val: vpa.Name}) 141 klog.Infof("vpa %s passed the validation webhook", vpa.Name) 142 return false, validating.ValidatorResult{Valid: true, Message: "validation succeed"}, nil 143 }