github.com/kubewharf/katalyst-core@v0.5.3/pkg/webhook/mutating/pod/pod.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 pod
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	kubewebhook "github.com/slok/kubewebhook/pkg/webhook"
    24  	whcontext "github.com/slok/kubewebhook/pkg/webhook/context"
    25  	"github.com/slok/kubewebhook/pkg/webhook/mutating"
    26  	core "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/client-go/tools/cache"
    30  	"k8s.io/klog/v2"
    31  
    32  	autoscalelister "github.com/kubewharf/katalyst-api/pkg/client/listers/autoscaling/v1alpha1"
    33  	workloadlister "github.com/kubewharf/katalyst-api/pkg/client/listers/workload/v1alpha1"
    34  	katalystbase "github.com/kubewharf/katalyst-core/cmd/base"
    35  	webhookconsts "github.com/kubewharf/katalyst-core/cmd/katalyst-webhook/app/webhook"
    36  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    37  	webhookconfig "github.com/kubewharf/katalyst-core/pkg/config/webhook"
    38  	"github.com/kubewharf/katalyst-core/pkg/consts"
    39  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    40  	"github.com/kubewharf/katalyst-core/pkg/util"
    41  )
    42  
    43  const podWebhookName = "pod"
    44  
    45  type WebhookPodMutator interface {
    46  	MutatePod(pod *core.Pod, namespace string) (allowed bool, err error)
    47  }
    48  
    49  // WebhookPod is the implementation of Kubernetes Webhook
    50  // any implementation should at least implement the interface of mutating.Mutator of validating.Validator
    51  type WebhookPod struct {
    52  	ctx context.Context
    53  
    54  	vpaIndexer cache.Indexer
    55  	spdIndexer cache.Indexer
    56  
    57  	vpaLister      autoscalelister.KatalystVerticalPodAutoscalerLister
    58  	spdLister      workloadlister.ServiceProfileDescriptorLister
    59  	workloadLister map[schema.GroupVersionKind]cache.GenericLister
    60  
    61  	syncedFunc    []cache.InformerSynced
    62  	metricEmitter metrics.MetricEmitter
    63  
    64  	mutators []WebhookPodMutator
    65  }
    66  
    67  // NewWebhookPod makes the webhook pf Pod
    68  func NewWebhookPod(
    69  	ctx context.Context,
    70  	webhookCtx *katalystbase.GenericContext,
    71  	_ *generic.GenericConfiguration,
    72  	_ *webhookconfig.GenericWebhookConfiguration,
    73  	_ *webhookconfig.WebhooksConfiguration,
    74  ) (kubewebhook.Webhook, webhookconsts.GenericStartFunc, error) {
    75  	metricEmitter := webhookCtx.EmitterPool.GetDefaultMetricsEmitter()
    76  	if metricEmitter == nil {
    77  		metricEmitter = metrics.DummyMetrics{}
    78  	}
    79  
    80  	vpaInformer := webhookCtx.InternalInformerFactory.Autoscaling().V1alpha1().KatalystVerticalPodAutoscalers()
    81  	spdInformer := webhookCtx.InternalInformerFactory.Workload().V1alpha1().ServiceProfileDescriptors()
    82  
    83  	// build indexer: workload --> vpa
    84  	if _, ok := vpaInformer.Informer().GetIndexer().GetIndexers()[consts.TargetReferenceIndex]; !ok {
    85  		err := vpaInformer.Informer().GetIndexer().AddIndexers(cache.Indexers{
    86  			consts.TargetReferenceIndex: util.VPATargetReferenceIndex,
    87  		})
    88  		if err != nil {
    89  			klog.Errorf("[pod webhook] failed to add vpa target reference index")
    90  			return nil, nil, err
    91  		}
    92  	}
    93  
    94  	// build index: workload ---> spd
    95  	if _, ok := spdInformer.Informer().GetIndexer().GetIndexers()[consts.OwnerReferenceIndex]; !ok {
    96  		err := spdInformer.Informer().AddIndexers(cache.Indexers{
    97  			consts.TargetReferenceIndex: util.SPDTargetReferenceIndex,
    98  		})
    99  		if err != nil {
   100  			klog.Errorf("[pod webhook] failed to add target reference index for spd")
   101  			return nil, nil, err
   102  		}
   103  	}
   104  
   105  	wp := &WebhookPod{
   106  		ctx:            ctx,
   107  		vpaIndexer:     vpaInformer.Informer().GetIndexer(),
   108  		spdIndexer:     spdInformer.Informer().GetIndexer(),
   109  		vpaLister:      vpaInformer.Lister(),
   110  		spdLister:      spdInformer.Lister(),
   111  		workloadLister: make(map[schema.GroupVersionKind]cache.GenericLister),
   112  		metricEmitter:  metricEmitter,
   113  		syncedFunc: []cache.InformerSynced{
   114  			vpaInformer.Informer().HasSynced,
   115  		},
   116  		mutators: []WebhookPodMutator{},
   117  	}
   118  
   119  	workloadInformers := webhookCtx.DynamicResourcesManager.GetDynamicInformers()
   120  	for _, wf := range workloadInformers {
   121  		wp.workloadLister[wf.GVK] = wf.Informer.Lister()
   122  		wp.syncedFunc = append(wp.syncedFunc, wf.Informer.Informer().HasSynced)
   123  	}
   124  
   125  	wp.mutators = append(wp.mutators,
   126  		NewWebhookPodResourceMutator(ctx, wp.vpaIndexer, wp.vpaLister, wp.workloadLister),
   127  		NewWebhookPodSPDReferenceMutator(ctx, wp.spdIndexer, wp.spdLister, wp.workloadLister),
   128  	)
   129  
   130  	cfg := mutating.WebhookConfig{
   131  		Name: "podMutator",
   132  		Obj:  &core.Pod{},
   133  	}
   134  	webhook, err := mutating.NewWebhook(cfg, wp, nil, nil, nil)
   135  	if err != nil {
   136  		return nil, nil, err
   137  	}
   138  
   139  	return webhook, wp.Run, nil
   140  }
   141  
   142  func (wp *WebhookPod) Run() bool {
   143  	if !cache.WaitForCacheSync(wp.ctx.Done(), wp.syncedFunc...) {
   144  		klog.Errorf("unable to sync caches for %s webhook")
   145  		return false
   146  	}
   147  	klog.Infof("Caches are synced for %s webhook", podWebhookName)
   148  
   149  	return true
   150  }
   151  
   152  func (wp *WebhookPod) Mutate(ctx context.Context, obj metav1.Object) (bool, error) {
   153  	klog.V(5).Info("notice an obj to be mutated")
   154  	pod, ok := obj.(*core.Pod)
   155  	if !ok {
   156  		err := fmt.Errorf("failed to convert obj to pod: %v", obj)
   157  		klog.Error(err.Error())
   158  		return false, err
   159  	} else if pod == nil {
   160  		err := fmt.Errorf("pod can't be nil")
   161  		klog.Error(err.Error())
   162  		return false, err
   163  	}
   164  	ar := whcontext.GetAdmissionRequest(ctx)
   165  	if ar == nil {
   166  		err := fmt.Errorf("failed to get admission request from ctx")
   167  		klog.Error(err.Error())
   168  		return false, err
   169  	}
   170  
   171  	klog.V(5).Infof("begin to mutate pod %s", pod.Name)
   172  	for _, mutator := range wp.mutators {
   173  		mutated, err := mutator.MutatePod(pod, ar.Namespace)
   174  		if err != nil {
   175  			klog.Errorf("failed to mutate pod %s: %v", pod.Name, err)
   176  			return false, err
   177  		}
   178  		if !mutated {
   179  			klog.Infof("pod %s mutation isn't allowed", pod.Name)
   180  			return false, nil
   181  		}
   182  	}
   183  
   184  	klog.Infof("pod %s was mutated ", pod.Name)
   185  	return true, nil
   186  }