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  }