...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision/admission.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision

     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  }
   130  

View as plain text