2016-02-22 16:15:09 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2015 The Kubernetes Authors .
2016-02-22 16:15:09 +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 .
* /
package resourcequota
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/controller"
2016-09-14 18:35:38 +00:00
"k8s.io/kubernetes/pkg/controller/informers"
2016-02-22 16:15:09 +00:00
"k8s.io/kubernetes/pkg/quota/evaluator/core"
"k8s.io/kubernetes/pkg/runtime"
2016-04-13 18:38:32 +00:00
"k8s.io/kubernetes/pkg/util/metrics"
2016-02-22 16:15:09 +00:00
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
"k8s.io/kubernetes/pkg/watch"
)
// ReplenishmentFunc is a function that is invoked when controller sees a change
// that may require a quota to be replenished (i.e. object deletion, or object moved to terminal state)
type ReplenishmentFunc func ( groupKind unversioned . GroupKind , namespace string , object runtime . Object )
// ReplenishmentControllerOptions is an options struct that tells a factory
// how to configure a controller that can inform the quota system it should
// replenish quota
type ReplenishmentControllerOptions struct {
// The kind monitored for replenishment
GroupKind unversioned . GroupKind
// The period that should be used to re-sync the monitored resource
ResyncPeriod controller . ResyncPeriodFunc
// The function to invoke when a change is observed that should trigger
// replenishment
ReplenishmentFunc ReplenishmentFunc
}
// PodReplenishmentUpdateFunc will replenish if the old pod was quota tracked but the new is not
func PodReplenishmentUpdateFunc ( options * ReplenishmentControllerOptions ) func ( oldObj , newObj interface { } ) {
return func ( oldObj , newObj interface { } ) {
oldPod := oldObj . ( * api . Pod )
newPod := newObj . ( * api . Pod )
if core . QuotaPod ( oldPod ) && ! core . QuotaPod ( newPod ) {
2016-07-15 07:38:55 +00:00
options . ReplenishmentFunc ( options . GroupKind , newPod . Namespace , oldPod )
2016-02-22 16:15:09 +00:00
}
}
}
// ObjectReplenenishmentDeleteFunc will replenish on every delete
func ObjectReplenishmentDeleteFunc ( options * ReplenishmentControllerOptions ) func ( obj interface { } ) {
return func ( obj interface { } ) {
metaObject , err := meta . Accessor ( obj )
if err != nil {
tombstone , ok := obj . ( cache . DeletedFinalStateUnknown )
if ! ok {
glog . Errorf ( "replenishment controller could not get object from tombstone %+v, could take up to %v before quota is replenished" , obj , options . ResyncPeriod ( ) )
utilruntime . HandleError ( err )
return
}
metaObject , err = meta . Accessor ( tombstone . Obj )
if err != nil {
glog . Errorf ( "replenishment controller tombstone contained object that is not a meta %+v, could take up to %v before quota is replenished" , tombstone . Obj , options . ResyncPeriod ( ) )
utilruntime . HandleError ( err )
return
}
}
options . ReplenishmentFunc ( options . GroupKind , metaObject . GetNamespace ( ) , nil )
}
}
// ReplenishmentControllerFactory knows how to build replenishment controllers
type ReplenishmentControllerFactory interface {
2016-04-19 14:46:31 +00:00
// NewController returns a controller configured with the specified options.
// This method is NOT thread-safe.
2016-09-14 18:35:38 +00:00
NewController ( options * ReplenishmentControllerOptions ) ( cache . ControllerInterface , error )
2016-02-22 16:15:09 +00:00
}
// replenishmentControllerFactory implements ReplenishmentControllerFactory
type replenishmentControllerFactory struct {
2016-04-19 14:46:31 +00:00
kubeClient clientset . Interface
2016-09-14 18:35:38 +00:00
podInformer cache . SharedInformer
2016-02-22 16:15:09 +00:00
}
// NewReplenishmentControllerFactory returns a factory that knows how to build controllers
// to replenish resources when updated or deleted
2016-09-14 18:35:38 +00:00
func NewReplenishmentControllerFactory ( podInformer cache . SharedInformer , kubeClient clientset . Interface ) ReplenishmentControllerFactory {
2016-02-22 16:15:09 +00:00
return & replenishmentControllerFactory {
2016-04-19 14:46:31 +00:00
kubeClient : kubeClient ,
podInformer : podInformer ,
2016-02-22 16:15:09 +00:00
}
}
2016-04-19 14:46:31 +00:00
func NewReplenishmentControllerFactoryFromClient ( kubeClient clientset . Interface ) ReplenishmentControllerFactory {
return NewReplenishmentControllerFactory ( nil , kubeClient )
}
2016-09-14 18:35:38 +00:00
func ( r * replenishmentControllerFactory ) NewController ( options * ReplenishmentControllerOptions ) ( cache . ControllerInterface , error ) {
var result cache . ControllerInterface
2016-10-13 12:56:07 +00:00
if r . kubeClient != nil && r . kubeClient . Core ( ) . RESTClient ( ) . GetRateLimiter ( ) != nil {
metrics . RegisterMetricAndTrackRateLimiterUsage ( "replenishment_controller" , r . kubeClient . Core ( ) . RESTClient ( ) . GetRateLimiter ( ) )
2016-04-13 18:38:32 +00:00
}
2016-02-22 16:15:09 +00:00
switch options . GroupKind {
case api . Kind ( "Pod" ) :
2016-04-19 14:46:31 +00:00
if r . podInformer != nil {
2016-09-14 18:35:38 +00:00
r . podInformer . AddEventHandler ( cache . ResourceEventHandlerFuncs {
2016-02-22 16:15:09 +00:00
UpdateFunc : PodReplenishmentUpdateFunc ( options ) ,
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
2016-04-19 14:46:31 +00:00
} )
result = r . podInformer . GetController ( )
break
}
2016-08-04 07:02:13 +00:00
r . podInformer = informers . NewPodInformer ( r . kubeClient , options . ResyncPeriod ( ) )
2016-04-19 14:46:31 +00:00
result = r . podInformer
2016-02-22 16:15:09 +00:00
case api . Kind ( "Service" ) :
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . Services ( api . NamespaceAll ) . List ( options )
} ,
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . Services ( api . NamespaceAll ) . Watch ( options )
} ,
} ,
& api . Service { } ,
options . ResyncPeriod ( ) ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-02-18 15:54:24 +00:00
UpdateFunc : ServiceReplenishmentUpdateFunc ( options ) ,
2016-02-22 16:15:09 +00:00
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
} ,
)
case api . Kind ( "ReplicationController" ) :
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . ReplicationControllers ( api . NamespaceAll ) . List ( options )
} ,
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . ReplicationControllers ( api . NamespaceAll ) . Watch ( options )
} ,
} ,
& api . ReplicationController { } ,
options . ResyncPeriod ( ) ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-02-22 16:15:09 +00:00
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
} ,
)
case api . Kind ( "PersistentVolumeClaim" ) :
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . PersistentVolumeClaims ( api . NamespaceAll ) . List ( options )
} ,
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . PersistentVolumeClaims ( api . NamespaceAll ) . Watch ( options )
} ,
} ,
& api . PersistentVolumeClaim { } ,
options . ResyncPeriod ( ) ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-02-22 16:15:09 +00:00
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
} ,
)
case api . Kind ( "Secret" ) :
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . Secrets ( api . NamespaceAll ) . List ( options )
} ,
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . Secrets ( api . NamespaceAll ) . Watch ( options )
} ,
} ,
2016-02-29 22:06:32 +00:00
& api . Secret { } ,
2016-02-22 16:15:09 +00:00
options . ResyncPeriod ( ) ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-02-22 16:15:09 +00:00
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
} ,
)
2016-02-29 17:02:05 +00:00
case api . Kind ( "ConfigMap" ) :
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-29 17:02:05 +00:00
& cache . ListWatch {
ListFunc : func ( options api . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . ConfigMaps ( api . NamespaceAll ) . List ( options )
} ,
WatchFunc : func ( options api . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . ConfigMaps ( api . NamespaceAll ) . Watch ( options )
} ,
} ,
& api . ConfigMap { } ,
options . ResyncPeriod ( ) ,
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-02-29 17:02:05 +00:00
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
} ,
)
2016-02-22 16:15:09 +00:00
default :
2016-07-07 16:14:39 +00:00
return nil , NewUnhandledGroupKindError ( options . GroupKind )
2016-02-22 16:15:09 +00:00
}
return result , nil
}
2016-02-18 15:54:24 +00:00
2016-07-15 07:38:55 +00:00
// ServiceReplenishmentUpdateFunc will replenish if the service was quota tracked has changed service type
2016-02-18 15:54:24 +00:00
func ServiceReplenishmentUpdateFunc ( options * ReplenishmentControllerOptions ) func ( oldObj , newObj interface { } ) {
return func ( oldObj , newObj interface { } ) {
oldService := oldObj . ( * api . Service )
newService := newObj . ( * api . Service )
2016-07-15 07:38:55 +00:00
if core . GetQuotaServiceType ( oldService ) != core . GetQuotaServiceType ( newService ) {
options . ReplenishmentFunc ( options . GroupKind , newService . Namespace , nil )
2016-02-18 15:54:24 +00:00
}
}
}
2016-07-07 16:14:39 +00:00
type unhandledKindErr struct {
kind unversioned . GroupKind
}
func ( e unhandledKindErr ) Error ( ) string {
return fmt . Sprintf ( "no replenishment controller available for %s" , e . kind )
}
func NewUnhandledGroupKindError ( kind unversioned . GroupKind ) error {
return unhandledKindErr { kind : kind }
}
func IsUnhandledGroupKindError ( err error ) bool {
if err == nil {
return false
}
_ , ok := err . ( unhandledKindErr )
return ok
}
// UnionReplenishmentControllerFactory iterates through its constituent factories ignoring, UnhandledGroupKindErrors
// returning the first success or failure it hits. If there are no hits either way, it return an UnhandledGroupKind error
type UnionReplenishmentControllerFactory [ ] ReplenishmentControllerFactory
2016-09-14 18:35:38 +00:00
func ( f UnionReplenishmentControllerFactory ) NewController ( options * ReplenishmentControllerOptions ) ( cache . ControllerInterface , error ) {
2016-07-07 16:14:39 +00:00
for _ , factory := range f {
controller , err := factory . NewController ( options )
if ! IsUnhandledGroupKindError ( err ) {
return controller , err
}
}
return nil , NewUnhandledGroupKindError ( options . GroupKind )
}