// Initializes the factory. func NewConfigFactory(client *client.Client, rateLimiter util.RateLimiter) *ConfigFactory { c := &ConfigFactory{ Client: client, PodQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc), NodeQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc), ScheduledPodLister: &cache.StoreToPodLister{}, // Only nodes in the "Ready" condition with status == "True" are schedulable NodeLister: &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ServiceLister: &cache.StoreToServiceLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ControllerLister: &cache.StoreToReplicationControllerLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, StopEverything: make(chan struct{}), } modeler := scheduler.NewSimpleModeler(&cache.StoreToPodLister{Store: c.PodQueue}, c.ScheduledPodLister) c.modeler = modeler c.PodLister = modeler.PodLister() c.BindPodsRateLimiter = rateLimiter // 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. c.ScheduledPodLister.Store, c.scheduledPodPopulator = framework.NewInformer( c.createAssignedPodLW(), &api.Pod{}, 0, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { if pod, ok := obj.(*api.Pod); ok { c.modeler.LockedAction(func() { c.modeler.ForgetPod(pod) }) } }, DeleteFunc: func(obj interface{}) { c.modeler.LockedAction(func() { switch t := obj.(type) { case *api.Pod: c.modeler.ForgetPod(t) case cache.DeletedFinalStateUnknown: c.modeler.ForgetPodByKey(t.Key) } }) }, }, ) return c }
// Create creates a new ImageChangeController which is used to trigger builds when a new // image is available func (factory *ImageChangeControllerFactory) Create() controller.RunnableController { queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(&imageStreamLW{factory.Client}, &imageapi.ImageStream{}, queue, 2*time.Minute).RunUntil(factory.Stop) store := cache.NewStore(cache.MetaNamespaceKeyFunc) cache.NewReflector(&buildConfigLW{client: factory.Client}, &buildapi.BuildConfig{}, store, 2*time.Minute).RunUntil(factory.Stop) imageChangeController := &buildcontroller.ImageChangeController{ BuildConfigStore: store, BuildConfigInstantiator: factory.BuildConfigInstantiator, } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, retryFunc("ImageStream update", func(err error) bool { _, isFatal := err.(buildcontroller.ImageChangeControllerFatalError) return isFatal }), kutil.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { imageRepo := obj.(*imageapi.ImageStream) return imageChangeController.HandleImageRepo(imageRepo) }, } }
// Create constructs a BuildPodController func (factory *BuildPodControllerFactory) Create() controller.RunnableController { factory.buildStore = cache.NewStore(cache.MetaNamespaceKeyFunc) cache.NewReflector(&buildLW{client: factory.OSClient}, &buildapi.Build{}, factory.buildStore, 2*time.Minute).RunUntil(factory.Stop) queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(&podLW{client: factory.KubeClient}, &kapi.Pod{}, queue, 2*time.Minute).RunUntil(factory.Stop) client := ControllerClient{factory.KubeClient, factory.OSClient} buildPodController := &buildcontroller.BuildPodController{ BuildStore: factory.buildStore, BuildUpdater: factory.BuildUpdater, PodManager: client, } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, retryFunc("BuildPod", nil), kutil.NewTokenBucketRateLimiter(1, 10)), Handle: func(obj interface{}) error { pod := obj.(*kapi.Pod) return buildPodController.HandlePod(pod) }, } }
// Create constructs a BuildController func (factory *BuildControllerFactory) Create() controller.RunnableController { queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(&buildLW{client: factory.OSClient}, &buildapi.Build{}, queue, 2*time.Minute).Run() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) client := ControllerClient{factory.KubeClient, factory.OSClient} buildController := &buildcontroller.BuildController{ BuildUpdater: factory.BuildUpdater, ImageStreamClient: client, PodManager: client, BuildStrategy: &typeBasedFactoryStrategy{ DockerBuildStrategy: factory.DockerBuildStrategy, SourceBuildStrategy: factory.SourceBuildStrategy, CustomBuildStrategy: factory.CustomBuildStrategy, }, Recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "build-controller"}), } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, limitedLogAndRetry(factory.BuildUpdater, 30*time.Minute), kutil.NewTokenBucketRateLimiter(1, 10)), Handle: func(obj interface{}) error { build := obj.(*buildapi.Build) return buildController.HandleBuild(build) }, } }
// Create creates a new ConfigChangeController which is used to trigger builds on creation func (factory *BuildConfigControllerFactory) Create() controller.RunnableController { queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(&buildConfigLW{client: factory.Client}, &buildapi.BuildConfig{}, queue, 2*time.Minute).RunUntil(factory.Stop) eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) bcController := &buildcontroller.BuildConfigController{ BuildConfigInstantiator: factory.BuildConfigInstantiator, Recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "build-config-controller"}), } return &controller.RetryController{ Queue: controller.NewQueueWrapper(queue), RetryManager: controller.NewQueueRetryManager( controller.NewQueueWrapper(queue), cache.MetaNamespaceKeyFunc, retryFunc("BuildConfig", buildcontroller.IsFatal), flowcontrol.NewTokenBucketRateLimiter(1, 10)), Handle: func(obj interface{}) error { bc := obj.(*buildapi.BuildConfig) return bcController.HandleBuildConfig(bc) }, } }
// Create creates an ImportController. func (f *ImportControllerFactory) Create() controller.RunnableController { lw := &cache.ListWatch{ ListFunc: func() (runtime.Object, error) { return f.Client.ImageStreams(kapi.NamespaceAll).List(labels.Everything(), fields.Everything()) }, WatchFunc: func(resourceVersion string) (watch.Interface, error) { return f.Client.ImageStreams(kapi.NamespaceAll).Watch(labels.Everything(), fields.Everything(), resourceVersion) }, } q := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(lw, &api.ImageStream{}, q, 2*time.Minute).Run() c := &ImportController{ streams: f.Client, mappings: f.Client, } return &controller.RetryController{ Queue: q, RetryManager: controller.NewQueueRetryManager( q, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { util.HandleError(err) return retries.Count < 5 }, kutil.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { r := obj.(*api.ImageStream) return c.Next(r) }, } }
func NewTurboScheduler(kubeClient *client.Client, vmturboMeta *vmtmeta.VMTMeta) *TurboScheduler { scheduledPodLister := &cache.StoreToPodLister{} podQueue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) modeler := scheduler.NewSimpleModeler(&cache.StoreToPodLister{Store: podQueue}, scheduledPodLister) bindPodsQPS := float32(15.0) bindPodsBurst := 20 rateLimiter := util.NewTokenBucketRateLimiter(bindPodsQPS, bindPodsBurst) config := &Config{ Modeler: modeler, Binder: &binder{kubeClient}, BindPodsRateLimiter: rateLimiter, } eventBroadcaster := record.NewBroadcaster() config.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: "turboscheduler"}) eventBroadcaster.StartLogging(glog.Infof) eventBroadcaster.StartRecordingToSink(kubeClient.Events("")) vmtSched := vmtscheduler.NewVMTScheduler(kubeClient, vmturboMeta) glog.V(3).Infof("VMTScheduler is set: %++v", vmtSched) defaultSched := defaultscheduler.NewDefaultScheduler(kubeClient) glog.V(3).Infof("DefaultScheduler is set: %++v", defaultSched) return &TurboScheduler{ config: config, vmtScheduler: vmtSched, defaultScheduler: defaultSched, } }
func TestSchedulerNoPhantomPodAfterDelete(t *testing.T) { stop := make(chan struct{}) defer close(stop) queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(10*time.Minute, stop) firstPod := podWithPort("pod.Name", "", 8080) node := api.Node{ObjectMeta: api.ObjectMeta{Name: "machine1"}} scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*api.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts} scheduler, bindingChan, errChan := setupTestSchedulerWithOnePodOnNode(t, queuedPodStore, scache, nodeLister, predicateMap, firstPod, &node) // We use conflicted pod ports to incur fit predicate failure. secondPod := podWithPort("bar", "", 8080) queuedPodStore.Add(secondPod) // queuedPodStore: [bar:8080] // cache: [(assumed)foo:8080] scheduler.scheduleOne() select { case err := <-errChan: expectErr := &FitError{ Pod: secondPod, FailedPredicates: FailedPredicateMap{node.Name: []algorithm.PredicateFailureReason{predicates.ErrPodNotFitsHostPorts}}, } if !reflect.DeepEqual(expectErr, err) { t.Errorf("err want=%v, get=%v", expectErr, err) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } // We mimic the workflow of cache behavior when a pod is removed by user. // Note: if the schedulercache timeout would be super short, the first pod would expire // and would be removed itself (without any explicit actions on schedulercache). Even in that case, // explicitly AddPod will as well correct the behavior. firstPod.Spec.NodeName = node.Name if err := scache.AddPod(firstPod); err != nil { t.Fatalf("err: %v", err) } if err := scache.RemovePod(firstPod); err != nil { t.Fatalf("err: %v", err) } queuedPodStore.Add(secondPod) scheduler.scheduleOne() select { case b := <-bindingChan: expectBinding := &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "bar"}, Target: api.ObjectReference{Kind: "Node", Name: node.Name}, } if !reflect.DeepEqual(expectBinding, b) { t.Errorf("binding want=%v, get=%v", expectBinding, b) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } }
// Create creates an ImageChangeController. func (factory *ImageChangeControllerFactory) Create() controller.RunnableController { imageStreamLW := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return factory.Client.ImageStreams(kapi.NamespaceAll).List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return factory.Client.ImageStreams(kapi.NamespaceAll).Watch(options) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(imageStreamLW, &imageapi.ImageStream{}, queue, 2*time.Minute).Run() deploymentConfigLW := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).Watch(options) }, } store := cache.NewStore(cache.MetaNamespaceKeyFunc) cache.NewReflector(deploymentConfigLW, &deployapi.DeploymentConfig{}, store, 2*time.Minute).Run() changeController := &ImageChangeController{ listDeploymentConfigs: func() ([]*deployapi.DeploymentConfig, error) { configs := []*deployapi.DeploymentConfig{} objs := store.List() for _, obj := range objs { configs = append(configs, obj.(*deployapi.DeploymentConfig)) } return configs, nil }, client: factory.Client, } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { utilruntime.HandleError(err) if _, isFatal := err.(fatalError); isFatal { return false } if retries.Count > 0 { return false } return true }, flowcontrol.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { repo := obj.(*imageapi.ImageStream) return changeController.Handle(repo) }, } }
func TestSchedulerNoPhantomPodAfterExpire(t *testing.T) { stop := make(chan struct{}) defer close(stop) queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(100*time.Millisecond, stop) pod := podWithPort("pod.Name", "", 8080) node := api.Node{ObjectMeta: api.ObjectMeta{Name: "machine1"}} scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*api.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts} scheduler, bindingChan, _ := setupTestSchedulerWithOnePodOnNode(t, queuedPodStore, scache, nodeLister, predicateMap, pod, &node) waitPodExpireChan := make(chan struct{}) timeout := make(chan struct{}) go func() { for { select { case <-timeout: return default: } pods, err := scache.List(labels.Everything()) if err != nil { t.Fatalf("cache.List failed: %v", err) } if len(pods) == 0 { close(waitPodExpireChan) return } time.Sleep(100 * time.Millisecond) } }() // waiting for the assumed pod to expire select { case <-waitPodExpireChan: case <-time.After(wait.ForeverTestTimeout): close(timeout) t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } // We use conflicted pod ports to incur fit predicate failure if first pod not removed. secondPod := podWithPort("bar", "", 8080) queuedPodStore.Add(secondPod) scheduler.scheduleOne() select { case b := <-bindingChan: expectBinding := &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "bar"}, Target: api.ObjectReference{Kind: "Node", Name: node.Name}, } if !reflect.DeepEqual(expectBinding, b) { t.Errorf("binding want=%v, get=%v", expectBinding, b) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } }
// Scheduler should preserve predicate constraint even if binding was longer // than cache ttl func TestSchedulerErrorWithLongBinding(t *testing.T) { stop := make(chan struct{}) defer close(stop) firstPod := podWithPort("foo", "", 8080) conflictPod := podWithPort("bar", "", 8080) pods := map[string]*v1.Pod{firstPod.Name: firstPod, conflictPod.Name: conflictPod} for _, test := range []struct { Expected map[string]bool CacheTTL time.Duration BindingDuration time.Duration }{ { Expected: map[string]bool{firstPod.Name: true}, CacheTTL: 100 * time.Millisecond, BindingDuration: 300 * time.Millisecond, }, { Expected: map[string]bool{firstPod.Name: true}, CacheTTL: 10 * time.Second, BindingDuration: 300 * time.Millisecond, }, } { queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(test.CacheTTL, stop) node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1"}} scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*v1.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts} scheduler, bindingChan := setupTestSchedulerLongBindingWithRetry( queuedPodStore, scache, nodeLister, predicateMap, stop, test.BindingDuration) scheduler.Run() queuedPodStore.Add(firstPod) queuedPodStore.Add(conflictPod) resultBindings := map[string]bool{} waitChan := time.After(5 * time.Second) for finished := false; !finished; { select { case b := <-bindingChan: resultBindings[b.Name] = true p := pods[b.Name] p.Spec.NodeName = b.Target.Name scache.AddPod(p) case <-waitChan: finished = true } } if !reflect.DeepEqual(resultBindings, test.Expected) { t.Errorf("Result binding are not equal to expected. %v != %v", resultBindings, test.Expected) } } }
// Create creates a DeploymentConfigChangeController. func (factory *DeploymentConfigChangeControllerFactory) Create() controller.RunnableController { deploymentConfigLW := &deployutil.ListWatcherImpl{ ListFunc: func() (runtime.Object, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).List(labels.Everything(), fields.Everything()) }, WatchFunc: func(resourceVersion string) (watch.Interface, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).Watch(labels.Everything(), fields.Everything(), resourceVersion) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(deploymentConfigLW, &deployapi.DeploymentConfig{}, queue, 2*time.Minute).Run() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) changeController := &DeploymentConfigChangeController{ changeStrategy: &changeStrategyImpl{ getDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) { return factory.KubeClient.ReplicationControllers(namespace).Get(name) }, generateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { return factory.Client.DeploymentConfigs(namespace).Generate(name) }, updateDeploymentConfigFunc: func(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) { return factory.Client.DeploymentConfigs(namespace).Update(config) }, }, decodeConfig: func(deployment *kapi.ReplicationController) (*deployapi.DeploymentConfig, error) { return deployutil.DecodeDeploymentConfig(deployment, factory.Codec) }, recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "deployer"}), } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { kutil.HandleError(err) if _, isFatal := err.(fatalError); isFatal { return false } if retries.Count > 0 { return false } return true }, kutil.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { config := obj.(*deployapi.DeploymentConfig) return changeController.Handle(config) }, } }
func TestSchedulerFailedSchedulingReasons(t *testing.T) { stop := make(chan struct{}) defer close(stop) queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(10*time.Minute, stop) node := api.Node{ ObjectMeta: api.ObjectMeta{Name: "machine1"}, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(2, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(100, resource.DecimalSI)), api.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)), }, Allocatable: api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(2, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(100, resource.DecimalSI)), api.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)), }}, } scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*api.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{ "PodFitsResources": predicates.PodFitsResources, } scheduler, _, errChan := setupTestScheduler(queuedPodStore, scache, nodeLister, predicateMap) podWithTooBigResourceRequests := podWithResources("bar", "", api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(4, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(500, resource.DecimalSI)), }, api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(4, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(500, resource.DecimalSI)), }) queuedPodStore.Add(podWithTooBigResourceRequests) scheduler.scheduleOne() select { case err := <-errChan: expectErr := &FitError{ Pod: podWithTooBigResourceRequests, FailedPredicates: FailedPredicateMap{node.Name: []algorithm.PredicateFailureReason{ predicates.NewInsufficientResourceError(api.ResourceCPU, 4000, 0, 2000), predicates.NewInsufficientResourceError(api.ResourceMemory, 500, 0, 100), }}, } if !reflect.DeepEqual(expectErr, err) { t.Errorf("err want=%+v, get=%+v", expectErr, err) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } }
// Initializes the factory. func NewConfigFactory(client *client.Client, schedulerName string, hardPodAffinitySymmetricWeight int, failureDomains string) *ConfigFactory { stopEverything := make(chan struct{}) schedulerCache := schedulercache.New(30*time.Second, stopEverything) c := &ConfigFactory{ Client: client, PodQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc), ScheduledPodLister: &cache.StoreToPodLister{}, // Only nodes in the "Ready" condition with status == "True" are schedulable NodeLister: &cache.StoreToNodeLister{}, PVLister: &cache.StoreToPVFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, PVCLister: &cache.StoreToPVCFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ServiceLister: &cache.StoreToServiceLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ControllerLister: &cache.StoreToReplicationControllerLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})}, ReplicaSetLister: &cache.StoreToReplicaSetLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, schedulerCache: schedulerCache, StopEverything: stopEverything, SchedulerName: schedulerName, HardPodAffinitySymmetricWeight: hardPodAffinitySymmetricWeight, FailureDomains: failureDomains, } c.PodLister = schedulerCache // 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. c.ScheduledPodLister.Indexer, c.scheduledPodPopulator = framework.NewIndexerInformer( c.createAssignedNonTerminatedPodLW(), &api.Pod{}, 0, framework.ResourceEventHandlerFuncs{ AddFunc: c.addPodToCache, UpdateFunc: c.updatePodInCache, DeleteFunc: c.deletePodFromCache, }, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, ) c.NodeLister.Store, c.nodePopulator = framework.NewInformer( c.createNodeLW(), &api.Node{}, 0, framework.ResourceEventHandlerFuncs{ AddFunc: c.addNodeToCache, UpdateFunc: c.updateNodeInCache, DeleteFunc: c.deleteNodeFromCache, }, ) return c }
// NewRateLimitedFunction creates a new rate limited function. func NewRateLimitedFunction(keyFunc kcache.KeyFunc, interval int, handlerFunc HandlerFunc) *RateLimitedFunction { fifo := kcache.NewFIFO(keyFunc) qps := float32(1000.0) // Call rate per second (SLA). if interval > 0 { qps = float32(1.0 / float32(interval)) } limiter := kutil.NewTokenBucketRateLimiter(qps, 1) return &RateLimitedFunction{handlerFunc, fifo, limiter} }
// Create creates a DeploymentConfigController. func (factory *DeploymentConfigControllerFactory) Create() controller.RunnableController { deploymentConfigLW := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).Watch(options) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(deploymentConfigLW, &deployapi.DeploymentConfig{}, queue, 2*time.Minute).Run() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) recorder := eventBroadcaster.NewRecorder(kapi.EventSource{Component: "deploymentconfig-controller"}) configController := NewDeploymentConfigController(factory.KubeClient, factory.Client, factory.Codec, recorder) return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { config := obj.(*deployapi.DeploymentConfig) // no retries for a fatal error if _, isFatal := err.(fatalError); isFatal { glog.V(4).Infof("Will not retry fatal error for deploymentConfig %s/%s: %v", config.Namespace, config.Name, err) kutil.HandleError(err) return false } // infinite retries for a transient error if _, isTransient := err.(transientError); isTransient { glog.V(4).Infof("Retrying deploymentConfig %s/%s with error: %v", config.Namespace, config.Name, err) return true } kutil.HandleError(err) // no retries for anything else if retries.Count > 0 { return false } return true }, kutil.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { config := obj.(*deployapi.DeploymentConfig) return configController.Handle(config) }, } }
// Create constructs a BuildController func (factory *BuildControllerFactory) Create() controller.RunnableController { queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(&buildLW{client: factory.OSClient}, &buildapi.Build{}, queue, 2*time.Minute).RunUntil(factory.Stop) eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) client := ControllerClient{factory.KubeClient, factory.OSClient} buildController := &buildcontroller.BuildController{ BuildUpdater: factory.BuildUpdater, BuildLister: factory.BuildLister, ImageStreamClient: client, PodManager: client, RunPolicies: policy.GetAllRunPolicies(factory.BuildLister, factory.BuildUpdater), BuildStrategy: &typeBasedFactoryStrategy{ DockerBuildStrategy: factory.DockerBuildStrategy, SourceBuildStrategy: factory.SourceBuildStrategy, CustomBuildStrategy: factory.CustomBuildStrategy, }, Recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "build-controller"}), } return &controller.RetryController{ Queue: controller.NewQueueWrapper(queue), RetryManager: controller.NewQueueRetryManager( controller.NewQueueWrapper(queue), cache.MetaNamespaceKeyFunc, limitedLogAndRetry(factory.BuildUpdater, 30*time.Minute), flowcontrol.NewTokenBucketRateLimiter(1, 10)), Handle: func(obj interface{}) error { build := obj.(*buildapi.Build) err := buildController.HandleBuild(build) if err != nil { // Update the build status message only if it changed. if msg := errors.ErrorToSentence(err); build.Status.Message != msg { // Set default Reason. if len(build.Status.Reason) == 0 { build.Status.Reason = buildapi.StatusReasonError } build.Status.Message = msg if err := buildController.BuildUpdater.Update(build.Namespace, build); err != nil { glog.V(2).Infof("Failed to update status message of Build %s/%s: %v", build.Namespace, build.Name, err) } buildController.Recorder.Eventf(build, kapi.EventTypeWarning, "HandleBuildError", "Build has error: %v", err) } } return err }, } }
// Create creates a BackingServiceInstanceControllerFactory. func (factory *BackingServiceInstanceControllerFactory) Create() controller.RunnableController { backingserviceinstanceLW := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return factory.Client.BackingServiceInstances(kapi.NamespaceAll).List(options) //return factory.KubeClient.Namespaces().List(labels.Everything(), fields.Everything()) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return factory.Client.BackingServiceInstances(kapi.NamespaceAll).Watch(options) //return factory.KubeClient.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(backingserviceinstanceLW, &backingserviceinstanceapi.BackingServiceInstance{}, queue, 1*time.Minute).Run() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) backingserviceInstanceController := &BackingServiceInstanceController{ Client: factory.Client, KubeClient: factory.KubeClient, recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "bsi"}), } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { utilruntime.HandleError(err) if _, isFatal := err.(fatalError); isFatal { return false } if retries.Count > 0 { return false } return true }, kutil.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { backingserviceinstance := obj.(*backingserviceinstanceapi.BackingServiceInstance) return backingserviceInstanceController.Handle(backingserviceinstance) }, } }
func TestDefaultErrorFunc(t *testing.T) { grace := int64(30) testPod := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSClusterFirst, TerminationGracePeriodSeconds: &grace, }, } handler := util.FakeHandler{ StatusCode: 200, ResponseBody: runtime.EncodeOrDie(latest.Codec, testPod), T: t, } mux := http.NewServeMux() // FakeHandler musn't be sent requests other than the one you want to test. mux.Handle(testapi.Default.ResourcePath("pods", "bar", "foo"), &handler) server := httptest.NewServer(mux) defer server.Close() factory := NewConfigFactory(client.NewOrDie(&client.Config{Host: server.URL, Version: testapi.Default.Version()}), nil) queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) podBackoff := podBackoff{ perPodBackoff: map[string]*backoffEntry{}, clock: &fakeClock{}, defaultDuration: 1 * time.Millisecond, maxDuration: 1 * time.Second, } errFunc := factory.makeDefaultErrorFunc(&podBackoff, queue) errFunc(testPod, nil) for { // This is a terrible way to do this but I plan on replacing this // whole error handling system in the future. The test will time // out if something doesn't work. time.Sleep(10 * time.Millisecond) got, exists, _ := queue.Get(testPod) if !exists { continue } handler.ValidateRequest(t, testapi.Default.ResourcePath("pods", "bar", "foo"), "GET", nil) if e, a := testPod, got; !reflect.DeepEqual(e, a) { t.Errorf("Expected %v, got %v", e, a) } break } }
func TestSchedulerRateLimitsBinding(t *testing.T) { scheduledPodStore := cache.NewStore(cache.MetaNamespaceKeyFunc) scheduledPodLister := &cache.StoreToPodLister{Store: scheduledPodStore} queuedPodStore := cache.NewFIFO(cache.MetaNamespaceKeyFunc) queuedPodLister := &cache.StoreToPodLister{Store: queuedPodStore} modeler := NewSimpleModeler(queuedPodLister, scheduledPodLister) algo := NewGenericScheduler( map[string]algorithm.FitPredicate{}, []algorithm.PriorityConfig{}, []algorithm.SchedulerExtender{}, modeler.PodLister(), rand.New(rand.NewSource(time.Now().UnixNano()))) // Rate limit to 1 pod fr := FakeRateLimiter{util.NewTokenBucketRateLimiter(0.02, 1), []bool{}} c := &Config{ Modeler: modeler, NodeLister: algorithm.FakeNodeLister( api.NodeList{Items: []api.Node{{ObjectMeta: api.ObjectMeta{Name: "machine1"}}}}, ), Algorithm: algo, Binder: fakeBinder{func(b *api.Binding) error { return nil }}, NextPod: func() *api.Pod { return queuedPodStore.Pop().(*api.Pod) }, Error: func(p *api.Pod, err error) { t.Errorf("Unexpected error when scheduling pod %+v: %v", p, err) }, Recorder: &record.FakeRecorder{}, BindPodsRateLimiter: &fr, } s := New(c) firstPod := podWithID("foo", "") secondPod := podWithID("boo", "") queuedPodStore.Add(firstPod) queuedPodStore.Add(secondPod) for i, hitRateLimit := range []bool{true, false} { s.scheduleOne() if fr.acceptValues[i] != hitRateLimit { t.Errorf("Unexpected rate limiting, expect rate limit to be: %v but found it was %v", hitRateLimit, fr.acceptValues[i]) } } }
func CreateRegistry(c RegistryConfig) Registry { metrics.Register() return &offerStorage{ RegistryConfig: c, offers: cache.NewFIFO(cache.KeyFunc(func(v interface{}) (string, error) { if perishable, ok := v.(Perishable); !ok { return "", fmt.Errorf("expected perishable offer, not '%+v'", v) } else { return perishable.Id(), nil } })), listeners: queue.NewDelayFIFO(), delayed: queue.NewDelayQueue(), slaves: newSlaveStorage(), } }
// Create creates a DeploymentConfigChangeController. func (factory *DeploymentConfigChangeControllerFactory) Create() controller.RunnableController { deploymentConfigLW := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return factory.Client.DeploymentConfigs(kapi.NamespaceAll).Watch(options) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(deploymentConfigLW, &deployapi.DeploymentConfig{}, queue, 2*time.Minute).Run() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) changeController := &DeploymentConfigChangeController{ client: factory.Client, kClient: factory.KubeClient, decodeConfig: func(deployment *kapi.ReplicationController) (*deployapi.DeploymentConfig, error) { return deployutil.DecodeDeploymentConfig(deployment, factory.Codec) }, } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { utilruntime.HandleError(err) if _, isFatal := err.(fatalError); isFatal { return false } if retries.Count > 0 { return false } return true }, flowcontrol.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { config := obj.(*deployapi.DeploymentConfig) return changeController.Handle(config) }, } }
// Create creates a ServiceBrokerControllerFactory. func (factory *ServiceBrokerControllerFactory) Create() controller.RunnableController { servicebrokerLW := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return factory.Client.ServiceBrokers().List(options) //return factory.KubeClient.Namespaces().List(labels.Everything(), fields.Everything()) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return factory.Client.ServiceBrokers().Watch(options) //return factory.KubeClient.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(servicebrokerLW, &servicebrokerapi.ServiceBroker{}, queue, 10*time.Second).Run() servicebrokerController := &ServiceBrokerController{ Client: factory.Client, KubeClient: factory.KubeClient, ServiceBrokerClient: servicebrokerclient.NewServiceBrokerClient(), } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { utilruntime.HandleError(err) if _, isFatal := err.(fatalError); isFatal { return false } if retries.Count > 0 { return false } return true }, kutil.NewTokenBucketRateLimiter(10, 1), ), Handle: func(obj interface{}) error { servicebroker := obj.(*servicebrokerapi.ServiceBroker) return servicebrokerController.Handle(servicebroker) }, } }
// Create creates a ApplicationControllerFactory. func (factory *ApplicationControllerFactory) Create() controller.RunnableController { applicationLW := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return factory.Client.Applications(kapi.NamespaceAll).List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return factory.Client.Applications(kapi.NamespaceAll).Watch(options) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(applicationLW, &applicationapi.Application{}, queue, 30*time.Second).Run() eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartRecordingToSink(factory.KubeClient.Events("")) applicationController := &ApplicationController{ Client: factory.Client, KubeClient: factory.KubeClient, Recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "application"}), } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { utilruntime.HandleError(err) if _, isFatal := err.(fatalError); isFatal { return false } if retries.Count > 0 { return false } return true }, kutil.NewTokenBucketRateLimiter(10, 1), ), Handle: func(obj interface{}) error { application := obj.(*applicationapi.Application) return applicationController.Handle(application) }, } }
func TestDefaultErrorFunc(t *testing.T) { testPod := &api.Pod{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}, Spec: apitesting.DeepEqualSafePodSpec(), } handler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: runtime.EncodeOrDie(testapi.Default.Codec(), testPod), T: t, } mux := http.NewServeMux() // FakeHandler musn't be sent requests other than the one you want to test. mux.Handle(testapi.Default.ResourcePath("pods", "bar", "foo"), &handler) server := httptest.NewServer(mux) // TODO: Uncomment when fix #19254 // defer server.Close() factory := NewConfigFactory(client.NewOrDie(&restclient.Config{Host: server.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}), api.DefaultSchedulerName) queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) podBackoff := podBackoff{ perPodBackoff: map[types.NamespacedName]*backoffEntry{}, clock: &fakeClock{}, defaultDuration: 1 * time.Millisecond, maxDuration: 1 * time.Second, } errFunc := factory.makeDefaultErrorFunc(&podBackoff, queue) errFunc(testPod, nil) for { // This is a terrible way to do this but I plan on replacing this // whole error handling system in the future. The test will time // out if something doesn't work. time.Sleep(10 * time.Millisecond) got, exists, _ := queue.Get(testPod) if !exists { continue } handler.ValidateRequest(t, testapi.Default.ResourcePath("pods", "bar", "foo"), "GET", nil) if e, a := testPod, got; !reflect.DeepEqual(e, a) { t.Errorf("Expected %v, got %v", e, a) } break } }
// NewPodWatch creates a pod watching function which is backed by a // FIFO/reflector pair. This avoids managing watches directly. // A stop channel to close the watch's reflector is also returned. // It is the caller's responsibility to defer closing the stop channel to prevent leaking resources. func NewPodWatch(client kclient.Interface, namespace, name, resourceVersion string, stopChannel chan struct{}) func() *kapi.Pod { fieldSelector, _ := fields.ParseSelector("metadata.name=" + name) podLW := &deployutil.ListWatcherImpl{ ListFunc: func() (runtime.Object, error) { return client.Pods(namespace).List(labels.Everything(), fieldSelector) }, WatchFunc: func(resourceVersion string) (watch.Interface, error) { return client.Pods(namespace).Watch(labels.Everything(), fieldSelector, resourceVersion) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(podLW, &kapi.Pod{}, queue, 1*time.Minute).RunUntil(stopChannel) return func() *kapi.Pod { obj := queue.Pop() return obj.(*kapi.Pod) } }
// Create creates an ImportController. func (f *ImportControllerFactory) Create() (controller.RunnableController, controller.StoppableController) { lw := &cache.ListWatch{ ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { return f.Client.ImageStreams(kapi.NamespaceAll).List(options) }, WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { return f.Client.ImageStreams(kapi.NamespaceAll).Watch(options) }, } q := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(lw, &api.ImageStream{}, q, f.ResyncInterval).Run() // instantiate a scheduled importer using a number of buckets buckets := 4 switch { case f.MinimumCheckInterval > time.Hour: buckets = 8 case f.MinimumCheckInterval < 10*time.Minute: buckets = 2 } seconds := f.MinimumCheckInterval / time.Second bucketQPS := 1.0 / float32(seconds) * float32(buckets) limiter := util.NewTokenBucketRateLimiter(bucketQPS, 1) b := newScheduled(f.ScheduleEnabled, f.Client, buckets, limiter, f.ImportRateLimiter) // instantiate an importer for changes that happen to the image stream changed := &controller.RetryController{ Queue: q, RetryManager: controller.NewQueueRetryManager( q, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { utilruntime.HandleError(err) return retries.Count < 5 }, util.NewTokenBucketRateLimiter(1, 10), ), Handle: b.Handle, } return changed, b.scheduler }
// WatchPod returns a ListWatch for watching a pod. The stopChannel is used // to close the reflector backing the watch. The caller is responsible for derring a close on the channel to // stop the reflector. func (c *realRecyclerClient) WatchPod(name, namespace, resourceVersion string, stopChannel chan struct{}) func() *api.Pod { fieldSelector, _ := fields.ParseSelector("metadata.name=" + name) podLW := &cache.ListWatch{ ListFunc: func() (runtime.Object, error) { return c.client.Pods(namespace).List(labels.Everything(), fieldSelector) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { return c.client.Pods(namespace).Watch(labels.Everything(), fieldSelector, options) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(podLW, &api.Pod{}, queue, 1*time.Minute).RunUntil(stopChannel) return func() *api.Pod { obj := queue.Pop() return obj.(*api.Pod) } }
// Create creates a new ImageChangeController which is used to trigger builds when a new // image is available func (factory *ImageChangeControllerFactory) Create() controller.RunnableController { queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(&imageStreamLW{factory.Client}, &imageapi.ImageStream{}, queue, 2*time.Minute).Run() store := cache.NewStore(cache.MetaNamespaceKeyFunc) cache.NewReflector(&buildConfigLW{client: factory.Client}, &buildapi.BuildConfig{}, store, 2*time.Minute).Run() imageChangeController := &buildcontroller.ImageChangeController{ BuildConfigStore: store, BuildConfigInstantiator: factory.BuildConfigInstantiator, Stop: factory.Stop, } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, func(obj interface{}, err error, retries controller.Retry) bool { imageStream := obj.(*imageapi.ImageStream) if _, isFatal := err.(buildcontroller.ImageChangeControllerFatalError); isFatal { glog.V(3).Infof("Will not retry fatal error for ImageStream update event %s/%s: %v", imageStream.Namespace, imageStream.Name, err) kutil.HandleError(err) return false } if maxRetries > retries.Count { glog.V(3).Infof("Giving up retrying ImageStream update event %s/%s: %v", imageStream.Namespace, imageStream.Name, err) kutil.HandleError(err) return false } glog.V(4).Infof("Retrying ImageStream update event %s/%s: %v", imageStream.Namespace, imageStream.Name, err) return true }, kutil.NewTokenBucketRateLimiter(1, 10), ), Handle: func(obj interface{}) error { imageRepo := obj.(*imageapi.ImageStream) return imageChangeController.HandleImageRepo(imageRepo) }, } }
// WatchPod returns a ListWatch for watching a pod. The stopChannel is used // to close the reflector backing the watch. The caller is responsible for // derring a close on the channel to stop the reflector. func (c *realRecyclerClient) WatchPod(name, namespace string, stopChannel chan struct{}) func() *api.Pod { fieldSelector, _ := fields.ParseSelector("metadata.name=" + name) podLW := &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { options.FieldSelector = fieldSelector return c.client.Core().Pods(namespace).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { options.FieldSelector = fieldSelector return c.client.Core().Pods(namespace).Watch(options) }, } queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(podLW, &api.Pod{}, queue, 1*time.Minute).RunUntil(stopChannel) return func() *api.Pod { return cache.Pop(queue).(*api.Pod) } }