func (w *etcdWatcher) sendAdd(res *etcd.Response) { if res.Node == nil { utilruntime.HandleError(fmt.Errorf("unexpected nil node: %#v", res)) return } if w.include != nil && !w.include(res.Node.Key) { return } obj, err := w.decodeObject(res.Node) if err != nil { utilruntime.HandleError(fmt.Errorf("failure to decode api object: %v\n'%v' from %#v %#v", err, string(res.Node.Value), res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. return } if !w.filter(obj) { return } action := watch.Added w.emit(watch.Event{ Type: action, Object: obj, }) }
func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { if obj, found := w.cache.getFromCache(node.ModifiedIndex, storage.SimpleFilter(storage.Everything)); found { return obj, nil } obj, err := runtime.Decode(w.encoding, []byte(node.Value)) if err != nil { return nil, err } // ensure resource version is set on the object we load from etcd if err := w.versioner.UpdateObject(obj, node.ModifiedIndex); err != nil { utilruntime.HandleError(fmt.Errorf("failure to version api object (%d) %#v: %v", node.ModifiedIndex, obj, err)) } // perform any necessary transformation if w.transform != nil { obj, err = w.transform(obj) if err != nil { utilruntime.HandleError(fmt.Errorf("failure to transform api object %#v: %v", obj, err)) return nil, err } } if node.ModifiedIndex != 0 { w.cache.addToCache(node.ModifiedIndex, obj) } return obj, nil }
// UpgradeResponse upgrades an HTTP response to one that supports multiplexed // streams. newStreamHandler will be called synchronously whenever the // other end of the upgraded connection creates a new stream. func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection { connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection)) upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade)) if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "unable to upgrade: missing upgrade headers in request: %#v", req.Header) return nil } hijacker, ok := w.(http.Hijacker) if !ok { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "unable to upgrade: unable to hijack response") return nil } w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31) w.WriteHeader(http.StatusSwitchingProtocols) conn, _, err := hijacker.Hijack() if err != nil { runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err)) return nil } spdyConn, err := NewServerConnection(conn, newStreamHandler) if err != nil { runtime.HandleError(fmt.Errorf("unable to upgrade: error creating SPDY server connection: %v", err)) return nil } return spdyConn }
func (dc *DeploymentController) deleteDeployment(obj interface{}) { d, ok := obj.(*extensions.Deployment) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) return } d, ok = tombstone.Obj.(*extensions.Deployment) if !ok { utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a Deployment %#v", obj)) return } } glog.V(4).Infof("Deleting deployment %s", d.Name) dc.enqueueDeployment(d) deployments, err := dc.dLister.Deployments(d.Namespace).List(labels.Everything()) if err != nil { utilruntime.HandleError(fmt.Errorf("error listing deployments in namespace %s: %v", d.Namespace, err)) return } // Trigger cleanup of any old overlapping deployments; we don't care about any error // returned here. for i := range deployments { otherD := deployments[i] overlaps, err := util.OverlapsWith(d, otherD) // Enqueue otherD so it gets cleaned up if err == nil && overlaps { dc.enqueueDeployment(otherD) } } }
// When a pod is updated, figure out what services it used to be a member of // and what services it will be a member of, and enqueue the union of these. // old and cur must be *v1.Pod types. func (e *EndpointController) updatePod(old, cur interface{}) { newPod := cur.(*v1.Pod) oldPod := old.(*v1.Pod) if newPod.ResourceVersion == oldPod.ResourceVersion { // Periodic resync will send update events for all known pods. // Two different versions of the same pod will always have different RVs. return } services, err := e.getPodServiceMemberships(newPod) if err != nil { utilruntime.HandleError(fmt.Errorf("Unable to get pod %v/%v's service memberships: %v", newPod.Namespace, newPod.Name, err)) return } // Only need to get the old services if the labels changed. if !reflect.DeepEqual(newPod.Labels, oldPod.Labels) || !hostNameAndDomainAreEqual(newPod, oldPod) { oldServices, err := e.getPodServiceMemberships(oldPod) if err != nil { utilruntime.HandleError(fmt.Errorf("Unable to get pod %v/%v's service memberships: %v", oldPod.Namespace, oldPod.Name, err)) return } services = services.Union(oldServices) } for key := range services { e.queue.Add(key) } }
// deletePod will enqueue a Recreate Deployment once all of its pods have stopped running. func (dc *DeploymentController) deletePod(obj interface{}) { pod, ok := obj.(*v1.Pod) // When a delete is dropped, the relist will notice a pod in the store not // in the list, leading to the insertion of a tombstone object which contains // the deleted key/value. Note that this value might be stale. If the Pod // changed labels the new deployment will not be woken up till the periodic resync. if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v, could take up to %v before a deployment recreates/updates pod", obj, FullDeploymentResyncPeriod)) return } pod, ok = tombstone.Obj.(*v1.Pod) if !ok { utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a pod %#v, could take up to %v before a deployment recreates/updates pods", obj, FullDeploymentResyncPeriod)) return } } if d := dc.getDeploymentForPod(pod); d != nil && d.Spec.Strategy.Type == extensions.RecreateDeploymentStrategyType { podList, err := dc.listPods(d) if err == nil && len(podList.Items) == 0 { dc.enqueueDeployment(d) } } }
// When a pod is deleted, enqueue the replica set that manages the pod and update its expectations. // obj could be an *v1.Pod, or a DeletionFinalStateUnknown marker item. func (rsc *ReplicaSetController) deletePod(obj interface{}) { pod, ok := obj.(*v1.Pod) // When a delete is dropped, the relist will notice a pod in the store not // in the list, leading to the insertion of a tombstone object which contains // the deleted key/value. Note that this value might be stale. If the pod // changed labels the new ReplicaSet will not be woken up till the periodic resync. if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %+v", obj)) return } pod, ok = tombstone.Obj.(*v1.Pod) if !ok { utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a pod %#v", obj)) return } } glog.V(4).Infof("Pod %s/%s deleted through %v, timestamp %+v: %#v.", pod.Namespace, pod.Name, utilruntime.GetCaller(), pod.DeletionTimestamp, pod) if rs := rsc.getPodReplicaSet(pod); rs != nil { rsKey, err := controller.KeyFunc(rs) if err != nil { utilruntime.HandleError(fmt.Errorf("Couldn't get key for ReplicaSet %#v: %v", rs, err)) return } rsc.expectations.DeletionObserved(rsKey, controller.PodKey(pod)) rsc.enqueueReplicaSet(rs) } }
// When a pod is deleted, enqueue the job that manages the pod and update its expectations. // obj could be an *v1.Pod, or a DeletionFinalStateUnknown marker item. func (jm *JobController) deletePod(obj interface{}) { pod, ok := obj.(*v1.Pod) // When a delete is dropped, the relist will notice a pod in the store not // in the list, leading to the insertion of a tombstone object which contains // the deleted key/value. Note that this value might be stale. If the pod // changed labels the new job will not be woken up till the periodic resync. if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %+v", obj)) return } pod, ok = tombstone.Obj.(*v1.Pod) if !ok { utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a pod %+v", obj)) return } } if job := jm.getPodJob(pod); job != nil { jobKey, err := controller.KeyFunc(job) if err != nil { utilruntime.HandleError(fmt.Errorf("Couldn't get key for job %#v: %v", job, err)) return } jm.expectations.DeletionObserved(jobKey) jm.enqueueController(job) } }
func (w *etcdWatcher) sendDelete(res *etcd.Response) { if res.PrevNode == nil { utilruntime.HandleError(fmt.Errorf("unexpected nil prev node: %#v", res)) return } if w.include != nil && !w.include(res.PrevNode.Key) { return } node := *res.PrevNode if res.Node != nil { // Note that this sends the *old* object with the etcd index for the time at // which it gets deleted. This will allow users to restart the watch at the right // index. node.ModifiedIndex = res.Node.ModifiedIndex } obj, err := w.decodeObject(&node) if err != nil { utilruntime.HandleError(fmt.Errorf("failure to decode api object: %v\nfrom %#v %#v", err, res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. return } if !w.filter(obj) { return } w.emit(watch.Event{ Type: watch.Deleted, Object: obj, }) }
func (c *MaxPDVolumeCountChecker) filterVolumes(volumes []v1.Volume, namespace string, filteredVolumes map[string]bool) error { for i := range volumes { vol := &volumes[i] if id, ok := c.filter.FilterVolume(vol); ok { filteredVolumes[id] = true } else if vol.PersistentVolumeClaim != nil { pvcName := vol.PersistentVolumeClaim.ClaimName if pvcName == "" { return fmt.Errorf("PersistentVolumeClaim had no name") } pvc, err := c.pvcInfo.GetPersistentVolumeClaimInfo(namespace, pvcName) if err != nil { // if the PVC is not found, log the error and count the PV towards the PV limit // generate a random volume ID since its required for de-dup utilruntime.HandleError(fmt.Errorf("Unable to look up PVC info for %s/%s, assuming PVC matches predicate when counting limits: %v", namespace, pvcName, err)) source := rand.NewSource(time.Now().UnixNano()) generatedID := "missingPVC" + strconv.Itoa(rand.New(source).Intn(1000000)) filteredVolumes[generatedID] = true return nil } if pvc == nil { return fmt.Errorf("PersistentVolumeClaim not found: %q", pvcName) } pvName := pvc.Spec.VolumeName if pvName == "" { return fmt.Errorf("PersistentVolumeClaim is not bound: %q", pvcName) } pv, err := c.pvInfo.GetPersistentVolumeInfo(pvName) if err != nil { // if the PV is not found, log the error // and count the PV towards the PV limit // generate a random volume ID since its required for de-dup utilruntime.HandleError(fmt.Errorf("Unable to look up PV info for %s/%s/%s, assuming PV matches predicate when counting limits: %v", namespace, pvcName, pvName, err)) source := rand.NewSource(time.Now().UnixNano()) generatedID := "missingPV" + strconv.Itoa(rand.New(source).Intn(1000000)) filteredVolumes[generatedID] = true return nil } if pv == nil { return fmt.Errorf("PersistentVolume not found: %q", pvName) } if id, ok := c.filter.FilterPersistentVolume(pv); ok { filteredVolumes[id] = true } } } return nil }
func PostStartHook(hookContext genericapiserver.PostStartHookContext) error { // intializing roles is really important. On some e2e runs, we've seen cases where etcd is down when the server // starts, the roles don't initialize, and nothing works. err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) { clientset, err := rbacclient.NewForConfig(hookContext.LoopbackClientConfig) if err != nil { utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err)) return false, nil } existingClusterRoles, err := clientset.ClusterRoles().List(api.ListOptions{}) if err != nil { utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err)) return false, nil } // only initialized on empty etcd if len(existingClusterRoles.Items) == 0 { for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) { if _, err := clientset.ClusterRoles().Create(&clusterRole); err != nil { // don't fail on failures, try to create as many as you can utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err)) continue } glog.Infof("Created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name) } } existingClusterRoleBindings, err := clientset.ClusterRoleBindings().List(api.ListOptions{}) if err != nil { utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err)) return false, nil } // only initialized on empty etcd if len(existingClusterRoleBindings.Items) == 0 { for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) { if _, err := clientset.ClusterRoleBindings().Create(&clusterRoleBinding); err != nil { // don't fail on failures, try to create as many as you can utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err)) continue } glog.Infof("Created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name) } } return true, nil }) // if we're never able to make it through intialization, kill the API server if err != nil { return fmt.Errorf("unable to initialize roles: %v", err) } return nil }
// maybeDeleteTerminatingPod non-gracefully deletes pods that are terminating // that should not be gracefully terminated. func (nc *NodeController) maybeDeleteTerminatingPod(obj interface{}) { pod, ok := obj.(*v1.Pod) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { glog.Errorf("Couldn't get object from tombstone %#v", obj) return } pod, ok = tombstone.Obj.(*v1.Pod) if !ok { glog.Errorf("Tombstone contained object that is not a Pod %#v", obj) return } } // consider only terminating pods if pod.DeletionTimestamp == nil { return } nodeObj, found, err := nc.nodeStore.Store.GetByKey(pod.Spec.NodeName) if err != nil { // this can only happen if the Store.KeyFunc has a problem creating // a key for the pod. If it happens once, it will happen again so // don't bother requeuing the pod. utilruntime.HandleError(err) return } // if there is no such node, do nothing and let the podGC clean it up. if !found { return } // delete terminating pods that have been scheduled on // nodes that do not support graceful termination // TODO(mikedanese): this can be removed when we no longer // guarantee backwards compatibility of master API to kubelets with // versions less than 1.1.0 node := nodeObj.(*v1.Node) v, err := utilversion.ParseSemantic(node.Status.NodeInfo.KubeletVersion) if err != nil { glog.V(0).Infof("Couldn't parse version %q of node: %v", node.Status.NodeInfo.KubeletVersion, err) utilruntime.HandleError(nc.forcefullyDeletePod(pod)) return } if v.LessThan(gracefulDeletionVersion) { utilruntime.HandleError(nc.forcefullyDeletePod(pod)) return } }
// Run starts an asynchronous loop that monitors the status of cluster nodes. func (nc *NodeController) Run() { go func() { defer utilruntime.HandleCrash() if !cache.WaitForCacheSync(wait.NeverStop, nc.nodeInformer.Informer().HasSynced, nc.podInformer.Informer().HasSynced, nc.daemonSetInformer.Informer().HasSynced) { utilruntime.HandleError(errors.New("NodeController timed out while waiting for informers to sync...")) return } // Incorporate the results of node status pushed from kubelet to master. go wait.Until(func() { if err := nc.monitorNodeStatus(); err != nil { glog.Errorf("Error monitoring node status: %v", err) } }, nc.nodeMonitorPeriod, wait.NeverStop) // Managing eviction of nodes: // When we delete pods off a node, if the node was not empty at the time we then // queue an eviction watcher. If we hit an error, retry deletion. go wait.Until(func() { nc.evictorLock.Lock() defer nc.evictorLock.Unlock() for k := range nc.zonePodEvictor { nc.zonePodEvictor[k].Try(func(value TimedValue) (bool, time.Duration) { obj, exists, err := nc.nodeStore.GetByKey(value.Value) if err != nil { glog.Warningf("Failed to get Node %v from the nodeStore: %v", value.Value, err) } else if !exists { glog.Warningf("Node %v no longer present in nodeStore!", value.Value) } else { node, _ := obj.(*v1.Node) zone := utilnode.GetZoneKey(node) EvictionsNumber.WithLabelValues(zone).Inc() } nodeUid, _ := value.UID.(string) remaining, err := deletePods(nc.kubeClient, nc.recorder, value.Value, nodeUid, nc.daemonSetStore) if err != nil { utilruntime.HandleError(fmt.Errorf("unable to evict node %q: %v", value.Value, err)) return false, 0 } if remaining { glog.Infof("Pods awaiting deletion due to NodeController eviction") } return true, 0 }) } }, nodeEvictionPeriod, wait.NeverStop) }() }
func (w *etcdWatcher) sendModify(res *etcd.Response) { if res.Node == nil { glog.Errorf("unexpected nil node: %#v", res) return } if w.include != nil && !w.include(res.Node.Key) { return } curObj, err := w.decodeObject(res.Node) if err != nil { utilruntime.HandleError(fmt.Errorf("failure to decode api object: %v\n'%v' from %#v %#v", err, string(res.Node.Value), res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. return } curObjPasses := w.filter(curObj) oldObjPasses := false var oldObj runtime.Object if res.PrevNode != nil && res.PrevNode.Value != "" { // Ignore problems reading the old object. if oldObj, err = w.decodeObject(res.PrevNode); err == nil { if err := w.versioner.UpdateObject(oldObj, res.Node.ModifiedIndex); err != nil { utilruntime.HandleError(fmt.Errorf("failure to version api object (%d) %#v: %v", res.Node.ModifiedIndex, oldObj, err)) } oldObjPasses = w.filter(oldObj) } } // Some changes to an object may cause it to start or stop matching a filter. // We need to report those as adds/deletes. So we have to check both the previous // and current value of the object. switch { case curObjPasses && oldObjPasses: w.emit(watch.Event{ Type: watch.Modified, Object: curObj, }) case curObjPasses && !oldObjPasses: w.emit(watch.Event{ Type: watch.Added, Object: curObj, }) case !curObjPasses && oldObjPasses: w.emit(watch.Event{ Type: watch.Deleted, Object: oldObj, }) } // Do nothing if neither new nor old object passed the filter. }
// RunUntil starts the controller until the provided ch is closed. func (c *Repair) RunUntil(ch chan struct{}) { wait.Until(func() { if err := c.RunOnce(); err != nil { runtime.HandleError(err) } }, c.interval, ch) }
// orphanFinalizer dequeues a node from the orphanQueue, then finds its dependents // based on the graph maintained by the GC, then removes it from the // OwnerReferences of its dependents, and finally updates the owner to remove // the "Orphan" finalizer. The node is add back into the orphanQueue if any of // these steps fail. func (gc *GarbageCollector) orphanFinalizer() { timedItem, quit := gc.orphanQueue.Get() if quit { return } defer gc.orphanQueue.Done(timedItem) owner, ok := timedItem.Object.(*node) if !ok { utilruntime.HandleError(fmt.Errorf("expect *node, got %#v", timedItem.Object)) } // we don't need to lock each element, because they never get updated owner.dependentsLock.RLock() dependents := make([]*node, 0, len(owner.dependents)) for dependent := range owner.dependents { dependents = append(dependents, dependent) } owner.dependentsLock.RUnlock() err := gc.orhpanDependents(owner.identity, dependents) if err != nil { glog.V(6).Infof("orphanDependents for %s failed with %v", owner.identity, err) gc.orphanQueue.Add(timedItem) return } // update the owner, remove "orphaningFinalizer" from its finalizers list err = gc.removeOrphanFinalizer(owner) if err != nil { glog.V(6).Infof("removeOrphanFinalizer for %s failed with %v", owner.identity, err) gc.orphanQueue.Add(timedItem) } OrphanProcessingLatency.Observe(sinceInMicroseconds(gc.clock, timedItem.StartTime)) }
func (dc *DeploymentController) updateDeployment(old, cur interface{}) { oldD := old.(*extensions.Deployment) curD := cur.(*extensions.Deployment) glog.V(4).Infof("Updating deployment %s", oldD.Name) dc.enqueueDeployment(curD) // If the selector of the current deployment just changed, we need to requeue any old // overlapping deployments. If the new selector steps on another deployment, the current // deployment will get denied during the resync loop. if !reflect.DeepEqual(curD.Spec.Selector, oldD.Spec.Selector) { deployments, err := dc.dLister.Deployments(curD.Namespace).List(labels.Everything()) if err != nil { utilruntime.HandleError(fmt.Errorf("error listing deployments in namespace %s: %v", curD.Namespace, err)) return } // Trigger cleanup of any old overlapping deployments; we don't care about any error // returned here. for i := range deployments { otherD := deployments[i] oldOverlaps, oldErr := util.OverlapsWith(oldD, otherD) curOverlaps, curErr := util.OverlapsWith(curD, otherD) // Enqueue otherD so it gets cleaned up if oldErr == nil && curErr == nil && oldOverlaps && !curOverlaps { dc.enqueueDeployment(otherD) } } } }
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 shouldOrphanDependents(e *event, accessor metav1.Object) bool { // The delta_fifo may combine the creation and update of the object into one // event, so we need to check AddEvent as well. if e.oldObj == nil { if accessor.GetDeletionTimestamp() == nil { return false } } else { oldAccessor, err := meta.Accessor(e.oldObj) if err != nil { utilruntime.HandleError(fmt.Errorf("cannot access oldObj: %v", err)) return false } // ignore the event if it's not updating DeletionTimestamp from non-nil to nil. if accessor.GetDeletionTimestamp() == nil || oldAccessor.GetDeletionTimestamp() != nil { return false } } finalizers := accessor.GetFinalizers() for _, finalizer := range finalizers { if finalizer == v1.FinalizerOrphan { return true } } return false }
// checkLeftoverEndpoints lists all currently existing endpoints and adds their // service to the queue. This will detect endpoints that exist with no // corresponding service; these endpoints need to be deleted. We only need to // do this once on startup, because in steady-state these are detected (but // some stragglers could have been left behind if the endpoint controller // reboots). func (e *EndpointController) checkLeftoverEndpoints() { list, err := e.client.Core().Endpoints(v1.NamespaceAll).List(v1.ListOptions{}) if err != nil { utilruntime.HandleError(fmt.Errorf("Unable to list endpoints (%v); orphaned endpoints will not be cleaned up. (They're pretty harmless, but you can restart this component if you want another attempt made.)", err)) return } for i := range list.Items { ep := &list.Items[i] key, err := keyFunc(ep) if err != nil { utilruntime.HandleError(fmt.Errorf("Unable to get key for endpoint %#v", ep)) continue } e.queue.Add(key) } }
// serviceAccountDeleted reacts to a ServiceAccount deletion by recreating a default ServiceAccount in the namespace if needed func (c *ServiceAccountsController) serviceAccountDeleted(obj interface{}) { sa, ok := obj.(*v1.ServiceAccount) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) return } sa, ok = tombstone.Obj.(*v1.ServiceAccount) if !ok { utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a ServiceAccount %#v", obj)) return } } c.queue.Add(sa.Namespace) }
// ServePortForward handles a port forwarding request. A single request is // kept alive as long as the client is still alive and the connection has not // been timed out due to idleness. This function handles multiple forwarded // connections; i.e., multiple `curl http://localhost:8888/` requests will be // handled by a single invocation of ServePortForward. func ServePortForward(w http.ResponseWriter, req *http.Request, portForwarder PortForwarder, podName string, uid types.UID, idleTimeout time.Duration, streamCreationTimeout time.Duration) { supportedPortForwardProtocols := []string{PortForwardProtocolV1Name} _, err := httpstream.Handshake(req, w, supportedPortForwardProtocols) // negotiated protocol isn't currently used server side, but could be in the future if err != nil { // Handshake writes the error to the client utilruntime.HandleError(err) return } streamChan := make(chan httpstream.Stream, 1) glog.V(5).Infof("Upgrading port forward response") upgrader := spdy.NewResponseUpgrader() conn := upgrader.UpgradeResponse(w, req, portForwardStreamReceived(streamChan)) if conn == nil { return } defer conn.Close() glog.V(5).Infof("(conn=%p) setting port forwarding streaming connection idle timeout to %v", conn, idleTimeout) conn.SetIdleTimeout(idleTimeout) h := &portForwardStreamHandler{ conn: conn, streamChan: streamChan, streamPairs: make(map[string]*portForwardStreamPair), streamCreationTimeout: streamCreationTimeout, pod: podName, uid: uid, forwarder: portForwarder, } h.run() }
// worker processes the queue of namespace objects. // Each namespace can be in the queue at most once. // The system ensures that no two workers can process // the same namespace at the same time. func (nm *NamespaceController) worker() { workFunc := func() bool { key, quit := nm.queue.Get() if quit { return true } defer nm.queue.Done(key) err := nm.syncNamespaceFromKey(key.(string)) if err == nil { // no error, forget this entry and return nm.queue.Forget(key) return false } if estimate, ok := err.(*contentRemainingError); ok { t := estimate.Estimate/2 + 1 glog.V(4).Infof("Content remaining in namespace %s, waiting %d seconds", key, t) nm.queue.AddAfter(key, time.Duration(t)*time.Second) } else { // rather than wait for a full resync, re-add the namespace to the queue to be processed nm.queue.AddRateLimited(key) utilruntime.HandleError(err) } return false } for { quit := workFunc() if quit { return } } }
// worker runs a worker thread that just dequeues items, processes them, and marks them done. func (rq *ResourceQuotaController) worker(queue workqueue.RateLimitingInterface) func() { workFunc := func() bool { key, quit := queue.Get() if quit { return true } defer queue.Done(key) err := rq.syncHandler(key.(string)) if err == nil { queue.Forget(key) return false } utilruntime.HandleError(err) queue.AddRateLimited(key) return false } return func() { for { if quit := workFunc(); quit { glog.Infof("resource quota controller worker shutting down") return } } } }
// getDeploymentForPod returns the deployment managing the given Pod. func (dc *DeploymentController) getDeploymentForPod(pod *v1.Pod) *extensions.Deployment { // Find the owning replica set var rs *extensions.ReplicaSet var err error // Look at the owner reference controllerRef := controller.GetControllerOf(&pod.ObjectMeta) if controllerRef != nil { // Not a pod owned by a replica set. if controllerRef.Kind != extensions.SchemeGroupVersion.WithKind("ReplicaSet").Kind { return nil } rs, err = dc.rsLister.ReplicaSets(pod.Namespace).Get(controllerRef.Name) if err != nil { glog.V(4).Infof("Cannot get replicaset %q for pod %q: %v", controllerRef.Name, pod.Name, err) return nil } } else { // Fallback to listing replica sets. rss, err := dc.rsLister.GetPodReplicaSets(pod) if err != nil { glog.V(4).Infof("Cannot list replica sets for pod %q: %v", pod.Name, err) return nil } // TODO: Handle multiple replica sets gracefully // For now we return the oldest replica set. if len(rss) > 1 { utilruntime.HandleError(fmt.Errorf("more than one ReplicaSet is selecting pod %q with labels: %+v", pod.Name, pod.Labels)) sort.Sort(controller.ReplicaSetsByCreationTimestamp(rss)) } rs = rss[0] } return dc.getDeploymentForReplicaSet(rs) }
// InternalError renders a simple internal error func InternalError(w http.ResponseWriter, req *http.Request, err error) { w.Header().Set("Content-Type", "text/plain") w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Internal Server Error: %#v: %v", req.RequestURI, err) runtime.HandleError(err) }
// shouldDelete checks if a Update is removing all the object's finalizers. If so, // it further checks if the object's DeletionGracePeriodSeconds is 0. If so, it // returns true. func (e *Store) shouldDelete(ctx genericapirequest.Context, key string, obj, existing runtime.Object) bool { if !e.EnableGarbageCollection { return false } newMeta, err := metav1.ObjectMetaFor(obj) if err != nil { utilruntime.HandleError(err) return false } oldMeta, err := metav1.ObjectMetaFor(existing) if err != nil { utilruntime.HandleError(err) return false } return len(newMeta.Finalizers) == 0 && oldMeta.DeletionGracePeriodSeconds != nil && *oldMeta.DeletionGracePeriodSeconds == 0 }
func recordToSink(sink EventSink, event *v1.Event, eventCorrelator *EventCorrelator, randGen *rand.Rand, sleepDuration time.Duration) { // Make a copy before modification, because there could be multiple listeners. // Events are safe to copy like this. eventCopy := *event event = &eventCopy result, err := eventCorrelator.EventCorrelate(event) if err != nil { utilruntime.HandleError(err) } if result.Skip { return } tries := 0 for { if recordEvent(sink, result.Event, result.Patch, result.Event.Count > 1, eventCorrelator) { break } tries++ if tries >= maxTriesPerEvent { glog.Errorf("Unable to write event '%#v' (retry limit exceeded!)", event) break } // Randomize the first sleep so that various clients won't all be // synced up if the master goes down. if tries == 1 { time.Sleep(time.Duration(float64(sleepDuration) * randGen.Float64())) } else { time.Sleep(sleepDuration) } } }
// forward dials the remote host specific in req, upgrades the request, starts // listeners for each port specified in ports, and forwards local connections // to the remote host via streams. func (pf *PortForwarder) forward() error { var err error listenSuccess := false for _, port := range pf.ports { err = pf.listenOnPort(&port) switch { case err == nil: listenSuccess = true default: if pf.errOut != nil { fmt.Fprintf(pf.errOut, "Unable to listen on port %d: %v\n", port.Local, err) } } } if !listenSuccess { return fmt.Errorf("Unable to listen on any of the requested ports: %v", pf.ports) } if pf.Ready != nil { close(pf.Ready) } // wait for interrupt or conn closure select { case <-pf.stopChan: case <-pf.streamConn.CloseChan(): runtime.HandleError(errors.New("lost connection to pod")) } return nil }
// run is the main loop for the portForwardStreamHandler. It processes new // streams, invoking portForward for each complete stream pair. The loop exits // when the httpstream.Connection is closed. func (h *portForwardStreamHandler) run() { glog.V(5).Infof("(conn=%p) waiting for port forward streams", h.conn) Loop: for { select { case <-h.conn.CloseChan(): glog.V(5).Infof("(conn=%p) upgraded connection closed", h.conn) break Loop case stream := <-h.streamChan: requestID := h.requestID(stream) streamType := stream.Headers().Get(api.StreamType) glog.V(5).Infof("(conn=%p, request=%s) received new stream of type %s", h.conn, requestID, streamType) p, created := h.getStreamPair(requestID) if created { go h.monitorStreamPair(p, time.After(h.streamCreationTimeout)) } if complete, err := p.add(stream); err != nil { msg := fmt.Sprintf("error processing stream for request %s: %v", requestID, err) utilruntime.HandleError(errors.New(msg)) p.printError(msg) } else if complete { go h.portForward(p) } } } }