// CreateDeleteController constructs a BuildPodDeleteController func (factory *BuildPodControllerFactory) CreateDeleteController() controller.RunnableController { client := ControllerClient{factory.KubeClient, factory.OSClient} queue := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, nil) cache.NewReflector(&buildPodDeleteLW{client, queue}, &kapi.Pod{}, queue, 5*time.Minute).Run() buildPodDeleteController := &buildcontroller.BuildPodDeleteController{ BuildStore: factory.buildStore, BuildUpdater: factory.BuildUpdater, } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, cache.MetaNamespaceKeyFunc, controller.RetryNever, kutil.NewTokenBucketRateLimiter(1, 10)), Handle: func(obj interface{}) error { deltas := obj.(cache.Deltas) for _, delta := range deltas { if delta.Type == cache.Deleted { return buildPodDeleteController.HandleBuildPodDeletion(delta.Object.(*kapi.Pod)) } } return nil }, } }
// CreateDeleteController constructs a BuildDeleteController func (factory *BuildControllerFactory) CreateDeleteController() controller.RunnableController { client := ControllerClient{factory.KubeClient, factory.OSClient} queue := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, keyListerGetter{}) cache.NewReflector(&buildDeleteLW{client, queue}, &buildapi.Build{}, queue, 5*time.Minute).RunUntil(factory.Stop) buildDeleteController := &buildcontroller.BuildDeleteController{ PodManager: client, } return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( queue, queue.KeyOf, controller.RetryNever, flowcontrol.NewTokenBucketRateLimiter(1, 10)), Handle: func(obj interface{}) error { deltas := obj.(cache.Deltas) for _, delta := range deltas { if delta.Type == cache.Deleted { return buildDeleteController.HandleBuildDeletion(delta.Object.(*buildapi.Build)) } } return nil }, } }
func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, s.indexer) cfg := &Config{ Queue: fifo, ListerWatcher: s.listerWatcher, ObjectType: s.objectType, FullResyncPeriod: s.fullResyncPeriod, RetryOnError: false, Process: s.HandleDeltas, } func() { s.startedLock.Lock() defer s.startedLock.Unlock() s.controller = New(cfg) s.started = true }() s.stopCh = stopCh s.cacheMutationDetector.Run(stopCh) s.processor.run(stopCh) s.controller.Run(stopCh) }
func NewEventQueueForStore(keyFunc cache.KeyFunc, knownObjects cache.KeyListerGetter) *EventQueue { return &EventQueue{ DeltaFIFO: cache.NewDeltaFIFO( keyFunc, cache.DeltaCompressorFunc(func(d cache.Deltas) cache.Deltas { return deltaCompressor(d, keyFunc) }), knownObjects), } }
func NewEventQueue(keyFunc cache.KeyFunc) *EventQueue { knownObjects := cache.NewStore(keyFunc) return &EventQueue{ DeltaFIFO: cache.NewDeltaFIFO( keyFunc, cache.DeltaCompressorFunc(func(d cache.Deltas) cache.Deltas { return deltaCompressor(d, keyFunc) }), knownObjects), knownObjects: knownObjects, } }
// NewIndexerInformer returns a cache.Indexer and a controller for populating the index // while also providing event notifications. You should only used the returned // cache.Index for Get/List operations; Add/Modify/Deletes will cause the event // notifications to be faulty. // // Parameters: // * lw is list and watch functions for the source of the resource you want to // be informed of. // * objType is an object of the type that you expect to receive. // * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate // calls, even if nothing changed). Otherwise, re-list will be delayed as // long as possible (until the upstream source closes the watch or times out, // or you stop the controller). // * h is the object you want notifications sent to. // func NewIndexerInformer( lw cache.ListerWatcher, objType runtime.Object, resyncPeriod time.Duration, h ResourceEventHandler, indexers cache.Indexers, ) (cache.Indexer, *Controller) { // This will hold the client state, as we know it. clientState := cache.NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers) // This will hold incoming changes. Note how we pass clientState in as a // KeyLister, that way resync operations will result in the correct set // of update/delete deltas. fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, clientState) cfg := &Config{ Queue: fifo, ListerWatcher: lw, ObjectType: objType, FullResyncPeriod: resyncPeriod, RetryOnError: false, Process: func(obj interface{}) error { // from oldest to newest for _, d := range obj.(cache.Deltas) { switch d.Type { case cache.Sync, cache.Added, cache.Updated: if old, exists, err := clientState.Get(d.Object); err == nil && exists { if err := clientState.Update(d.Object); err != nil { return err } h.OnUpdate(old, d.Object) } else { if err := clientState.Add(d.Object); err != nil { return err } h.OnAdd(d.Object) } case cache.Deleted: if err := clientState.Delete(d.Object); err != nil { return err } h.OnDelete(d.Object) } } return nil }, } return clientState, New(cfg) }
// NewSharedInformer creates a new instance for the listwatcher. // TODO: create a cache/factory of these at a higher level for the list all, watch all of a given resource that can // be shared amongst all consumers. func NewSharedInformer(lw cache.ListerWatcher, objType runtime.Object, resyncPeriod time.Duration) SharedInformer { sharedInformer := &sharedInformer{ processor: &sharedProcessor{}, store: cache.NewStore(DeletionHandlingMetaNamespaceKeyFunc), } fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, sharedInformer.store) cfg := &Config{ Queue: fifo, ListerWatcher: lw, ObjectType: objType, FullResyncPeriod: resyncPeriod, RetryOnError: false, Process: sharedInformer.HandleDeltas, } sharedInformer.controller = New(cfg) return sharedInformer }
// Run starts a background goroutine that watches for changes to services that // have (or had) LoadBalancers=true and ensures that they have // load balancers created and deleted appropriately. // serviceSyncPeriod controls how often we check the cluster's services to // ensure that the correct load balancers exist. // nodeSyncPeriod controls how often we check the cluster's nodes to determine // if load balancers need to be updated to point to a new set. // // It's an error to call Run() more than once for a given ServiceController // object. func (s *ServiceController) Run(serviceSyncPeriod, nodeSyncPeriod time.Duration) error { if err := s.init(); err != nil { return err } // We have to make this check because the ListWatch that we use in // WatchServices requires Client functions that aren't in the interface // for some reason. if _, ok := s.kubeClient.(*clientset.Clientset); !ok { return fmt.Errorf("ServiceController only works with real Client objects, but was passed something else satisfying the clientset.Interface.") } // Get the currently existing set of services and then all future creates // and updates of services. // A delta compressor is needed for the DeltaFIFO queue because we only ever // care about the most recent state. serviceQueue := cache.NewDeltaFIFO( cache.MetaNamespaceKeyFunc, cache.DeltaCompressorFunc(func(d cache.Deltas) cache.Deltas { if len(d) == 0 { return d } return cache.Deltas{*d.Newest()} }), s.cache, ) lw := cache.NewListWatchFromClient(s.kubeClient.(*clientset.Clientset).CoreClient, "services", api.NamespaceAll, fields.Everything()) cache.NewReflector(lw, &api.Service{}, serviceQueue, serviceSyncPeriod).Run() for i := 0; i < workerGoroutines; i++ { go s.watchServices(serviceQueue) } nodeLW := cache.NewListWatchFromClient(s.kubeClient.(*clientset.Clientset).CoreClient, "nodes", api.NamespaceAll, fields.Everything()) cache.NewReflector(nodeLW, &api.Node{}, s.nodeLister.Store, 0).Run() go s.nodeSyncLoop(nodeSyncPeriod) return nil }
func (o *ObserveOptions) Run() error { if len(o.deleteCommand) > 0 && len(o.nameSyncCommand) == 0 { fmt.Fprintf(o.errOut, "warning: If you are modifying resources outside of %q, you should use the --names command to ensure you don't miss deletions that occur while the command is not running.\n", o.mapping.Resource) } // watch the given resource for changes store := cache.NewDeltaFIFO(objectArgumentsKeyFunc, nil, o.knownObjects) lw := restListWatcher{Helper: resource.NewHelper(o.client, o.mapping)} if !o.allNamespaces { lw.namespace = o.namespace } // ensure any child processes are reaped if we are running as PID 1 proc.StartReaper() // listen on the provided address for metrics if len(o.listenAddr) > 0 { prometheus.MustRegister(observeCounts) prometheus.MustRegister(execDurations) prometheus.MustRegister(nameExecDurations) errWaitingForSync := fmt.Errorf("waiting for initial sync") healthz.InstallHandler(http.DefaultServeMux, healthz.NamedCheck("ready", func(r *http.Request) error { if !store.HasSynced() { return errWaitingForSync } return nil })) http.Handle("/metrics", prometheus.Handler()) go func() { glog.Fatalf("Unable to listen on %q: %v", o.listenAddr, http.ListenAndServe(o.listenAddr, nil)) }() glog.V(2).Infof("Listening on %s at /metrics and /healthz", o.listenAddr) } // exit cleanly after a certain period // lock guards the loop to ensure no child processes are running var lock sync.Mutex if o.exitAfterPeriod > 0 { go func() { <-time.After(o.exitAfterPeriod) lock.Lock() o.dumpMetrics() fmt.Fprintf(o.errOut, "Shutting down after %s ...\n", o.exitAfterPeriod) os.Exit(0) }() } defer o.dumpMetrics() // start the reflector reflector := cache.NewNamedReflector("observer", lw, nil, store, o.resyncPeriod) reflector.Run() if o.once { // wait until the reflector reports it has completed the initial list and the // fifo has been populated for len(reflector.LastSyncResourceVersion()) == 0 { time.Sleep(50 * time.Millisecond) } // if the store is empty, there is nothing to sync if store.HasSynced() && len(store.ListKeys()) == 0 { fmt.Fprintf(o.errOut, "Nothing to sync, exiting immediately\n") return nil } } // process all changes that occur in the resource syncing := false for { _, err := store.Pop(func(obj interface{}) error { // if we failed to retrieve the list of keys, exit if err := o.argumentStore.ListKeysError(); err != nil { return fmt.Errorf("unable to list known keys: %v", err) } deltas := obj.(cache.Deltas) for _, delta := range deltas { lock.Lock() // handle before and after observe notification switch { case !syncing && delta.Type == cache.Sync: if err := o.startSync(); err != nil { return err } syncing = true case syncing && delta.Type != cache.Sync: if err := o.finishSync(); err != nil { return err } syncing = false } // require the user to provide a name function in order to get events beyond added / updated if !syncing && o.knownObjects == nil && !(delta.Type == cache.Added || delta.Type == cache.Updated) { continue } observeCounts.WithLabelValues(string(delta.Type)).Inc() // calculate the arguments for the delta and then invoke any command object, arguments, output, err := o.calculateArguments(delta) if err != nil { return err } if err := o.next(delta.Type, object, output, arguments); err != nil { return err } lock.Unlock() } return nil }) if err != nil { return err } // if we only want to run once, exit here if o.once && store.HasSynced() { if syncing { if err := o.finishSync(); err != nil { return err } } return nil } } }
func Example() { // source simulates an apiserver object endpoint. source := framework.NewFakeControllerSource() // This will hold the downstream state, as we know it. downstream := cache.NewStore(framework.DeletionHandlingMetaNamespaceKeyFunc) // This will hold incoming changes. Note how we pass downstream in as a // KeyLister, that way resync operations will result in the correct set // of update/delete deltas. fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, nil, downstream) // Let's do threadsafe output to get predictable test results. deletionCounter := make(chan string, 1000) cfg := &framework.Config{ Queue: fifo, ListerWatcher: source, ObjectType: &api.Pod{}, FullResyncPeriod: time.Millisecond * 100, RetryOnError: false, // Let's implement a simple controller that just deletes // everything that comes in. Process: func(obj interface{}) error { // Obj is from the Pop method of the Queue we make above. newest := obj.(cache.Deltas).Newest() if newest.Type != cache.Deleted { // Update our downstream store. err := downstream.Add(newest.Object) if err != nil { return err } // Delete this object. source.Delete(newest.Object.(runtime.Object)) } else { // Update our downstream store. err := downstream.Delete(newest.Object) if err != nil { return err } // fifo's KeyOf is easiest, because it handles // DeletedFinalStateUnknown markers. key, err := fifo.KeyOf(newest.Object) if err != nil { return err } // Report this deletion. deletionCounter <- key } return nil }, } // Create the controller and run it until we close stop. stop := make(chan struct{}) defer close(stop) go framework.New(cfg).Run(stop) // Let's add a few objects to the source. testIDs := []string{"a-hello", "b-controller", "c-framework"} for _, name := range testIDs { // Note that these pods are not valid-- the fake source doesn't // call validation or anything. source.Add(&api.Pod{ObjectMeta: api.ObjectMeta{Name: name}}) } // Let's wait for the controller to process the things we just added. outputSet := util.StringSet{} for i := 0; i < len(testIDs); i++ { outputSet.Insert(<-deletionCounter) } for _, key := range outputSet.List() { fmt.Println(key) } // Output: // a-hello // b-controller // c-framework }