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 }