k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/admission/namespace/autoprovision/admission.go (about) 1 /* 2 Copyright 2014 The Kubernetes 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 autoprovision 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 24 corev1 "k8s.io/api/core/v1" 25 "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apiserver/pkg/admission" 28 genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" 29 "k8s.io/client-go/informers" 30 "k8s.io/client-go/kubernetes" 31 corev1listers "k8s.io/client-go/listers/core/v1" 32 api "k8s.io/kubernetes/pkg/apis/core" 33 ) 34 35 // PluginName indicates name of admission plugin. 36 const PluginName = "NamespaceAutoProvision" 37 38 // Register registers a plugin 39 func Register(plugins *admission.Plugins) { 40 plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { 41 return NewProvision(), nil 42 }) 43 } 44 45 // Provision is an implementation of admission.Interface. 46 // It looks at all incoming requests in a namespace context, and if the namespace does not exist, it creates one. 47 // It is useful in deployments that do not want to restrict creation of a namespace prior to its usage. 48 type Provision struct { 49 *admission.Handler 50 client kubernetes.Interface 51 namespaceLister corev1listers.NamespaceLister 52 } 53 54 var _ admission.MutationInterface = &Provision{} 55 var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Provision{}) 56 var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Provision{}) 57 58 // Admit makes an admission decision based on the request attributes 59 func (p *Provision) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error { 60 // Don't create a namespace if the request is for a dry-run. 61 if a.IsDryRun() { 62 return nil 63 } 64 65 // if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do 66 // if we're here, then the API server has found a route, which means that if we have a non-empty namespace 67 // its a namespaced resource. 68 if len(a.GetNamespace()) == 0 || a.GetKind().GroupKind() == api.Kind("Namespace") { 69 return nil 70 } 71 // we need to wait for our caches to warm 72 if !p.WaitForReady() { 73 return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) 74 } 75 76 _, err := p.namespaceLister.Get(a.GetNamespace()) 77 if err == nil { 78 return nil 79 } 80 81 if !errors.IsNotFound(err) { 82 return admission.NewForbidden(a, err) 83 } 84 85 namespace := &corev1.Namespace{ 86 ObjectMeta: metav1.ObjectMeta{ 87 Name: a.GetNamespace(), 88 Namespace: "", 89 }, 90 Status: corev1.NamespaceStatus{}, 91 } 92 93 _, err = p.client.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) 94 if err != nil && !errors.IsAlreadyExists(err) { 95 return admission.NewForbidden(a, err) 96 } 97 98 return nil 99 } 100 101 // NewProvision creates a new namespace provision admission control handler 102 func NewProvision() *Provision { 103 return &Provision{ 104 Handler: admission.NewHandler(admission.Create), 105 } 106 } 107 108 // SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface. 109 func (p *Provision) SetExternalKubeClientSet(client kubernetes.Interface) { 110 p.client = client 111 } 112 113 // SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface. 114 func (p *Provision) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { 115 namespaceInformer := f.Core().V1().Namespaces() 116 p.namespaceLister = namespaceInformer.Lister() 117 p.SetReadyFunc(namespaceInformer.Informer().HasSynced) 118 } 119 120 // ValidateInitialization implements the InitializationValidator interface. 121 func (p *Provision) ValidateInitialization() error { 122 if p.namespaceLister == nil { 123 return fmt.Errorf("missing namespaceLister") 124 } 125 if p.client == nil { 126 return fmt.Errorf("missing client") 127 } 128 return nil 129 }