2015-03-20 16:49:03 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
2015-03-20 16:49:03 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2015-10-10 03:58:57 +00:00
|
|
|
package namespace
|
2015-03-20 16:49:03 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
2015-09-17 22:21:55 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
2015-09-03 21:40:58 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/cache"
|
2016-02-05 21:58:03 +00:00
|
|
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
2016-03-03 04:34:18 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/typed/dynamic"
|
2016-02-03 19:43:21 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller/framework"
|
|
|
|
"k8s.io/kubernetes/pkg/runtime"
|
2016-04-13 18:38:32 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/metrics"
|
2016-01-15 07:32:10 +00:00
|
|
|
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
|
2016-02-03 19:43:21 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/wait"
|
|
|
|
"k8s.io/kubernetes/pkg/util/workqueue"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/watch"
|
2015-06-04 02:59:07 +00:00
|
|
|
|
|
|
|
"github.com/golang/glog"
|
2015-03-20 16:49:03 +00:00
|
|
|
)
|
|
|
|
|
2015-07-31 11:38:04 +00:00
|
|
|
// NamespaceController is responsible for performing actions dependent upon a namespace phase
|
|
|
|
type NamespaceController struct {
|
2016-02-03 19:43:21 +00:00
|
|
|
// client that purges namespace content, must have list/delete privileges on all content
|
|
|
|
kubeClient clientset.Interface
|
2016-03-03 04:34:18 +00:00
|
|
|
// clientPool manages a pool of dynamic clients
|
|
|
|
clientPool dynamic.ClientPool
|
2016-02-03 19:43:21 +00:00
|
|
|
// store that holds the namespaces
|
|
|
|
store cache.Store
|
|
|
|
// controller that observes the namespaces
|
|
|
|
controller *framework.Controller
|
|
|
|
// namespaces that have been queued up for processing by workers
|
|
|
|
queue *workqueue.Type
|
2016-03-03 04:34:18 +00:00
|
|
|
// list of preferred group versions and their corresponding resource set for namespace deletion
|
|
|
|
groupVersionResources []unversioned.GroupVersionResource
|
|
|
|
// opCache is a cache to remember if a particular operation is not supported to aid dynamic client.
|
|
|
|
opCache operationNotSupportedCache
|
|
|
|
// finalizerToken is the finalizer token managed by this controller
|
|
|
|
finalizerToken api.FinalizerName
|
2015-03-20 16:49:03 +00:00
|
|
|
}
|
|
|
|
|
2015-07-31 11:38:04 +00:00
|
|
|
// NewNamespaceController creates a new NamespaceController
|
2016-03-03 04:34:18 +00:00
|
|
|
func NewNamespaceController(
|
|
|
|
kubeClient clientset.Interface,
|
|
|
|
clientPool dynamic.ClientPool,
|
|
|
|
groupVersionResources []unversioned.GroupVersionResource,
|
|
|
|
resyncPeriod time.Duration,
|
|
|
|
finalizerToken api.FinalizerName) *NamespaceController {
|
2016-02-03 19:43:21 +00:00
|
|
|
// create the controller so we can inject the enqueue function
|
|
|
|
namespaceController := &NamespaceController{
|
|
|
|
kubeClient: kubeClient,
|
2016-03-03 04:34:18 +00:00
|
|
|
clientPool: clientPool,
|
2016-02-03 19:43:21 +00:00
|
|
|
queue: workqueue.New(),
|
2016-03-03 04:34:18 +00:00
|
|
|
groupVersionResources: groupVersionResources,
|
|
|
|
opCache: operationNotSupportedCache{},
|
|
|
|
finalizerToken: finalizerToken,
|
2016-02-03 19:43:21 +00:00
|
|
|
}
|
|
|
|
|
2016-04-13 18:38:32 +00:00
|
|
|
if kubeClient != nil && kubeClient.Core().GetRESTClient().GetRateLimiter() != nil {
|
|
|
|
metrics.RegisterMetricAndTrackRateLimiterUsage("namespace_controller", kubeClient.Core().GetRESTClient().GetRateLimiter())
|
|
|
|
}
|
|
|
|
|
2016-02-03 19:43:21 +00:00
|
|
|
// configure the backing store/controller
|
|
|
|
store, controller := framework.NewInformer(
|
2015-03-20 16:49:03 +00:00
|
|
|
&cache.ListWatch{
|
2015-12-10 09:39:03 +00:00
|
|
|
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
|
2016-02-03 21:21:05 +00:00
|
|
|
return kubeClient.Core().Namespaces().List(options)
|
2015-03-20 16:49:03 +00:00
|
|
|
},
|
2015-12-10 09:39:03 +00:00
|
|
|
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
|
2016-02-03 21:21:05 +00:00
|
|
|
return kubeClient.Core().Namespaces().Watch(options)
|
2015-03-20 16:49:03 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
&api.Namespace{},
|
2015-04-13 17:15:27 +00:00
|
|
|
resyncPeriod,
|
|
|
|
framework.ResourceEventHandlerFuncs{
|
|
|
|
AddFunc: func(obj interface{}) {
|
|
|
|
namespace := obj.(*api.Namespace)
|
2016-02-03 19:43:21 +00:00
|
|
|
namespaceController.enqueueNamespace(namespace)
|
2015-04-13 17:15:27 +00:00
|
|
|
},
|
|
|
|
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
|
|
namespace := newObj.(*api.Namespace)
|
2016-02-03 19:43:21 +00:00
|
|
|
namespaceController.enqueueNamespace(namespace)
|
2015-04-13 17:15:27 +00:00
|
|
|
},
|
|
|
|
},
|
2015-03-20 16:49:03 +00:00
|
|
|
)
|
|
|
|
|
2016-02-03 19:43:21 +00:00
|
|
|
namespaceController.store = store
|
|
|
|
namespaceController.controller = controller
|
|
|
|
return namespaceController
|
2015-03-20 16:49:03 +00:00
|
|
|
}
|
|
|
|
|
2016-02-03 19:43:21 +00:00
|
|
|
// enqueueNamespace adds an object to the controller work queue
|
|
|
|
// obj could be an *api.Namespace, or a DeletionFinalStateUnknown item.
|
|
|
|
func (nm *NamespaceController) enqueueNamespace(obj interface{}) {
|
|
|
|
key, err := controller.KeyFunc(obj)
|
2015-11-04 22:47:28 +00:00
|
|
|
if err != nil {
|
2016-02-03 19:43:21 +00:00
|
|
|
glog.Errorf("Couldn't get key for object %+v: %v", obj, err)
|
|
|
|
return
|
2015-11-04 22:47:28 +00:00
|
|
|
}
|
2016-02-03 19:43:21 +00:00
|
|
|
nm.queue.Add(key)
|
2015-06-04 18:40:56 +00:00
|
|
|
}
|
|
|
|
|
2016-02-03 19:43:21 +00:00
|
|
|
// worker processes the queue of namespace objects.
|
|
|
|
// Each namespace can be in the queue at most once.
|
|
|
|
// The system ensures that no two workers can process
|
|
|
|
// the same namespace at the same time.
|
|
|
|
func (nm *NamespaceController) worker() {
|
|
|
|
for {
|
|
|
|
func() {
|
|
|
|
key, quit := nm.queue.Get()
|
|
|
|
if quit {
|
|
|
|
return
|
2015-10-12 21:23:50 +00:00
|
|
|
}
|
2016-02-03 19:43:21 +00:00
|
|
|
defer nm.queue.Done(key)
|
|
|
|
if err := nm.syncNamespaceFromKey(key.(string)); err != nil {
|
|
|
|
if estimate, ok := err.(*contentRemainingError); ok {
|
|
|
|
go func() {
|
2016-02-26 18:06:27 +00:00
|
|
|
defer utilruntime.HandleCrash()
|
2016-02-03 19:43:21 +00:00
|
|
|
t := estimate.Estimate/2 + 1
|
|
|
|
glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", key, t)
|
|
|
|
time.Sleep(time.Duration(t) * time.Second)
|
|
|
|
nm.queue.Add(key)
|
|
|
|
}()
|
2016-02-26 18:06:27 +00:00
|
|
|
} else {
|
|
|
|
// rather than wait for a full resync, re-add the namespace to the queue to be processed
|
|
|
|
nm.queue.Add(key)
|
|
|
|
utilruntime.HandleError(err)
|
2016-02-03 19:43:21 +00:00
|
|
|
}
|
2016-01-19 00:06:57 +00:00
|
|
|
}
|
2016-02-03 19:43:21 +00:00
|
|
|
}()
|
2015-09-03 14:50:53 +00:00
|
|
|
}
|
2015-03-20 16:49:03 +00:00
|
|
|
}
|
|
|
|
|
2016-02-03 19:43:21 +00:00
|
|
|
// syncNamespaceFromKey looks for a namespace with the specified key in its store and synchronizes it
|
|
|
|
func (nm *NamespaceController) syncNamespaceFromKey(key string) (err error) {
|
|
|
|
startTime := time.Now()
|
|
|
|
defer glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Now().Sub(startTime))
|
2015-09-21 20:06:25 +00:00
|
|
|
|
2016-02-03 19:43:21 +00:00
|
|
|
obj, exists, err := nm.store.GetByKey(key)
|
|
|
|
if !exists {
|
|
|
|
glog.Infof("Namespace has been deleted %v", key)
|
2015-03-20 16:49:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
2015-11-04 22:47:28 +00:00
|
|
|
if err != nil {
|
2016-02-03 19:43:21 +00:00
|
|
|
glog.Infof("Unable to retrieve namespace %v from store: %v", key, err)
|
|
|
|
nm.queue.Add(key)
|
2015-11-04 22:47:28 +00:00
|
|
|
return err
|
|
|
|
}
|
2016-02-03 19:43:21 +00:00
|
|
|
namespace := obj.(*api.Namespace)
|
2016-03-03 04:34:18 +00:00
|
|
|
return syncNamespace(nm.kubeClient, nm.clientPool, nm.opCache, nm.groupVersionResources, namespace, nm.finalizerToken)
|
2015-03-20 16:49:03 +00:00
|
|
|
}
|
|
|
|
|
2016-02-03 19:43:21 +00:00
|
|
|
// Run starts observing the system with the specified number of workers.
|
|
|
|
func (nm *NamespaceController) Run(workers int, stopCh <-chan struct{}) {
|
|
|
|
defer utilruntime.HandleCrash()
|
|
|
|
go nm.controller.Run(stopCh)
|
|
|
|
for i := 0; i < workers; i++ {
|
|
|
|
go wait.Until(nm.worker, time.Second, stopCh)
|
2015-10-12 21:23:50 +00:00
|
|
|
}
|
2016-02-03 19:43:21 +00:00
|
|
|
<-stopCh
|
|
|
|
glog.Infof("Shutting down NamespaceController")
|
|
|
|
nm.queue.ShutDown()
|
2015-10-12 21:23:50 +00:00
|
|
|
}
|