// Runs e; will not return until stopCh is closed. workers determines how many // endpoints will be handled in parallel. func (e *EndpointController) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() defer e.queue.ShutDown() go e.serviceController.Run(stopCh) go e.podController.Run(stopCh) if !cache.WaitForCacheSync(stopCh, e.podStoreSynced) { return } for i := 0; i < workers; i++ { go wait.Until(e.worker, time.Second, stopCh) } go func() { defer utilruntime.HandleCrash() time.Sleep(5 * time.Minute) // give time for our cache to fill e.checkLeftoverEndpoints() }() if e.internalPodInformer != nil { go e.internalPodInformer.Run(stopCh) } <-stopCh }
// killContainersWithSyncResult kills all pod's containers with sync results. func (m *kubeGenericRuntimeManager) killContainersWithSyncResult(pod *v1.Pod, runningPod kubecontainer.Pod, gracePeriodOverride *int64) (syncResults []*kubecontainer.SyncResult) { containerResults := make(chan *kubecontainer.SyncResult, len(runningPod.Containers)) wg := sync.WaitGroup{} wg.Add(len(runningPod.Containers)) for _, container := range runningPod.Containers { go func(container *kubecontainer.Container) { defer utilruntime.HandleCrash() defer wg.Done() killContainerResult := kubecontainer.NewSyncResult(kubecontainer.KillContainer, container.Name) if err := m.killContainer(pod, container.ID, container.Name, "Need to kill Pod", gracePeriodOverride); err != nil { killContainerResult.Fail(kubecontainer.ErrKillContainer, err.Error()) } containerResults <- killContainerResult }(container) } wg.Wait() close(containerResults) for containerResult := range containerResults { syncResults = append(syncResults, containerResult) } return }
func (a *HorizontalController) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() glog.Infof("Starting HPA Controller") go a.controller.Run(stopCh) <-stopCh glog.Infof("Shutting down HPA Controller") }
// monitorResizeEvents spawns a goroutine that periodically gets the terminal size and tries to send // it to the resizeEvents channel if the size has changed. The goroutine stops when the stop channel // is closed. func monitorResizeEvents(fd uintptr, resizeEvents chan<- Size, stop chan struct{}) { go func() { defer runtime.HandleCrash() size := GetSize(fd) if size == nil { return } lastSize := *size for { // see if we need to stop running select { case <-stop: return default: } size := GetSize(fd) if size == nil { return } if size.Height != lastSize.Height || size.Width != lastSize.Width { lastSize.Height = size.Height lastSize.Width = size.Width resizeEvents <- *size } // sleep to avoid hot looping time.Sleep(250 * time.Millisecond) } }() }
func (udp *udpProxySocket) getBackendConn(activeClients *clientCache, cliAddr net.Addr, proxier *Proxier, service proxy.ServicePortName, timeout time.Duration) (net.Conn, error) { activeClients.mu.Lock() defer activeClients.mu.Unlock() svrConn, found := activeClients.clients[cliAddr.String()] if !found { // TODO: This could spin up a new goroutine to make the outbound connection, // and keep accepting inbound traffic. glog.V(3).Infof("New UDP connection from %s", cliAddr) var err error svrConn, err = tryConnect(service, cliAddr, "udp", proxier) if err != nil { return nil, err } if err = svrConn.SetDeadline(time.Now().Add(timeout)); err != nil { glog.Errorf("SetDeadline failed: %v", err) return nil, err } activeClients.clients[cliAddr.String()] = svrConn go func(cliAddr net.Addr, svrConn net.Conn, activeClients *clientCache, timeout time.Duration) { defer runtime.HandleCrash() udp.proxyClient(cliAddr, svrConn, activeClients, timeout) }(cliAddr, svrConn, activeClients, timeout) } return svrConn, nil }
// Runs controller blocks until stopCh is closed func (e *TokensController) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() // Start controllers (to fill stores, call informers, fill work queues) go e.serviceAccountController.Run(stopCh) go e.secretController.Run(stopCh) // Wait for stores to fill for !e.serviceAccountController.HasSynced() || !e.secretController.HasSynced() { time.Sleep(100 * time.Millisecond) } // Spawn workers to process work queues for i := 0; i < workers; i++ { go wait.Until(e.syncServiceAccount, 0, stopCh) go wait.Until(e.syncSecret, 0, stopCh) } // Block until stop channel is closed <-stopCh // Shut down queues e.syncServiceAccountQueue.ShutDown() e.syncSecretQueue.ShutDown() }
func (cc *cadvisorClient) exportHTTP(port uint) error { // Register the handlers regardless as this registers the prometheus // collector properly. mux := http.NewServeMux() err := cadvisorhttp.RegisterHandlers(mux, cc, "", "", "", "") if err != nil { return err } cadvisorhttp.RegisterPrometheusHandler(mux, cc, "/metrics", containerLabels) // Only start the http server if port > 0 if port > 0 { serv := &http.Server{ Addr: fmt.Sprintf(":%d", port), Handler: mux, } // TODO(vmarmol): Remove this when the cAdvisor port is once again free. // If export failed, retry in the background until we are able to bind. // This allows an existing cAdvisor to be killed before this one registers. go func() { defer runtime.HandleCrash() err := serv.ListenAndServe() for err != nil { glog.Infof("Failed to register cAdvisor on port %d, retrying. Error: %v", port, err) time.Sleep(time.Minute) err = serv.ListenAndServe() } }() } return nil }
// finishRequest makes a given resultFunc asynchronous and handles errors returned by the response. // Any api.Status object returned is considered an "error", which interrupts the normal response flow. func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, err error) { // these channels need to be buffered to prevent the goroutine below from hanging indefinitely // when the select statement reads something other than the one the goroutine sends on. ch := make(chan runtime.Object, 1) errCh := make(chan error, 1) panicCh := make(chan interface{}, 1) go func() { // panics don't cross goroutine boundaries, so we have to handle ourselves defer utilruntime.HandleCrash(func(panicReason interface{}) { // Propagate to parent goroutine panicCh <- panicReason }) if result, err := fn(); err != nil { errCh <- err } else { ch <- result } }() select { case result = <-ch: if status, ok := result.(*metav1.Status); ok { return nil, errors.FromObject(status) } return result, nil case err = <-errCh: return nil, err case p := <-panicCh: panic(p) case <-time.After(timeout): return nil, errors.NewTimeoutError("request did not complete within allowed duration", 0) } }
func (p *processorListener) run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() for { var next interface{} select { case <-stopCh: func() { p.lock.Lock() defer p.lock.Unlock() p.cond.Broadcast() }() return case next = <-p.nextCh: } switch notification := next.(type) { case updateNotification: p.handler.OnUpdate(notification.oldObj, notification.newObj) case addNotification: p.handler.OnAdd(notification.newObj) case deleteNotification: p.handler.OnDelete(notification.oldObj) default: utilruntime.HandleError(fmt.Errorf("unrecognized notification: %#v", next)) } } }
func (p *processorListener) pop(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() for { blockingGet := func() (interface{}, bool) { p.lock.Lock() defer p.lock.Unlock() for len(p.pendingNotifications) == 0 { // check if we're shutdown select { case <-stopCh: return nil, true default: } p.cond.Wait() } nt := p.pendingNotifications[0] p.pendingNotifications = p.pendingNotifications[1:] return nt, false } notification, stopped := blockingGet() if stopped { return } select { case <-stopCh: return case p.nextCh <- notification: } } }
func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() fifo := NewDeltaFIFO(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) }
// receive reads result from the decoder in a loop and sends down the result channel. func (sw *StreamWatcher) receive() { defer close(sw.result) defer sw.Stop() defer utilruntime.HandleCrash() for { action, obj, err := sw.source.Decode() if err != nil { // Ignore expected error. if sw.stopping() { return } switch err { case io.EOF: // watch closed normally case io.ErrUnexpectedEOF: glog.V(1).Infof("Unexpected EOF during watch stream event decoding: %v", err) default: msg := "Unable to decode an event from the watch stream: %v" if net.IsProbableEOF(err) { glog.V(5).Infof(msg, err) } else { glog.Errorf(msg, err) } } return } sw.result <- Event{ Type: action, Object: obj, } } }
func (grm *goRoutineMap) Run( operationName string, operationFunc func() error) error { grm.lock.Lock() defer grm.lock.Unlock() existingOp, exists := grm.operations[operationName] if exists { // Operation with name exists if existingOp.operationPending { return NewAlreadyExistsError(operationName) } if err := existingOp.expBackoff.SafeToRetry(operationName); err != nil { return err } } grm.operations[operationName] = operation{ operationPending: true, expBackoff: existingOp.expBackoff, } go func() (err error) { // Handle unhandled panics (very unlikely) defer k8sRuntime.HandleCrash() // Handle completion of and error, if any, from operationFunc() defer grm.operationComplete(operationName, &err) // Handle panic, if any, from operationFunc() defer k8sRuntime.RecoverFromPanic(&err) return operationFunc() }() return nil }
// monitorResizeEvents spawns a goroutine that waits for SIGWINCH signals (these indicate the // terminal has resized). After receiving a SIGWINCH, this gets the terminal size and tries to send // it to the resizeEvents channel. The goroutine stops when the stop channel is closed. func monitorResizeEvents(fd uintptr, resizeEvents chan<- Size, stop chan struct{}) { go func() { defer runtime.HandleCrash() winch := make(chan os.Signal, 1) signal.Notify(winch, syscall.SIGWINCH) defer signal.Stop(winch) for { select { case <-winch: size := GetSize(fd) if size == nil { return } // try to send size select { case resizeEvents <- *size: // success default: // not sent } case <-stop: return } } }() }
// handle implements a WebSocket handler. func (r *Reader) handle(ws *websocket.Conn) { // Close the connection when the client requests it, or when we finish streaming, whichever happens first closeConnOnce := &sync.Once{} closeConn := func() { closeConnOnce.Do(func() { ws.Close() }) } negotiated := ws.Config().Protocol r.selectedProtocol = negotiated[0] defer close(r.err) defer closeConn() go func() { defer runtime.HandleCrash() // This blocks until the connection is closed. // Client should not send anything. IgnoreReceives(ws, r.timeout) // Once the client closes, we should also close closeConn() }() r.err <- messageCopy(ws, r.r, !r.protocols[r.selectedProtocol].Binary, r.ping, r.timeout) }
// Run the main goroutine responsible for watching and syncing jobs. func (jm *CronJobController) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() glog.Infof("Starting CronJob Manager") // Check things every 10 second. go wait.Until(jm.SyncAll, 10*time.Second, stopCh) <-stopCh glog.Infof("Shutting down CronJob Manager") }
// Update reconciles the list's entries with the specified addresses. Existing // tunnels that are not in addresses are removed from entries and closed in a // background goroutine. New tunnels specified in addresses are opened in a // background goroutine and then added to entries. func (l *SSHTunnelList) Update(addrs []string) { haveAddrsMap := make(map[string]bool) wantAddrsMap := make(map[string]bool) func() { l.tunnelsLock.Lock() defer l.tunnelsLock.Unlock() // Build a map of what we currently have. for i := range l.entries { haveAddrsMap[l.entries[i].Address] = true } // Determine any necessary additions. for i := range addrs { // Add tunnel if it is not in l.entries or l.adding if _, ok := haveAddrsMap[addrs[i]]; !ok { if _, ok := l.adding[addrs[i]]; !ok { l.adding[addrs[i]] = true addr := addrs[i] go func() { defer runtime.HandleCrash() // Actually adding tunnel to list will block until lock // is released after deletions. l.createAndAddTunnel(addr) }() } } wantAddrsMap[addrs[i]] = true } // Determine any necessary deletions. var newEntries []sshTunnelEntry for i := range l.entries { if _, ok := wantAddrsMap[l.entries[i].Address]; !ok { tunnelEntry := l.entries[i] glog.Infof("Removing tunnel to deleted node at %q", tunnelEntry.Address) go func() { defer runtime.HandleCrash() if err := tunnelEntry.Tunnel.Close(); err != nil { glog.Errorf("Failed to close tunnel to %q: %v", tunnelEntry.Address, err) } }() } else { newEntries = append(newEntries, l.entries[i]) } } l.entries = newEntries }() }
// Run begins watching and syncing. func (e *quotaAccessor) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() e.reflector.RunUntil(stopCh) <-stopCh glog.Infof("Shutting down quota accessor") }
// etcdWatch calls etcd's Watch function, and handles any errors. Meant to be called // as a goroutine. func (w *etcdWatcher) etcdWatch(ctx context.Context, client etcd.KeysAPI, key string, resourceVersion uint64) { defer utilruntime.HandleCrash() defer close(w.etcdError) defer close(w.etcdIncoming) // All calls to etcd are coming from this function - once it is finished // no other call to etcd should be generated by this watcher. done := func() {} // We need to be prepared, that Stop() can be called at any time. // It can potentially also be called, even before this function is called. // If that is the case, we simply skip all the code here. // See #18928 for more details. var watcher etcd.Watcher returned := func() bool { w.stopLock.Lock() defer w.stopLock.Unlock() if w.stopped { // Watcher has already been stopped - don't event initiate it here. return true } w.wg.Add(1) done = w.wg.Done // Perform initialization of watcher under lock - we want to avoid situation when // Stop() is called in the meantime (which in tests can cause etcd termination and // strange behavior here). if resourceVersion == 0 { latest, err := etcdGetInitialWatchState(ctx, client, key, w.list, w.quorum, w.etcdIncoming) if err != nil { w.etcdError <- err return true } resourceVersion = latest } opts := etcd.WatcherOptions{ Recursive: w.list, AfterIndex: resourceVersion, } watcher = client.Watcher(key, &opts) w.ctx, w.cancel = context.WithCancel(ctx) return false }() defer done() if returned { return } for { resp, err := watcher.Next(w.ctx) if err != nil { w.etcdError <- err return } w.etcdIncoming <- resp } }
// NewReader creates a WebSocket pipe that will copy the contents of r to a provided // WebSocket connection. If ping is true, a zero length message will be sent to the client // before the stream begins reading. // // The protocols parameter maps subprotocol names to StreamProtocols. The empty string // subprotocol name is used if websocket.Config.Protocol is empty. func NewReader(r io.Reader, ping bool, protocols map[string]ReaderProtocolConfig) *Reader { return &Reader{ r: r, err: make(chan error), ping: ping, protocols: protocols, handleCrash: func() { runtime.HandleCrash() }, } }
func (p *streamProtocolV2) copyStdin() { if p.Stdin != nil { var once sync.Once // copy from client's stdin to container's stdin go func() { defer runtime.HandleCrash() // if p.stdin is noninteractive, p.g. `echo abc | kubectl exec -i <pod> -- cat`, make sure // we close remoteStdin as soon as the copy from p.stdin to remoteStdin finishes. Otherwise // the executed command will remain running. defer once.Do(func() { p.remoteStdin.Close() }) if _, err := io.Copy(p.remoteStdin, p.Stdin); err != nil { runtime.HandleError(err) } }() // read from remoteStdin until the stream is closed. this is essential to // be able to exit interactive sessions cleanly and not leak goroutines or // hang the client's terminal. // // TODO we aren't using go-dockerclient any more; revisit this to determine if it's still // required by engine-api. // // go-dockerclient's current hijack implementation // (https://github.com/fsouza/go-dockerclient/blob/89f3d56d93788dfe85f864a44f85d9738fca0670/client.go#L564) // waits for all three streams (stdin/stdout/stderr) to finish copying // before returning. When hijack finishes copying stdout/stderr, it calls // Close() on its side of remoteStdin, which allows this copy to complete. // When that happens, we must Close() on our side of remoteStdin, to // allow the copy in hijack to complete, and hijack to return. go func() { defer runtime.HandleCrash() defer once.Do(func() { p.remoteStdin.Close() }) // this "copy" doesn't actually read anything - it's just here to wait for // the server to close remoteStdin. if _, err := io.Copy(ioutil.Discard, p.remoteStdin); err != nil { runtime.HandleError(err) } }() } }
// IgnoreReceives reads from a WebSocket until it is closed, then returns. If timeout is set, the // read and write deadlines are pushed every time a new message is received. func IgnoreReceives(ws *websocket.Conn, timeout time.Duration) { defer runtime.HandleCrash() var data []byte for { resetTimeout(ws, timeout) if err := websocket.Message.Receive(ws, &data); err != nil { return } } }
// Run begins watching and syncing. func (e *quotaEvaluator) run() { defer utilruntime.HandleCrash() for i := 0; i < e.workers; i++ { go wait.Until(e.doWork, time.Second, e.stopCh) } <-e.stopCh glog.Infof("Shutting down quota evaluator") e.queue.ShutDown() }
// Run starts observing the system with the specified number of workers. func (nm *NamespaceController) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() go nm.controller.Run(stopCh) for i := 0; i < workers; i++ { go wait.Until(nm.worker, time.Second, stopCh) } <-stopCh glog.Infof("Shutting down NamespaceController") nm.queue.ShutDown() }
// 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(workers int) { defer runtime.HandleCrash() go s.serviceController.Run(wait.NeverStop) for i := 0; i < workers; i++ { go wait.Until(s.worker, time.Second, wait.NeverStop) } nodeLW := cache.NewListWatchFromClient(s.kubeClient.Core().RESTClient(), "nodes", v1.NamespaceAll, fields.Everything()) cache.NewReflector(nodeLW, &v1.Node{}, s.nodeLister.Store, 0).Run() go wait.Until(s.nodeSyncLoop, nodeSyncPeriod, wait.NeverStop) }
// Run begins watching and syncing. func (cc *ClusterController) Run() { defer utilruntime.HandleCrash() go cc.clusterController.Run(wait.NeverStop) // monitor cluster status periodically, in phase 1 we just get the health state from "/healthz" go wait.Until(func() { if err := cc.UpdateClusterStatus(); err != nil { glog.Errorf("Error monitoring cluster status: %v", err) } }, cc.clusterMonitorPeriod, wait.NeverStop) }
func (adc *attachDetachController) Run(stopCh <-chan struct{}) { defer runtime.HandleCrash() glog.Infof("Starting Attach Detach Controller") go adc.reconciler.Run(stopCh) go adc.desiredStateOfWorldPopulator.Run(stopCh) <-stopCh glog.Infof("Shutting down Attach Detach Controller") }
// Run starts the leader election loop func (le *LeaderElector) Run() { defer func() { runtime.HandleCrash() le.config.Callbacks.OnStoppedLeading() }() le.acquire() stop := make(chan struct{}) go le.config.Callbacks.OnStartedLeading(stop) le.renew() close(stop) }
func (l *SSHTunnelList) delayedHealthCheck(e sshTunnelEntry, delay time.Duration) { go func() { defer runtime.HandleCrash() time.Sleep(delay) if err := l.healthCheck(e); err != nil { glog.Errorf("Healthcheck failed for tunnel to %q: %v", e.Address, err) glog.Infof("Attempting once to re-establish tunnel to %q", e.Address) l.removeAndReAdd(e) } }() }
func handleResizeEvents(stream io.Reader, channel chan<- term.Size) { defer runtime.HandleCrash() decoder := json.NewDecoder(stream) for { size := term.Size{} if err := decoder.Decode(&size); err != nil { break } channel <- size } }