2014-08-20 21:34:55 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors.
|
2014-08-20 21:34:55 +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 factory can set up a scheduler. This code is here instead of
|
|
|
|
// plugin/cmd/scheduler for both testability and reuse.
|
|
|
|
package factory
|
|
|
|
|
|
|
|
import (
|
2015-09-23 22:24:49 +00:00
|
|
|
"fmt"
|
2016-05-04 06:50:31 +00:00
|
|
|
"strings"
|
2014-10-03 16:59:39 +00:00
|
|
|
"sync"
|
2015-09-23 22:24:49 +00:00
|
|
|
"sync/atomic"
|
2014-08-20 21:34:55 +00:00
|
|
|
"time"
|
|
|
|
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
|
|
"k8s.io/kubernetes/pkg/api/errors"
|
2015-09-03 21:40:58 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/cache"
|
2016-10-05 08:40:39 +00:00
|
|
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/fields"
|
2015-09-23 22:24:49 +00:00
|
|
|
"k8s.io/kubernetes/pkg/types"
|
2016-01-15 07:32:10 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/runtime"
|
2015-09-09 17:45:01 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/sets"
|
2016-05-04 06:50:31 +00:00
|
|
|
utilvalidation "k8s.io/kubernetes/pkg/util/validation"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/plugin/pkg/scheduler"
|
|
|
|
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
|
2015-11-26 08:57:26 +00:00
|
|
|
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
|
2015-08-05 22:03:47 +00:00
|
|
|
schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
|
|
|
|
"k8s.io/kubernetes/plugin/pkg/scheduler/api/validation"
|
2014-08-20 21:34:55 +00:00
|
|
|
|
|
|
|
"github.com/golang/glog"
|
2016-02-15 16:13:38 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
2016-03-14 04:58:28 +00:00
|
|
|
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
|
2014-08-20 21:34:55 +00:00
|
|
|
)
|
|
|
|
|
2015-11-27 09:07:17 +00:00
|
|
|
const (
|
|
|
|
SchedulerAnnotationKey = "scheduler.alpha.kubernetes.io/name"
|
2016-07-18 09:47:22 +00:00
|
|
|
initialGetBackoff = 100 * time.Millisecond
|
|
|
|
maximalGetBackoff = time.Minute
|
2015-11-27 09:07:17 +00:00
|
|
|
)
|
|
|
|
|
2014-12-01 19:49:13 +00:00
|
|
|
// ConfigFactory knows how to fill out a scheduler config with its support functions.
|
|
|
|
type ConfigFactory struct {
|
2016-10-05 08:40:39 +00:00
|
|
|
Client clientset.Interface
|
2014-11-19 16:16:25 +00:00
|
|
|
// queue for pods that need scheduling
|
|
|
|
PodQueue *cache.FIFO
|
2015-03-13 00:28:00 +00:00
|
|
|
// a means to list all known scheduled pods.
|
|
|
|
ScheduledPodLister *cache.StoreToPodLister
|
|
|
|
// a means to list all known scheduled pods and pods assumed to have been scheduled.
|
|
|
|
PodLister algorithm.PodLister
|
2015-09-10 08:40:22 +00:00
|
|
|
// a means to list all nodes
|
2015-03-12 01:06:26 +00:00
|
|
|
NodeLister *cache.StoreToNodeLister
|
2015-11-29 19:00:49 +00:00
|
|
|
// a means to list all PersistentVolumes
|
|
|
|
PVLister *cache.StoreToPVFetcher
|
|
|
|
// a means to list all PersistentVolumeClaims
|
|
|
|
PVCLister *cache.StoreToPVCFetcher
|
2014-12-12 22:29:20 +00:00
|
|
|
// a means to list all services
|
|
|
|
ServiceLister *cache.StoreToServiceLister
|
2015-07-01 14:26:54 +00:00
|
|
|
// a means to list all controllers
|
|
|
|
ControllerLister *cache.StoreToReplicationControllerLister
|
2016-02-15 16:13:38 +00:00
|
|
|
// a means to list all replicasets
|
|
|
|
ReplicaSetLister *cache.StoreToReplicaSetLister
|
2015-03-13 00:28:00 +00:00
|
|
|
|
2015-04-07 23:44:08 +00:00
|
|
|
// Close this to stop all reflectors
|
|
|
|
StopEverything chan struct{}
|
|
|
|
|
2016-09-14 18:35:38 +00:00
|
|
|
scheduledPodPopulator *cache.Controller
|
|
|
|
nodePopulator *cache.Controller
|
2016-10-11 15:13:35 +00:00
|
|
|
pvPopulator *cache.Controller
|
|
|
|
pvcPopulator *cache.Controller
|
|
|
|
servicePopulator *cache.Controller
|
|
|
|
controllerPopulator *cache.Controller
|
2016-04-21 08:24:12 +00:00
|
|
|
|
|
|
|
schedulerCache schedulercache.Cache
|
2015-11-27 09:07:17 +00:00
|
|
|
|
|
|
|
// SchedulerName of a scheduler is used to select which pods will be
|
|
|
|
// processed by this scheduler, based on pods's annotation key:
|
|
|
|
// 'scheduler.alpha.kubernetes.io/name'
|
|
|
|
SchedulerName string
|
2016-05-04 06:50:31 +00:00
|
|
|
|
|
|
|
// RequiredDuringScheduling affinity is not symmetric, but there is an implicit PreferredDuringScheduling affinity rule
|
|
|
|
// corresponding to every RequiredDuringScheduling affinity rule.
|
|
|
|
// HardPodAffinitySymmetricWeight represents the weight of implicit PreferredDuringScheduling affinity rule, in the range 0-100.
|
|
|
|
HardPodAffinitySymmetricWeight int
|
|
|
|
|
|
|
|
// Indicate the "all topologies" set for empty topologyKey when it's used for PreferredDuringScheduling pod anti-affinity.
|
|
|
|
FailureDomains string
|
2016-10-11 15:13:35 +00:00
|
|
|
|
|
|
|
// Equivalence class cache
|
|
|
|
EquivalencePodCache *scheduler.EquivalenceCache
|
2014-11-19 16:16:25 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:40:15 +00:00
|
|
|
// Initializes the factory.
|
2016-10-05 08:40:39 +00:00
|
|
|
func NewConfigFactory(client clientset.Interface, schedulerName string, hardPodAffinitySymmetricWeight int, failureDomains string) *ConfigFactory {
|
2016-03-14 04:58:28 +00:00
|
|
|
stopEverything := make(chan struct{})
|
|
|
|
schedulerCache := schedulercache.New(30*time.Second, stopEverything)
|
|
|
|
|
2015-03-13 00:28:00 +00:00
|
|
|
c := &ConfigFactory{
|
|
|
|
Client: client,
|
|
|
|
PodQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc),
|
2015-04-07 23:44:08 +00:00
|
|
|
ScheduledPodLister: &cache.StoreToPodLister{},
|
2015-05-02 00:00:37 +00:00
|
|
|
// Only nodes in the "Ready" condition with status == "True" are schedulable
|
2016-05-04 06:50:31 +00:00
|
|
|
NodeLister: &cache.StoreToNodeLister{},
|
|
|
|
PVLister: &cache.StoreToPVFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)},
|
|
|
|
PVCLister: &cache.StoreToPVCFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)},
|
2016-09-16 17:38:50 +00:00
|
|
|
ServiceLister: &cache.StoreToServiceLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})},
|
2016-04-07 12:15:21 +00:00
|
|
|
ControllerLister: &cache.StoreToReplicationControllerLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})},
|
2016-10-04 17:23:27 +00:00
|
|
|
ReplicaSetLister: &cache.StoreToReplicaSetLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})},
|
2016-05-04 06:50:31 +00:00
|
|
|
schedulerCache: schedulerCache,
|
|
|
|
StopEverything: stopEverything,
|
|
|
|
SchedulerName: schedulerName,
|
|
|
|
HardPodAffinitySymmetricWeight: hardPodAffinitySymmetricWeight,
|
|
|
|
FailureDomains: failureDomains,
|
2014-11-19 16:16:25 +00:00
|
|
|
}
|
2016-03-14 04:58:28 +00:00
|
|
|
|
|
|
|
c.PodLister = schedulerCache
|
2015-04-07 23:44:08 +00:00
|
|
|
|
|
|
|
// On add/delete to the scheduled pods, remove from the assumed pods.
|
|
|
|
// We construct this here instead of in CreateFromKeys because
|
|
|
|
// ScheduledPodLister is something we provide to plug in functions that
|
|
|
|
// they may need to call.
|
2016-09-14 18:35:38 +00:00
|
|
|
c.ScheduledPodLister.Indexer, c.scheduledPodPopulator = cache.NewIndexerInformer(
|
2015-12-21 12:33:42 +00:00
|
|
|
c.createAssignedNonTerminatedPodLW(),
|
2015-04-07 23:44:08 +00:00
|
|
|
&api.Pod{},
|
|
|
|
0,
|
2016-09-14 18:35:38 +00:00
|
|
|
cache.ResourceEventHandlerFuncs{
|
2016-04-21 08:24:12 +00:00
|
|
|
AddFunc: c.addPodToCache,
|
|
|
|
UpdateFunc: c.updatePodInCache,
|
|
|
|
DeleteFunc: c.deletePodFromCache,
|
|
|
|
},
|
2016-04-07 12:15:21 +00:00
|
|
|
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
|
2016-04-21 08:24:12 +00:00
|
|
|
)
|
|
|
|
|
2016-09-14 18:35:38 +00:00
|
|
|
c.NodeLister.Store, c.nodePopulator = cache.NewInformer(
|
2016-04-21 08:24:12 +00:00
|
|
|
c.createNodeLW(),
|
|
|
|
&api.Node{},
|
|
|
|
0,
|
2016-09-14 18:35:38 +00:00
|
|
|
cache.ResourceEventHandlerFuncs{
|
2016-04-21 08:24:12 +00:00
|
|
|
AddFunc: c.addNodeToCache,
|
|
|
|
UpdateFunc: c.updateNodeInCache,
|
|
|
|
DeleteFunc: c.deleteNodeFromCache,
|
2015-04-07 23:44:08 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2016-10-11 15:13:35 +00:00
|
|
|
// TODO(harryz) need to fill all the handlers here and below for equivalence cache
|
|
|
|
c.PVLister.Store, c.pvPopulator = cache.NewInformer(
|
|
|
|
c.createPersistentVolumeLW(),
|
|
|
|
&api.PersistentVolume{},
|
|
|
|
0,
|
|
|
|
cache.ResourceEventHandlerFuncs{},
|
|
|
|
)
|
|
|
|
|
|
|
|
c.PVCLister.Store, c.pvcPopulator = cache.NewInformer(
|
|
|
|
c.createPersistentVolumeClaimLW(),
|
|
|
|
&api.PersistentVolumeClaim{},
|
|
|
|
0,
|
|
|
|
cache.ResourceEventHandlerFuncs{},
|
|
|
|
)
|
|
|
|
|
|
|
|
c.ServiceLister.Indexer, c.servicePopulator = cache.NewIndexerInformer(
|
|
|
|
c.createServiceLW(),
|
|
|
|
&api.Service{},
|
|
|
|
0,
|
|
|
|
cache.ResourceEventHandlerFuncs{},
|
|
|
|
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
|
|
|
|
)
|
|
|
|
|
|
|
|
c.ControllerLister.Indexer, c.controllerPopulator = cache.NewIndexerInformer(
|
|
|
|
c.createControllerLW(),
|
|
|
|
&api.ReplicationController{},
|
|
|
|
0,
|
|
|
|
cache.ResourceEventHandlerFuncs{},
|
|
|
|
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
|
|
|
|
)
|
|
|
|
|
2015-03-13 00:28:00 +00:00
|
|
|
return c
|
2014-12-10 04:25:45 +00:00
|
|
|
}
|
2014-11-19 16:16:25 +00:00
|
|
|
|
2016-10-11 15:13:35 +00:00
|
|
|
// TODO(harryz) need to update all the handlers here and below for equivalence cache
|
2016-04-21 08:24:12 +00:00
|
|
|
func (c *ConfigFactory) addPodToCache(obj interface{}) {
|
|
|
|
pod, ok := obj.(*api.Pod)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert to *api.Pod: %v", obj)
|
|
|
|
return
|
|
|
|
}
|
2016-10-11 15:13:35 +00:00
|
|
|
|
2016-04-21 08:24:12 +00:00
|
|
|
if err := c.schedulerCache.AddPod(pod); err != nil {
|
|
|
|
glog.Errorf("scheduler cache AddPod failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigFactory) updatePodInCache(oldObj, newObj interface{}) {
|
|
|
|
oldPod, ok := oldObj.(*api.Pod)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert oldObj to *api.Pod: %v", oldObj)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
newPod, ok := newObj.(*api.Pod)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert newObj to *api.Pod: %v", newObj)
|
|
|
|
return
|
|
|
|
}
|
2016-10-11 15:13:35 +00:00
|
|
|
|
2016-04-21 08:24:12 +00:00
|
|
|
if err := c.schedulerCache.UpdatePod(oldPod, newPod); err != nil {
|
|
|
|
glog.Errorf("scheduler cache UpdatePod failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigFactory) deletePodFromCache(obj interface{}) {
|
|
|
|
var pod *api.Pod
|
|
|
|
switch t := obj.(type) {
|
|
|
|
case *api.Pod:
|
|
|
|
pod = t
|
|
|
|
case cache.DeletedFinalStateUnknown:
|
|
|
|
var ok bool
|
|
|
|
pod, ok = t.Obj.(*api.Pod)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert to *api.Pod: %v", t.Obj)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
glog.Errorf("cannot convert to *api.Pod: %v", t)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := c.schedulerCache.RemovePod(pod); err != nil {
|
|
|
|
glog.Errorf("scheduler cache RemovePod failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigFactory) addNodeToCache(obj interface{}) {
|
|
|
|
node, ok := obj.(*api.Node)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert to *api.Node: %v", obj)
|
|
|
|
return
|
|
|
|
}
|
2016-10-11 15:13:35 +00:00
|
|
|
|
2016-04-21 08:24:12 +00:00
|
|
|
if err := c.schedulerCache.AddNode(node); err != nil {
|
|
|
|
glog.Errorf("scheduler cache AddNode failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigFactory) updateNodeInCache(oldObj, newObj interface{}) {
|
|
|
|
oldNode, ok := oldObj.(*api.Node)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert oldObj to *api.Node: %v", oldObj)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
newNode, ok := newObj.(*api.Node)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert newObj to *api.Node: %v", newObj)
|
|
|
|
return
|
|
|
|
}
|
2016-10-11 15:13:35 +00:00
|
|
|
|
2016-04-21 08:24:12 +00:00
|
|
|
if err := c.schedulerCache.UpdateNode(oldNode, newNode); err != nil {
|
|
|
|
glog.Errorf("scheduler cache UpdateNode failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ConfigFactory) deleteNodeFromCache(obj interface{}) {
|
|
|
|
var node *api.Node
|
|
|
|
switch t := obj.(type) {
|
|
|
|
case *api.Node:
|
|
|
|
node = t
|
|
|
|
case cache.DeletedFinalStateUnknown:
|
|
|
|
var ok bool
|
|
|
|
node, ok = t.Obj.(*api.Node)
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("cannot convert to *api.Node: %v", t.Obj)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
glog.Errorf("cannot convert to *api.Node: %v", t)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := c.schedulerCache.RemoveNode(node); err != nil {
|
|
|
|
glog.Errorf("scheduler cache RemoveNode failed: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-10 04:25:45 +00:00
|
|
|
// Create creates a scheduler with the default algorithm provider.
|
|
|
|
func (f *ConfigFactory) Create() (*scheduler.Config, error) {
|
|
|
|
return f.CreateFromProvider(DefaultProvider)
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:40:15 +00:00
|
|
|
// Creates a scheduler from the name of a registered algorithm provider.
|
2014-12-10 04:25:45 +00:00
|
|
|
func (f *ConfigFactory) CreateFromProvider(providerName string) (*scheduler.Config, error) {
|
2015-09-29 07:04:07 +00:00
|
|
|
glog.V(2).Infof("Creating scheduler from algorithm provider '%v'", providerName)
|
2014-12-10 04:25:45 +00:00
|
|
|
provider, err := GetAlgorithmProvider(providerName)
|
2014-11-19 16:16:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-09-04 06:50:14 +00:00
|
|
|
return f.CreateFromKeys(provider.FitPredicateKeys, provider.PriorityFunctionKeys, []algorithm.SchedulerExtender{})
|
2014-12-10 04:25:45 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:40:15 +00:00
|
|
|
// Creates a scheduler from the configuration file
|
2015-02-20 00:18:28 +00:00
|
|
|
func (f *ConfigFactory) CreateFromConfig(policy schedulerapi.Policy) (*scheduler.Config, error) {
|
2015-09-29 07:04:07 +00:00
|
|
|
glog.V(2).Infof("Creating scheduler from configuration: %v", policy)
|
2015-02-20 00:18:28 +00:00
|
|
|
|
2015-03-31 01:37:19 +00:00
|
|
|
// validate the policy configuration
|
|
|
|
if err := validation.ValidatePolicy(policy); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-09-09 17:45:01 +00:00
|
|
|
predicateKeys := sets.NewString()
|
2015-02-20 00:18:28 +00:00
|
|
|
for _, predicate := range policy.Predicates {
|
|
|
|
glog.V(2).Infof("Registering predicate: %s", predicate.Name)
|
2015-02-27 00:40:15 +00:00
|
|
|
predicateKeys.Insert(RegisterCustomFitPredicate(predicate))
|
2015-02-20 00:18:28 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 17:45:01 +00:00
|
|
|
priorityKeys := sets.NewString()
|
2015-02-20 00:18:28 +00:00
|
|
|
for _, priority := range policy.Priorities {
|
|
|
|
glog.V(2).Infof("Registering priority: %s", priority.Name)
|
|
|
|
priorityKeys.Insert(RegisterCustomPriorityFunction(priority))
|
|
|
|
}
|
|
|
|
|
2015-09-04 06:50:14 +00:00
|
|
|
extenders := make([]algorithm.SchedulerExtender, 0)
|
|
|
|
if len(policy.ExtenderConfigs) != 0 {
|
|
|
|
for ii := range policy.ExtenderConfigs {
|
|
|
|
glog.V(2).Infof("Creating extender with config %+v", policy.ExtenderConfigs[ii])
|
|
|
|
if extender, err := scheduler.NewHTTPExtender(&policy.ExtenderConfigs[ii], policy.APIVersion); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else {
|
|
|
|
extenders = append(extenders, extender)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return f.CreateFromKeys(predicateKeys, priorityKeys, extenders)
|
2015-02-20 00:18:28 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:40:15 +00:00
|
|
|
// Creates a scheduler from a set of registered fit predicate keys and priority keys.
|
2015-09-04 06:50:14 +00:00
|
|
|
func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String, extenders []algorithm.SchedulerExtender) (*scheduler.Config, error) {
|
2014-12-10 04:25:45 +00:00
|
|
|
glog.V(2).Infof("creating scheduler with fit predicates '%v' and priority functions '%v", predicateKeys, priorityKeys)
|
2016-05-04 06:50:31 +00:00
|
|
|
|
|
|
|
if f.HardPodAffinitySymmetricWeight < 0 || f.HardPodAffinitySymmetricWeight > 100 {
|
|
|
|
return nil, fmt.Errorf("invalid hardPodAffinitySymmetricWeight: %d, must be in the range 0-100", f.HardPodAffinitySymmetricWeight)
|
|
|
|
}
|
|
|
|
|
2016-05-23 12:19:46 +00:00
|
|
|
predicateFuncs, err := f.GetPredicates(predicateKeys)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
priorityConfigs, err := f.GetPriorityFunctionConfigs(priorityKeys)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-09-13 09:54:13 +00:00
|
|
|
priorityMetaProducer, err := f.GetPriorityMetadataProducer()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-05-23 12:19:46 +00:00
|
|
|
f.Run()
|
|
|
|
|
2016-09-13 09:54:13 +00:00
|
|
|
algo := scheduler.NewGenericScheduler(f.schedulerCache, predicateFuncs, priorityMetaProducer, priorityConfigs, extenders)
|
2016-05-23 12:19:46 +00:00
|
|
|
|
|
|
|
podBackoff := podBackoff{
|
|
|
|
perPodBackoff: map[types.NamespacedName]*backoffEntry{},
|
|
|
|
clock: realClock{},
|
|
|
|
|
|
|
|
defaultDuration: 1 * time.Second,
|
|
|
|
maxDuration: 60 * time.Second,
|
|
|
|
}
|
|
|
|
|
|
|
|
return &scheduler.Config{
|
|
|
|
SchedulerCache: f.schedulerCache,
|
|
|
|
// The scheduler only needs to consider schedulable nodes.
|
|
|
|
NodeLister: f.NodeLister.NodeCondition(getNodeConditionPredicate()),
|
|
|
|
Algorithm: algo,
|
|
|
|
Binder: &binder{f.Client},
|
|
|
|
PodConditionUpdater: &podConditionUpdater{f.Client},
|
|
|
|
NextPod: func() *api.Pod {
|
|
|
|
return f.getNextPod()
|
|
|
|
},
|
|
|
|
Error: f.makeDefaultErrorFunc(&podBackoff, f.PodQueue),
|
|
|
|
StopEverything: f.StopEverything,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *ConfigFactory) GetPriorityFunctionConfigs(priorityKeys sets.String) ([]algorithm.PriorityConfig, error) {
|
|
|
|
pluginArgs, err := f.getPluginArgs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return getPriorityFunctionConfigs(priorityKeys, *pluginArgs)
|
|
|
|
}
|
|
|
|
|
2016-09-13 09:54:13 +00:00
|
|
|
func (f *ConfigFactory) GetPriorityMetadataProducer() (algorithm.MetadataProducer, error) {
|
|
|
|
pluginArgs, err := f.getPluginArgs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return getPriorityMetadataProducer(*pluginArgs)
|
|
|
|
}
|
|
|
|
|
2016-05-23 12:19:46 +00:00
|
|
|
func (f *ConfigFactory) GetPredicates(predicateKeys sets.String) (map[string]algorithm.FitPredicate, error) {
|
|
|
|
pluginArgs, err := f.getPluginArgs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return getFitPredicateFunctions(predicateKeys, *pluginArgs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *ConfigFactory) getPluginArgs() (*PluginFactoryArgs, error) {
|
2016-05-04 06:50:31 +00:00
|
|
|
failureDomainArgs := strings.Split(f.FailureDomains, ",")
|
|
|
|
for _, failureDomain := range failureDomainArgs {
|
2015-12-16 06:27:13 +00:00
|
|
|
if errs := utilvalidation.IsQualifiedName(failureDomain); len(errs) != 0 {
|
|
|
|
return nil, fmt.Errorf("invalid failure domain: %q: %s", failureDomain, strings.Join(errs, ";"))
|
2016-05-04 06:50:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-23 12:19:46 +00:00
|
|
|
return &PluginFactoryArgs{
|
2015-07-01 14:26:54 +00:00
|
|
|
PodLister: f.PodLister,
|
|
|
|
ServiceLister: f.ServiceLister,
|
|
|
|
ControllerLister: f.ControllerLister,
|
2016-02-15 16:13:38 +00:00
|
|
|
ReplicaSetLister: f.ReplicaSetLister,
|
2015-05-02 00:00:37 +00:00
|
|
|
// All fit predicates only need to consider schedulable nodes.
|
2015-10-22 19:47:43 +00:00
|
|
|
NodeLister: f.NodeLister.NodeCondition(getNodeConditionPredicate()),
|
2016-03-23 23:45:24 +00:00
|
|
|
NodeInfo: &predicates.CachedNodeInfo{StoreToNodeLister: f.NodeLister},
|
2015-11-29 19:00:49 +00:00
|
|
|
PVInfo: f.PVLister,
|
|
|
|
PVCInfo: f.PVCLister,
|
2016-05-04 06:50:31 +00:00
|
|
|
HardPodAffinitySymmetricWeight: f.HardPodAffinitySymmetricWeight,
|
|
|
|
FailureDomains: sets.NewString(failureDomainArgs...).List(),
|
2016-05-23 12:19:46 +00:00
|
|
|
}, nil
|
|
|
|
}
|
2014-11-19 16:16:25 +00:00
|
|
|
|
2016-05-23 12:19:46 +00:00
|
|
|
func (f *ConfigFactory) Run() {
|
2014-08-20 21:34:55 +00:00
|
|
|
// Watch and queue pods that need scheduling.
|
2015-12-21 12:33:42 +00:00
|
|
|
cache.NewReflector(f.createUnassignedNonTerminatedPodLW(), &api.Pod{}, f.PodQueue, 0).RunUntil(f.StopEverything)
|
2015-03-27 22:33:03 +00:00
|
|
|
|
2015-04-07 23:44:08 +00:00
|
|
|
// Begin populating scheduled pods.
|
2015-04-08 00:40:30 +00:00
|
|
|
go f.scheduledPodPopulator.Run(f.StopEverything)
|
2014-08-20 21:34:55 +00:00
|
|
|
|
2016-04-21 08:24:12 +00:00
|
|
|
// Begin populating nodes.
|
|
|
|
go f.nodePopulator.Run(f.StopEverything)
|
2014-08-20 21:34:55 +00:00
|
|
|
|
2016-10-11 15:13:35 +00:00
|
|
|
// Begin populating pv & pvc
|
|
|
|
go f.pvPopulator.Run(f.StopEverything)
|
|
|
|
go f.pvcPopulator.Run(f.StopEverything)
|
2015-11-29 19:00:49 +00:00
|
|
|
|
2016-10-11 15:13:35 +00:00
|
|
|
// Begin populating services
|
|
|
|
go f.servicePopulator.Run(f.StopEverything)
|
2014-12-12 22:29:20 +00:00
|
|
|
|
2016-10-11 15:13:35 +00:00
|
|
|
// Begin populating controllers
|
|
|
|
go f.controllerPopulator.Run(f.StopEverything)
|
2015-07-01 14:26:54 +00:00
|
|
|
|
2016-02-15 16:13:38 +00:00
|
|
|
// Watch and cache all ReplicaSet objects. Scheduler needs to find all pods
|
|
|
|
// created by the same services or ReplicationControllers/ReplicaSets, so that it can spread them correctly.
|
|
|
|
// Cache this locally.
|
2016-10-04 17:23:27 +00:00
|
|
|
cache.NewReflector(f.createReplicaSetLW(), &extensions.ReplicaSet{}, f.ReplicaSetLister.Indexer, 0).RunUntil(f.StopEverything)
|
2014-11-19 16:16:25 +00:00
|
|
|
}
|
|
|
|
|
2015-11-27 09:07:17 +00:00
|
|
|
func (f *ConfigFactory) getNextPod() *api.Pod {
|
|
|
|
for {
|
2016-06-14 07:32:57 +00:00
|
|
|
pod := cache.Pop(f.PodQueue).(*api.Pod)
|
2015-11-27 09:07:17 +00:00
|
|
|
if f.responsibleForPod(pod) {
|
|
|
|
glog.V(4).Infof("About to try and schedule pod %v", pod.Name)
|
|
|
|
return pod
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *ConfigFactory) responsibleForPod(pod *api.Pod) bool {
|
|
|
|
if f.SchedulerName == api.DefaultSchedulerName {
|
|
|
|
return pod.Annotations[SchedulerAnnotationKey] == f.SchedulerName || pod.Annotations[SchedulerAnnotationKey] == ""
|
|
|
|
} else {
|
|
|
|
return pod.Annotations[SchedulerAnnotationKey] == f.SchedulerName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-22 19:47:43 +00:00
|
|
|
func getNodeConditionPredicate() cache.NodeConditionPredicate {
|
2016-07-07 11:06:32 +00:00
|
|
|
return func(node *api.Node) bool {
|
2016-07-11 08:32:29 +00:00
|
|
|
for i := range node.Status.Conditions {
|
|
|
|
cond := &node.Status.Conditions[i]
|
2016-05-27 09:37:20 +00:00
|
|
|
// We consider the node for scheduling only when its:
|
|
|
|
// - NodeReady condition status is ConditionTrue,
|
|
|
|
// - NodeOutOfDisk condition status is ConditionFalse,
|
|
|
|
// - NodeNetworkUnavailable condition status is ConditionFalse.
|
2015-10-22 19:47:43 +00:00
|
|
|
if cond.Type == api.NodeReady && cond.Status != api.ConditionTrue {
|
|
|
|
glog.V(4).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
|
|
|
return false
|
|
|
|
} else if cond.Type == api.NodeOutOfDisk && cond.Status != api.ConditionFalse {
|
|
|
|
glog.V(4).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
|
|
|
return false
|
2016-05-27 09:37:20 +00:00
|
|
|
} else if cond.Type == api.NodeNetworkUnavailable && cond.Status != api.ConditionFalse {
|
|
|
|
glog.V(4).Infof("Ignoring node %v with %v condition status %v", node.Name, cond.Type, cond.Status)
|
|
|
|
return false
|
2015-10-22 19:47:43 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-03 21:35:50 +00:00
|
|
|
// Ignore nodes that are marked unschedulable
|
|
|
|
if node.Spec.Unschedulable {
|
|
|
|
glog.V(4).Infof("Ignoring node %v since it is unschedulable", node.Name)
|
|
|
|
return false
|
|
|
|
}
|
2015-10-22 19:47:43 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-27 00:40:15 +00:00
|
|
|
// Returns a cache.ListWatch that finds all pods that need to be
|
2014-09-16 21:08:57 +00:00
|
|
|
// scheduled.
|
2015-12-21 12:33:42 +00:00
|
|
|
func (factory *ConfigFactory) createUnassignedNonTerminatedPodLW() *cache.ListWatch {
|
|
|
|
selector := fields.ParseSelectorOrDie("spec.nodeName==" + "" + ",status.phase!=" + string(api.PodSucceeded) + ",status.phase!=" + string(api.PodFailed))
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Core().GetRESTClient(), "pods", api.NamespaceAll, selector)
|
2014-09-16 21:08:57 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:40:15 +00:00
|
|
|
// Returns a cache.ListWatch that finds all pods that are
|
2014-09-16 21:08:57 +00:00
|
|
|
// already scheduled.
|
2015-01-07 21:12:29 +00:00
|
|
|
// TODO: return a ListerWatcher interface instead?
|
2015-12-21 12:33:42 +00:00
|
|
|
func (factory *ConfigFactory) createAssignedNonTerminatedPodLW() *cache.ListWatch {
|
|
|
|
selector := fields.ParseSelectorOrDie("spec.nodeName!=" + "" + ",status.phase!=" + string(api.PodSucceeded) + ",status.phase!=" + string(api.PodFailed))
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Core().GetRESTClient(), "pods", api.NamespaceAll, selector)
|
2014-09-16 21:08:57 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 08:40:22 +00:00
|
|
|
// createNodeLW returns a cache.ListWatch that gets all changes to nodes.
|
|
|
|
func (factory *ConfigFactory) createNodeLW() *cache.ListWatch {
|
2016-06-03 21:35:50 +00:00
|
|
|
// all nodes are considered to ensure that the scheduler cache has access to all nodes for lookups
|
|
|
|
// the NodeCondition is used to filter out the nodes that are not ready or unschedulable
|
|
|
|
// the filtered list is used as the super set of nodes to consider for scheduling
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Core().GetRESTClient(), "nodes", api.NamespaceAll, fields.ParseSelectorOrDie(""))
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
|
|
|
|
2015-11-29 19:00:49 +00:00
|
|
|
// createPersistentVolumeLW returns a cache.ListWatch that gets all changes to persistentVolumes.
|
|
|
|
func (factory *ConfigFactory) createPersistentVolumeLW() *cache.ListWatch {
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Core().GetRESTClient(), "persistentVolumes", api.NamespaceAll, fields.ParseSelectorOrDie(""))
|
2015-11-29 19:00:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// createPersistentVolumeClaimLW returns a cache.ListWatch that gets all changes to persistentVolumeClaims.
|
|
|
|
func (factory *ConfigFactory) createPersistentVolumeClaimLW() *cache.ListWatch {
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Core().GetRESTClient(), "persistentVolumeClaims", api.NamespaceAll, fields.ParseSelectorOrDie(""))
|
2015-11-29 19:00:49 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:40:15 +00:00
|
|
|
// Returns a cache.ListWatch that gets all changes to services.
|
2015-01-08 02:18:21 +00:00
|
|
|
func (factory *ConfigFactory) createServiceLW() *cache.ListWatch {
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Core().GetRESTClient(), "services", api.NamespaceAll, fields.ParseSelectorOrDie(""))
|
2014-12-12 22:29:20 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 14:26:54 +00:00
|
|
|
// Returns a cache.ListWatch that gets all changes to controllers.
|
|
|
|
func (factory *ConfigFactory) createControllerLW() *cache.ListWatch {
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Core().GetRESTClient(), "replicationControllers", api.NamespaceAll, fields.ParseSelectorOrDie(""))
|
2015-07-01 14:26:54 +00:00
|
|
|
}
|
|
|
|
|
2016-02-15 16:13:38 +00:00
|
|
|
// Returns a cache.ListWatch that gets all changes to replicasets.
|
|
|
|
func (factory *ConfigFactory) createReplicaSetLW() *cache.ListWatch {
|
2016-10-05 08:40:39 +00:00
|
|
|
return cache.NewListWatchFromClient(factory.Client.Extensions().GetRESTClient(), "replicasets", api.NamespaceAll, fields.ParseSelectorOrDie(""))
|
2016-02-15 16:13:38 +00:00
|
|
|
}
|
|
|
|
|
2014-12-01 19:49:13 +00:00
|
|
|
func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue *cache.FIFO) func(pod *api.Pod, err error) {
|
2014-08-20 22:03:32 +00:00
|
|
|
return func(pod *api.Pod, err error) {
|
2015-05-16 23:46:50 +00:00
|
|
|
if err == scheduler.ErrNoNodesAvailable {
|
|
|
|
glog.V(4).Infof("Unable to schedule %v %v: no nodes are registered to the cluster; waiting", pod.Namespace, pod.Name)
|
|
|
|
} else {
|
|
|
|
glog.Errorf("Error scheduling %v %v: %v; retrying", pod.Namespace, pod.Name, err)
|
|
|
|
}
|
2014-10-03 16:59:39 +00:00
|
|
|
backoff.gc()
|
2014-08-20 22:03:32 +00:00
|
|
|
// Retry asynchronously.
|
|
|
|
// Note that this is extremely rudimentary and we need a more real error handling path.
|
|
|
|
go func() {
|
2016-01-15 07:32:10 +00:00
|
|
|
defer runtime.HandleCrash()
|
2015-09-23 22:24:49 +00:00
|
|
|
podID := types.NamespacedName{
|
|
|
|
Namespace: pod.Namespace,
|
|
|
|
Name: pod.Name,
|
|
|
|
}
|
|
|
|
|
|
|
|
entry := backoff.getEntry(podID)
|
|
|
|
if !entry.TryWait(backoff.maxDuration) {
|
|
|
|
glog.Warningf("Request for pod %v already in flight, abandoning", podID)
|
|
|
|
return
|
|
|
|
}
|
2014-08-20 22:03:32 +00:00
|
|
|
// Get the pod again; it may have changed/been scheduled already.
|
2016-07-18 09:47:22 +00:00
|
|
|
getBackoff := initialGetBackoff
|
|
|
|
for {
|
2016-10-05 08:40:39 +00:00
|
|
|
pod, err := factory.Client.Core().Pods(podID.Namespace).Get(podID.Name)
|
2016-09-14 04:33:28 +00:00
|
|
|
if err == nil {
|
|
|
|
if len(pod.Spec.NodeName) == 0 {
|
|
|
|
podQueue.AddIfNotPresent(pod)
|
|
|
|
}
|
2016-07-18 09:47:22 +00:00
|
|
|
break
|
2015-05-16 23:46:50 +00:00
|
|
|
}
|
2016-07-18 09:47:22 +00:00
|
|
|
if errors.IsNotFound(err) {
|
2016-07-19 16:10:50 +00:00
|
|
|
glog.Warningf("A pod %v no longer exists", podID)
|
2016-07-18 09:47:22 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
glog.Errorf("Error getting pod %v for retry: %v; retrying...", podID, err)
|
|
|
|
if getBackoff = getBackoff * 2; getBackoff > maximalGetBackoff {
|
|
|
|
getBackoff = maximalGetBackoff
|
|
|
|
}
|
|
|
|
time.Sleep(getBackoff)
|
2014-08-20 22:03:32 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
|
|
|
|
2015-03-02 23:00:09 +00:00
|
|
|
// nodeEnumerator allows a cache.Poller to enumerate items in an api.NodeList
|
2014-12-08 03:44:27 +00:00
|
|
|
type nodeEnumerator struct {
|
|
|
|
*api.NodeList
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 03:44:27 +00:00
|
|
|
// Len returns the number of items in the node list.
|
|
|
|
func (ne *nodeEnumerator) Len() int {
|
|
|
|
if ne.NodeList == nil {
|
2014-08-20 21:34:55 +00:00
|
|
|
return 0
|
|
|
|
}
|
2014-12-08 03:44:27 +00:00
|
|
|
return len(ne.Items)
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// Get returns the item (and ID) with the particular index.
|
2015-01-26 21:44:53 +00:00
|
|
|
func (ne *nodeEnumerator) Get(index int) interface{} {
|
|
|
|
return &ne.Items[index]
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type binder struct {
|
2016-10-05 08:40:39 +00:00
|
|
|
Client clientset.Interface
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bind just does a POST binding RPC.
|
|
|
|
func (b *binder) Bind(binding *api.Binding) error {
|
2016-04-20 11:35:06 +00:00
|
|
|
glog.V(3).Infof("Attempting to bind %v to %v", binding.Name, binding.Target.Name)
|
2014-10-03 15:44:06 +00:00
|
|
|
ctx := api.WithNamespace(api.NewContext(), binding.Namespace)
|
2016-10-05 08:40:39 +00:00
|
|
|
return b.Client.Core().GetRESTClient().Post().Namespace(api.NamespaceValue(ctx)).Resource("bindings").Body(binding).Do().Error()
|
2015-03-04 20:55:41 +00:00
|
|
|
// TODO: use Pods interface for binding once clusters are upgraded
|
|
|
|
// return b.Pods(binding.Namespace).Bind(binding)
|
2014-08-20 21:34:55 +00:00
|
|
|
}
|
2014-10-03 16:59:39 +00:00
|
|
|
|
2016-04-18 12:26:16 +00:00
|
|
|
type podConditionUpdater struct {
|
2016-10-05 08:40:39 +00:00
|
|
|
Client clientset.Interface
|
2016-04-18 12:26:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *podConditionUpdater) Update(pod *api.Pod, condition *api.PodCondition) error {
|
|
|
|
glog.V(2).Infof("Updating pod condition for %s/%s to (%s==%s)", pod.Namespace, pod.Name, condition.Type, condition.Status)
|
|
|
|
if api.UpdatePodCondition(&pod.Status, condition) {
|
2016-10-05 08:40:39 +00:00
|
|
|
_, err := p.Client.Core().Pods(pod.Namespace).UpdateStatus(pod)
|
2016-04-18 12:26:16 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-10-03 16:59:39 +00:00
|
|
|
type clock interface {
|
|
|
|
Now() time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
type realClock struct{}
|
|
|
|
|
|
|
|
func (realClock) Now() time.Time {
|
|
|
|
return time.Now()
|
|
|
|
}
|
|
|
|
|
2015-09-23 22:24:49 +00:00
|
|
|
// backoffEntry is single threaded. in particular, it only allows a single action to be waiting on backoff at a time.
|
|
|
|
// It is expected that all users will only use the public TryWait(...) method
|
|
|
|
// It is also not safe to copy this object.
|
2014-10-03 16:59:39 +00:00
|
|
|
type backoffEntry struct {
|
2015-09-23 22:24:49 +00:00
|
|
|
backoff time.Duration
|
|
|
|
lastUpdate time.Time
|
|
|
|
reqInFlight int32
|
|
|
|
}
|
|
|
|
|
|
|
|
// tryLock attempts to acquire a lock via atomic compare and swap.
|
|
|
|
// returns true if the lock was acquired, false otherwise
|
|
|
|
func (b *backoffEntry) tryLock() bool {
|
|
|
|
return atomic.CompareAndSwapInt32(&b.reqInFlight, 0, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// unlock returns the lock. panics if the lock isn't held
|
|
|
|
func (b *backoffEntry) unlock() {
|
|
|
|
if !atomic.CompareAndSwapInt32(&b.reqInFlight, 1, 0) {
|
2016-03-05 09:53:06 +00:00
|
|
|
panic(fmt.Sprintf("unexpected state on unlocking: %+v", b))
|
2015-09-23 22:24:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TryWait tries to acquire the backoff lock, maxDuration is the maximum allowed period to wait for.
|
|
|
|
func (b *backoffEntry) TryWait(maxDuration time.Duration) bool {
|
|
|
|
if !b.tryLock() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
defer b.unlock()
|
|
|
|
b.wait(maxDuration)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (entry *backoffEntry) getBackoff(maxDuration time.Duration) time.Duration {
|
|
|
|
duration := entry.backoff
|
|
|
|
newDuration := time.Duration(duration) * 2
|
|
|
|
if newDuration > maxDuration {
|
|
|
|
newDuration = maxDuration
|
|
|
|
}
|
|
|
|
entry.backoff = newDuration
|
2016-03-05 09:53:06 +00:00
|
|
|
glog.V(4).Infof("Backing off %s for pod %+v", duration.String(), entry)
|
2015-09-23 22:24:49 +00:00
|
|
|
return duration
|
|
|
|
}
|
|
|
|
|
|
|
|
func (entry *backoffEntry) wait(maxDuration time.Duration) {
|
|
|
|
time.Sleep(entry.getBackoff(maxDuration))
|
2014-10-03 16:59:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type podBackoff struct {
|
2015-09-23 22:24:49 +00:00
|
|
|
perPodBackoff map[types.NamespacedName]*backoffEntry
|
2015-01-09 18:03:49 +00:00
|
|
|
lock sync.Mutex
|
|
|
|
clock clock
|
|
|
|
defaultDuration time.Duration
|
|
|
|
maxDuration time.Duration
|
2014-10-03 16:59:39 +00:00
|
|
|
}
|
|
|
|
|
2015-09-23 22:24:49 +00:00
|
|
|
func (p *podBackoff) getEntry(podID types.NamespacedName) *backoffEntry {
|
2014-10-03 16:59:39 +00:00
|
|
|
p.lock.Lock()
|
|
|
|
defer p.lock.Unlock()
|
|
|
|
entry, ok := p.perPodBackoff[podID]
|
|
|
|
if !ok {
|
2015-01-09 18:03:49 +00:00
|
|
|
entry = &backoffEntry{backoff: p.defaultDuration}
|
2014-10-03 16:59:39 +00:00
|
|
|
p.perPodBackoff[podID] = entry
|
|
|
|
}
|
|
|
|
entry.lastUpdate = p.clock.Now()
|
|
|
|
return entry
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *podBackoff) gc() {
|
|
|
|
p.lock.Lock()
|
|
|
|
defer p.lock.Unlock()
|
|
|
|
now := p.clock.Now()
|
|
|
|
for podID, entry := range p.perPodBackoff {
|
2015-01-09 18:03:49 +00:00
|
|
|
if now.Sub(entry.lastUpdate) > p.maxDuration {
|
2014-10-03 16:59:39 +00:00
|
|
|
delete(p.perPodBackoff, podID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|