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"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/watch"
2016-02-22 16:15:09 +00:00
"k8s.io/kubernetes/pkg/api"
2016-11-18 20:50:17 +00:00
"k8s.io/kubernetes/pkg/api/v1"
2016-02-22 16:15:09 +00:00
"k8s.io/kubernetes/pkg/client/cache"
2017-01-06 06:34:29 +00:00
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
2016-02-22 16:15:09 +00:00
"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"
2016-04-13 18:38:32 +00:00
"k8s.io/kubernetes/pkg/util/metrics"
2016-02-22 16:15:09 +00:00
)
// 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)
2016-11-21 02:55:31 +00:00
type ReplenishmentFunc func ( groupKind schema . GroupKind , namespace string , object runtime . Object )
2016-02-22 16:15:09 +00:00
// 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
2016-11-21 02:55:31 +00:00
GroupKind schema . GroupKind
2016-02-22 16:15:09 +00:00
// 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 { } ) {
2016-11-18 20:50:17 +00:00
oldPod := oldObj . ( * v1 . Pod )
newPod := newObj . ( * v1 . Pod )
if core . QuotaV1Pod ( oldPod ) && ! core . QuotaV1Pod ( 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.
2017-01-12 13:45:53 +00:00
NewController ( options * ReplenishmentControllerOptions ) ( cache . Controller , error )
2016-02-22 16:15:09 +00:00
}
// replenishmentControllerFactory implements ReplenishmentControllerFactory
type replenishmentControllerFactory struct {
2016-10-14 17:44:20 +00:00
kubeClient clientset . Interface
sharedInformerFactory informers . SharedInformerFactory
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-10-14 17:44:20 +00:00
func NewReplenishmentControllerFactory ( f informers . SharedInformerFactory , kubeClient clientset . Interface ) ReplenishmentControllerFactory {
2016-02-22 16:15:09 +00:00
return & replenishmentControllerFactory {
2016-10-14 17:44:20 +00:00
kubeClient : kubeClient ,
sharedInformerFactory : f ,
2016-02-22 16:15:09 +00:00
}
}
2016-10-14 17:44:20 +00:00
// NewReplenishmentControllerFactoryFromClient returns a factory that knows how to build controllers to replenish resources
// when updated or deleted using the specified client.
2016-04-19 14:46:31 +00:00
func NewReplenishmentControllerFactoryFromClient ( kubeClient clientset . Interface ) ReplenishmentControllerFactory {
return NewReplenishmentControllerFactory ( nil , kubeClient )
}
2016-11-06 23:45:36 +00:00
// controllerFor returns a replenishment controller for the specified group resource.
func controllerFor (
2016-11-21 02:55:31 +00:00
groupResource schema . GroupResource ,
2016-11-06 23:45:36 +00:00
f informers . SharedInformerFactory ,
2017-01-12 13:45:53 +00:00
handlerFuncs cache . ResourceEventHandlerFuncs ,
) ( cache . Controller , error ) {
2016-11-06 23:45:36 +00:00
genericInformer , err := f . ForResource ( groupResource )
if err != nil {
return nil , err
}
informer := genericInformer . Informer ( )
informer . AddEventHandler ( handlerFuncs )
return informer . GetController ( ) , nil
}
2017-01-12 13:45:53 +00:00
func ( r * replenishmentControllerFactory ) NewController ( options * ReplenishmentControllerOptions ) ( result cache . Controller , err error ) {
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-10-14 17:44:20 +00:00
if r . sharedInformerFactory != nil {
2016-11-06 23:45:36 +00:00
result , err = controllerFor ( api . Resource ( "pods" ) , r . sharedInformerFactory , cache . ResourceEventHandlerFuncs {
2016-02-22 16:15:09 +00:00
UpdateFunc : PodReplenishmentUpdateFunc ( options ) ,
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
2016-04-19 14:46:31 +00:00
} )
break
}
2016-10-14 17:44:20 +00:00
result = informers . NewPodInformer ( r . kubeClient , options . ResyncPeriod ( ) )
2016-02-22 16:15:09 +00:00
case api . Kind ( "Service" ) :
2016-11-06 23:45:36 +00:00
// TODO move to informer when defined
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
2016-11-18 20:50:17 +00:00
ListFunc : func ( options v1 . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . Services ( v1 . NamespaceAll ) . List ( options )
2016-02-22 16:15:09 +00:00
} ,
2016-11-18 20:50:17 +00:00
WatchFunc : func ( options v1 . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . Services ( v1 . NamespaceAll ) . Watch ( options )
2016-02-22 16:15:09 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
& v1 . Service { } ,
2016-02-22 16:15:09 +00:00
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-11-06 23:45:36 +00:00
// TODO move to informer when defined
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
2016-11-18 20:50:17 +00:00
ListFunc : func ( options v1 . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . ReplicationControllers ( v1 . NamespaceAll ) . List ( options )
2016-02-22 16:15:09 +00:00
} ,
2016-11-18 20:50:17 +00:00
WatchFunc : func ( options v1 . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . ReplicationControllers ( v1 . NamespaceAll ) . Watch ( options )
2016-02-22 16:15:09 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
& v1 . ReplicationController { } ,
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 ) ,
} ,
)
case api . Kind ( "PersistentVolumeClaim" ) :
2016-11-06 23:45:36 +00:00
if r . sharedInformerFactory != nil {
result , err = controllerFor ( api . Resource ( "persistentvolumeclaims" ) , r . sharedInformerFactory , cache . ResourceEventHandlerFuncs {
DeleteFunc : ObjectReplenishmentDeleteFunc ( options ) ,
} )
break
}
// TODO (derekwaynecarr) remove me when we can require a sharedInformerFactory in all code paths...
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
2016-11-18 20:50:17 +00:00
ListFunc : func ( options v1 . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . PersistentVolumeClaims ( v1 . NamespaceAll ) . List ( options )
2016-02-22 16:15:09 +00:00
} ,
2016-11-18 20:50:17 +00:00
WatchFunc : func ( options v1 . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . PersistentVolumeClaims ( v1 . NamespaceAll ) . Watch ( options )
2016-02-22 16:15:09 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
& v1 . PersistentVolumeClaim { } ,
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 ) ,
} ,
)
case api . Kind ( "Secret" ) :
2016-11-06 23:45:36 +00:00
// TODO move to informer when defined
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-22 16:15:09 +00:00
& cache . ListWatch {
2016-11-18 20:50:17 +00:00
ListFunc : func ( options v1 . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . Secrets ( v1 . NamespaceAll ) . List ( options )
2016-02-22 16:15:09 +00:00
} ,
2016-11-18 20:50:17 +00:00
WatchFunc : func ( options v1 . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . Secrets ( v1 . NamespaceAll ) . Watch ( options )
2016-02-22 16:15:09 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
& v1 . 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-11-06 23:45:36 +00:00
// TODO move to informer when defined
2016-09-14 18:35:38 +00:00
_ , result = cache . NewInformer (
2016-02-29 17:02:05 +00:00
& cache . ListWatch {
2016-11-18 20:50:17 +00:00
ListFunc : func ( options v1 . ListOptions ) ( runtime . Object , error ) {
return r . kubeClient . Core ( ) . ConfigMaps ( v1 . NamespaceAll ) . List ( options )
2016-02-29 17:02:05 +00:00
} ,
2016-11-18 20:50:17 +00:00
WatchFunc : func ( options v1 . ListOptions ) ( watch . Interface , error ) {
return r . kubeClient . Core ( ) . ConfigMaps ( v1 . NamespaceAll ) . Watch ( options )
2016-02-29 17:02:05 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
& v1 . ConfigMap { } ,
2016-02-29 17:02:05 +00:00
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
}
2016-11-06 23:45:36 +00:00
return result , err
2016-02-22 16:15:09 +00:00
}
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 { } ) {
2016-11-18 20:50:17 +00:00
oldService := oldObj . ( * v1 . Service )
newService := newObj . ( * v1 . 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 {
2016-11-21 02:55:31 +00:00
kind schema . GroupKind
2016-07-07 16:14:39 +00:00
}
func ( e unhandledKindErr ) Error ( ) string {
return fmt . Sprintf ( "no replenishment controller available for %s" , e . kind )
}
2016-11-21 02:55:31 +00:00
func NewUnhandledGroupKindError ( kind schema . GroupKind ) error {
2016-07-07 16:14:39 +00:00
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
2017-01-12 13:45:53 +00:00
func ( f UnionReplenishmentControllerFactory ) NewController ( options * ReplenishmentControllerOptions ) ( cache . Controller , 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 )
}