func PostStartHook(hookContext genericapiserver.PostStartHookContext) error { clientset, err := rbacclient.NewForConfig(hookContext.LoopbackClientConfig) if err != nil { utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err)) return nil } existingClusterRoles, err := clientset.ClusterRoles().List(api.ListOptions{}) if err != nil { utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err)) return nil } // if clusterroles already exist, then assume we don't have work to do because we've already // initialized or another API server has started this task if len(existingClusterRoles.Items) > 0 { return nil } 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) } return 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 }
// 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) } } }
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) } } }
func (c *RouterController) HandleNamespaces() { for i := 0; i < c.NamespaceRetries; i++ { namespaces, err := c.Namespaces.NamespaceNames() if err == nil { c.lock.Lock() defer c.lock.Unlock() glog.V(4).Infof("Updating watched namespaces: %v", namespaces) if err := c.Plugin.HandleNamespaces(namespaces); err != nil { utilruntime.HandleError(err) } // Namespace filtering is assumed to be have been // performed so long as the plugin event handler is called // at least once. c.filteredByNamespace = true c.commit() return } utilruntime.HandleError(fmt.Errorf("unable to find namespaces for router: %v", err)) time.Sleep(c.NamespaceWaitInterval) } glog.V(4).Infof("Unable to update list of namespaces") }
// When a pod is deleted, enqueue the replica set that manages the pod and update its expectations. // obj could be an *api.Pod, or a DeletionFinalStateUnknown marker item. func (rsc *ReplicaSetController) deletePod(obj interface{}) { pod, ok := obj.(*api.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.(*api.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) } }
// Process queued changes for an object. The 'process' function is called // repeatedly with each available cache.Delta that describes state changes // for that object. If the process function returns an error queued changes // for that object are dropped but processing continues with the next available // object's cache.Deltas. The error is logged with call stack information. func (queue *EventQueue) Pop(process ProcessEventFunc, expectedType interface{}) (interface{}, error) { return queue.DeltaFIFO.Pop(func(obj interface{}) error { // Oldest to newest delta lists for _, delta := range obj.(cache.Deltas) { // Update private store to track object deletion if queue.knownObjects != nil { queue.updateKnownObjects(delta) } // Handle DeletedFinalStateUnknown delta objects var err error if expectedType != nil { delta.Object, err = extractDeltaObject(delta, expectedType) if err != nil { utilruntime.HandleError(err) return nil } } // Process one delta for the object if err = process(delta); err != nil { utilruntime.HandleError(fmt.Errorf("event processing failed: %v", err)) return nil } } return nil }) }
// 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 *api.Pod types. func (e *endpointController) updatePod(old, cur interface{}) { if api.Semantic.DeepEqual(old, cur) { return } newPod := old.(*api.Pod) 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 } oldPod := cur.(*api.Pod) // Only need to get the old services if the labels changed. if !reflect.DeepEqual(newPod.Labels, oldPod.Labels) { 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) } }
// PrepareForUpdate clears fields that are not allowed to be set by end users on update. // It extracts the latest info from the manifest and sets that on the object. It allows a user // to update the manifest so that it matches the digest (in case an older server stored a manifest // that was malformed, it can always be corrected). func (imageStrategy) PrepareForUpdate(obj, old runtime.Object) { newImage := obj.(*api.Image) oldImage := old.(*api.Image) // image metadata cannot be altered newImage.DockerImageReference = oldImage.DockerImageReference newImage.DockerImageMetadata = oldImage.DockerImageMetadata newImage.DockerImageMetadataVersion = oldImage.DockerImageMetadataVersion newImage.DockerImageLayers = oldImage.DockerImageLayers // allow an image update that results in the manifest matching the digest (the name) newManifest := newImage.DockerImageManifest newImage.DockerImageManifest = oldImage.DockerImageManifest if newManifest != oldImage.DockerImageManifest && len(newManifest) > 0 { ok, err := api.ManifestMatchesImage(oldImage, []byte(newManifest)) if err != nil { utilruntime.HandleError(fmt.Errorf("attempted to validate that a manifest change to %q matched the signature, but failed: %v", oldImage.Name, err)) } else if ok { newImage.DockerImageManifest = newManifest } } if err := api.ImageWithMetadata(newImage); err != nil { utilruntime.HandleError(fmt.Errorf("Unable to update image metadata for %q: %v", newImage.Name, err)) } }
// maybeDeleteTerminatingPod non-gracefully deletes pods that are terminating // that should not be gracefully terminated. func (nc *NodeController) maybeDeleteTerminatingPod(obj interface{}) { pod, ok := obj.(*api.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.(*api.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 } // delete terminating pods that have not yet been scheduled if len(pod.Spec.NodeName) == 0 { utilruntime.HandleError(nc.forcefullyDeletePod(pod)) 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 } // delete terminating pods that have been scheduled on // nonexistent nodes if !found { glog.Warningf("Unable to find Node: %v, deleting all assigned Pods.", pod.Spec.NodeName) utilruntime.HandleError(nc.forcefullyDeletePod(pod)) 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.(*api.Node) v, err := version.Parse(node.Status.NodeInfo.KubeletVersion) if err != nil { glog.V(0).Infof("couldn't parse verions %q of minion: %v", node.Status.NodeInfo.KubeletVersion, err) utilruntime.HandleError(nc.forcefullyDeletePod(pod)) return } if gracefulDeletionVersion.GT(v) { utilruntime.HandleError(nc.forcefullyDeletePod(pod)) return } }
// clearLocalAllocation clears an in-memory allocation if it belongs // to the specified service key. func (ic *IngressIPController) clearLocalAllocation(key, ipString string) bool { glog.V(5).Infof("Attempting to clear local allocation of ip %v for service %v", ipString, key) ip := net.ParseIP(ipString) if ip == nil { // An invalid ip address cannot be deallocated utilruntime.HandleError(fmt.Errorf("Error parsing ip: %v", ipString)) return false } ipKey, ok := ic.allocationMap[ipString] switch { case !ok: glog.V(6).Infof("IP address %v is not currently allocated", ipString) return false case key != ipKey: glog.V(6).Infof("IP address %v is not allocated to service %v", ipString, key) return false } // Remove allocation if err := ic.ipAllocator.Release(ip); err != nil { // Release from contiguous allocator should never return an error. utilruntime.HandleError(fmt.Errorf("Error releasing ip %v for service %v: %v", ipString, key, err)) return false } delete(ic.allocationMap, ipString) glog.V(5).Infof("IP address %v is now available for allocation", ipString) return true }
func (c *ClusterQuotaMappingController) namespaceWork() bool { key, quit := c.namespaceQueue.Get() if quit { return true } defer c.namespaceQueue.Done(key) namespace, exists, err := c.namespaceLister.GetByKey(key.(string)) if !exists { c.namespaceQueue.Forget(key) return false } if err != nil { utilruntime.HandleError(err) return false } err = c.syncNamespace(namespace.(*kapi.Namespace)) outOfRetries := c.namespaceQueue.NumRequeues(key) > 5 switch { case err != nil && outOfRetries: utilruntime.HandleError(err) c.namespaceQueue.Forget(key) case err != nil && !outOfRetries: c.namespaceQueue.AddRateLimited(key) default: c.namespaceQueue.Forget(key) } return false }
// Loop infinitely, processing all service updates provided by the queue. func (s *ServiceController) watchServices(serviceQueue *cache.DeltaFIFO) { for { serviceQueue.Pop(func(obj interface{}) error { deltas, ok := obj.(cache.Deltas) if !ok { runtime.HandleError(fmt.Errorf("Received object from service watcher that wasn't Deltas: %+v", obj)) return nil } delta := deltas.Newest() if delta == nil { runtime.HandleError(fmt.Errorf("Received nil delta from watcher queue.")) return nil } err, retryDelay := s.processDelta(delta) if retryDelay != 0 { // Add the failed service back to the queue so we'll retry it. runtime.HandleError(fmt.Errorf("Failed to process service delta. Retrying in %s: %v", retryDelay, err)) go func(deltas cache.Deltas, delay time.Duration) { time.Sleep(delay) if err := serviceQueue.AddIfNotPresent(deltas); err != nil { runtime.HandleError(fmt.Errorf("Error requeuing service delta - will not retry: %v", err)) } }(deltas, retryDelay) } else if err != nil { runtime.HandleError(fmt.Errorf("Failed to process service delta. Not retrying: %v", err)) } return nil }) } }
// 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 *api.Pod types. func (e *EndpointController) updatePod(old, cur interface{}) { newPod := cur.(*api.Pod) oldPod := old.(*api.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) } }
// makeImageStreamTagAdmissionUsageFunc returns a function that computes a resource usage for given image // stream tag during admission. func makeImageStreamTagAdmissionUsageFunc(isNamespacer osclient.ImageStreamsNamespacer) generic.UsageFunc { return func(object runtime.Object) kapi.ResourceList { ist, ok := object.(*imageapi.ImageStreamTag) if !ok { return kapi.ResourceList{} } res := map[kapi.ResourceName]resource.Quantity{ imageapi.ResourceImageStreams: *resource.NewQuantity(0, resource.BinarySI), } isName, _, err := imageapi.ParseImageStreamTagName(ist.Name) if err != nil { utilruntime.HandleError(err) return kapi.ResourceList{} } is, err := isNamespacer.ImageStreams(ist.Namespace).Get(isName) if err != nil { if !kerrors.IsNotFound(err) { utilruntime.HandleError(fmt.Errorf("failed to get image stream %s/%s: %v", ist.Namespace, isName, err)) } } if is == nil { res[imageapi.ResourceImageStreams] = *resource.NewQuantity(1, resource.BinarySI) } return res } }
func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { if obj, found := w.cache.getFromCache(node.ModifiedIndex, 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 }
// When a pod is deleted, enqueue the job that manages the pod and update its expectations. // obj could be an *api.Pod, or a DeletionFinalStateUnknown marker item. func (jm *JobController) deletePod(obj interface{}) { pod, ok := obj.(*api.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.(*api.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) } }
// invalidateCache returns true if there was a change in the cluster namespace that holds cluster policy and policy bindings func (ac *AuthorizationCache) invalidateCache() bool { invalidateCache := false clusterPolicyList, err := ac.policyClient.ReadOnlyClusterPolicies().List(nil) if err != nil { utilruntime.HandleError(err) return invalidateCache } temporaryVersions := sets.NewString() for _, clusterPolicy := range clusterPolicyList.Items { temporaryVersions.Insert(clusterPolicy.ResourceVersion) } if (len(ac.clusterPolicyResourceVersions) != len(temporaryVersions)) || !ac.clusterPolicyResourceVersions.HasAll(temporaryVersions.List()...) { invalidateCache = true ac.clusterPolicyResourceVersions = temporaryVersions } clusterPolicyBindingList, err := ac.policyClient.ReadOnlyClusterPolicyBindings().List(nil) if err != nil { utilruntime.HandleError(err) return invalidateCache } temporaryVersions.Delete(temporaryVersions.List()...) for _, clusterPolicyBinding := range clusterPolicyBindingList.Items { temporaryVersions.Insert(clusterPolicyBinding.ResourceVersion) } if (len(ac.clusterBindingResourceVersions) != len(temporaryVersions)) || !ac.clusterBindingResourceVersions.HasAll(temporaryVersions.List()...) { invalidateCache = true ac.clusterBindingResourceVersions = temporaryVersions } return invalidateCache }
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.Filter(obj) { return } action := watch.Added if res.Node.ModifiedIndex != res.Node.CreatedIndex { action = watch.Modified } w.emit(watch.Event{ Type: action, Object: obj, }) }
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.Filter(obj) { return } w.emit(watch.Event{ Type: watch.Deleted, Object: obj, }) }
func newPostStartHook(directClusterRoleAccess *clusterroleetcd.REST) genericapiserver.PostStartHookFunc { return func(genericapiserver.PostStartHookContext) error { ctx := api.NewContext() existingClusterRoles, err := directClusterRoleAccess.List(ctx, &api.ListOptions{}) if err != nil { utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err)) return nil } // if clusterroles already exist, then assume we don't have work to do because we've already // initialized or another API server has started this task if len(existingClusterRoles.(*rbac.ClusterRoleList).Items) > 0 { return nil } for _, clusterRole := range bootstrappolicy.ClusterRoles() { if _, err := directClusterRoleAccess.Create(ctx, &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) } return nil } }
func createStreams(req *http.Request, w http.ResponseWriter, supportedStreamProtocols []string, idleTimeout, streamCreationTimeout time.Duration) (*context, bool) { opts, err := newOptions(req) if err != nil { runtime.HandleError(err) w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, err.Error()) return nil, false } if wsstream.IsWebSocketRequest(req) { return createWebSocketStreams(req, w, opts, idleTimeout) } protocol, err := httpstream.Handshake(req, w, supportedStreamProtocols) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprint(w, err.Error()) return nil, false } streamCh := make(chan streamAndReply) upgrader := spdy.NewResponseUpgrader() conn := upgrader.UpgradeResponse(w, req, func(stream httpstream.Stream, replySent <-chan struct{}) error { streamCh <- streamAndReply{Stream: stream, replySent: replySent} return nil }) // from this point on, we can no longer call methods on response if conn == nil { // The upgrader is responsible for notifying the client of any errors that // occurred during upgrading. All we can do is return here at this point // if we weren't successful in upgrading. return nil, false } conn.SetIdleTimeout(idleTimeout) var handler protocolHandler switch protocol { case StreamProtocolV2Name: handler = &v2ProtocolHandler{} case "": glog.V(4).Infof("Client did not request protocol negotiaion. Falling back to %q", StreamProtocolV1Name) fallthrough case StreamProtocolV1Name: handler = &v1ProtocolHandler{} } expired := time.NewTimer(streamCreationTimeout) ctx, err := handler.waitForStreams(streamCh, opts.expectedStreams, expired.C) if err != nil { runtime.HandleError(err) return nil, false } ctx.conn = conn ctx.tty = opts.tty return ctx, true }
// watchHandler watches w and keeps *resourceVersion up to date. func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *string, resyncCh <-chan time.Time, stopCh <-chan struct{}) error { start := time.Now() eventCount := 0 // Stopping the watcher should be idempotent and if we return from this function there's no way // we're coming back in with the same watch interface. defer w.Stop() loop: for { select { case <-stopCh: return errorStopRequested case <-resyncCh: return errorResyncRequested case event, ok := <-w.ResultChan(): if !ok { break loop } if event.Type == watch.Error { return apierrs.FromObject(event.Object) } if e, a := r.expectedType, reflect.TypeOf(event.Object); e != nil && e != a { utilruntime.HandleError(fmt.Errorf("%s: expected type %v, but watch event object had type %v", r.name, e, a)) continue } meta, err := meta.Accessor(event.Object) if err != nil { utilruntime.HandleError(fmt.Errorf("%s: unable to understand watch event %#v", r.name, event)) continue } newResourceVersion := meta.GetResourceVersion() switch event.Type { case watch.Added: r.store.Add(event.Object) case watch.Modified: r.store.Update(event.Object) case watch.Deleted: // TODO: Will any consumers need access to the "last known // state", which is passed in event.Object? If so, may need // to change this. r.store.Delete(event.Object) default: utilruntime.HandleError(fmt.Errorf("%s: unable to understand watch event %#v", r.name, event)) } *resourceVersion = newResourceVersion r.setLastSyncResourceVersion(newResourceVersion) eventCount++ } } watchDuration := time.Now().Sub(start) if watchDuration < 1*time.Second && eventCount == 0 { glog.V(4).Infof("%s: Unexpected watch close - watch lasted less than a second and no items received", r.name) return errors.New("very short watch") } glog.V(4).Infof("%s: Watch close - %v total %v items received", r.name, r.expectedType, eventCount) return nil }
// HandleWS implements a websocket handler. func (s *WatchServer) HandleWS(ws *websocket.Conn) { defer ws.Close() done := make(chan struct{}) go wsstream.IgnoreReceives(ws, 0) var unknown runtime.Unknown internalEvent := &versioned.InternalEvent{} buf := &bytes.Buffer{} streamBuf := &bytes.Buffer{} ch := s.watching.ResultChan() for { select { case <-done: s.watching.Stop() return case event, ok := <-ch: if !ok { // End of results. return } obj := event.Object s.fixup(obj) if err := s.embeddedEncoder.Encode(obj, buf); err != nil { // unexpected error utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err)) return } // ContentType is not required here because we are defaulting to the serializer // type unknown.Raw = buf.Bytes() event.Object = &unknown // the internal event will be versioned by the encoder *internalEvent = versioned.InternalEvent(event) if err := s.encoder.Encode(internalEvent, streamBuf); err != nil { // encoding error utilruntime.HandleError(fmt.Errorf("unable to encode event: %v", err)) s.watching.Stop() return } if s.useTextFraming { if err := websocket.Message.Send(ws, streamBuf.String()); err != nil { // Client disconnect. s.watching.Stop() return } } else { if err := websocket.Message.Send(ws, streamBuf.Bytes()); err != nil { // Client disconnect. s.watching.Stop() return } } buf.Reset() streamBuf.Reset() } } }
// manageReplicas checks and updates replicas for the given ReplicaSet. func (rsc *ReplicaSetController) manageReplicas(filteredPods []*api.Pod, rs *extensions.ReplicaSet) { diff := len(filteredPods) - rs.Spec.Replicas rsKey, err := controller.KeyFunc(rs) if err != nil { glog.Errorf("Couldn't get key for ReplicaSet %#v: %v", rs, err) return } if diff < 0 { diff *= -1 if diff > rsc.burstReplicas { diff = rsc.burstReplicas } rsc.expectations.ExpectCreations(rsKey, diff) wait := sync.WaitGroup{} wait.Add(diff) glog.V(2).Infof("Too few %q/%q replicas, need %d, creating %d", rs.Namespace, rs.Name, rs.Spec.Replicas, diff) for i := 0; i < diff; i++ { go func() { defer wait.Done() if err := rsc.podControl.CreatePods(rs.Namespace, rs.Spec.Template, rs); err != nil { // Decrement the expected number of creates because the informer won't observe this pod glog.V(2).Infof("Failed creation, decrementing expectations for replica set %q/%q", rs.Namespace, rs.Name) rsc.expectations.CreationObserved(rsKey) utilruntime.HandleError(err) } }() } wait.Wait() } else if diff > 0 { if diff > rsc.burstReplicas { diff = rsc.burstReplicas } rsc.expectations.ExpectDeletions(rsKey, diff) glog.V(2).Infof("Too many %q/%q replicas, need %d, deleting %d", rs.Namespace, rs.Name, rs.Spec.Replicas, diff) // No need to sort pods if we are about to delete all of them if rs.Spec.Replicas != 0 { // Sort the pods in the order such that not-ready < ready, unscheduled // < scheduled, and pending < running. This ensures that we delete pods // in the earlier stages whenever possible. sort.Sort(controller.ActivePods(filteredPods)) } wait := sync.WaitGroup{} wait.Add(diff) for i := 0; i < diff; i++ { go func(ix int) { defer wait.Done() if err := rsc.podControl.DeletePod(rs.Namespace, filteredPods[ix].Name, rs); err != nil { // Decrement the expected number of deletes because the informer won't observe this deletion glog.V(2).Infof("Failed deletion, decrementing expectations for replica set %q/%q", rs.Namespace, rs.Name) rsc.expectations.DeletionObserved(rsKey) utilruntime.HandleError(err) } }(i) } wait.Wait() } }
// NewServiceGroup returns the ServiceGroup and a set of all the NodeIDs covered by the service func NewServiceGroup(g osgraph.Graph, serviceNode *kubegraph.ServiceNode) (ServiceGroup, IntSet) { covered := IntSet{} covered.Insert(serviceNode.ID()) service := ServiceGroup{} service.Service = serviceNode for _, uncastServiceFulfiller := range g.PredecessorNodesByEdgeKind(serviceNode, kubeedges.ExposedThroughServiceEdgeKind) { container := osgraph.GetTopLevelContainerNode(g, uncastServiceFulfiller) switch castContainer := container.(type) { case *deploygraph.DeploymentConfigNode: service.FulfillingDCs = append(service.FulfillingDCs, castContainer) case *kubegraph.ReplicationControllerNode: service.FulfillingRCs = append(service.FulfillingRCs, castContainer) case *kubegraph.PodNode: service.FulfillingPods = append(service.FulfillingPods, castContainer) default: utilruntime.HandleError(fmt.Errorf("unrecognized container: %v", castContainer)) } } for _, uncastServiceFulfiller := range g.PredecessorNodesByEdgeKind(serviceNode, routeedges.ExposedThroughRouteEdgeKind) { container := osgraph.GetTopLevelContainerNode(g, uncastServiceFulfiller) switch castContainer := container.(type) { case *routegraph.RouteNode: service.ExposingRoutes = append(service.ExposingRoutes, castContainer) default: utilruntime.HandleError(fmt.Errorf("unrecognized container: %v", castContainer)) } } // add the DCPipelines for all the DCs that fulfill the service for _, fulfillingDC := range service.FulfillingDCs { dcPipeline, dcCovers := NewDeploymentConfigPipeline(g, fulfillingDC) covered.Insert(dcCovers.List()...) service.DeploymentConfigPipelines = append(service.DeploymentConfigPipelines, dcPipeline) } for _, fulfillingRC := range service.FulfillingRCs { rcView, rcCovers := NewReplicationController(g, fulfillingRC) covered.Insert(rcCovers.List()...) service.ReplicationControllers = append(service.ReplicationControllers, rcView) } for _, fulfillingPod := range service.FulfillingPods { _, podCovers := NewPod(g, fulfillingPod) covered.Insert(podCovers.List()...) } return service, covered }
func (c *MaxPDVolumeCountChecker) filterVolumes(volumes []api.Volume, namespace string, filteredVolumes map[string]bool) error { for _, vol := range volumes { 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 }
// PrepareForUpdate clears fields that are not allowed to be set by end users on update. // It extracts the latest info from the manifest and sets that on the object. It allows a user // to update the manifest so that it matches the digest (in case an older server stored a manifest // that was malformed, it can always be corrected). func (s imageStrategy) PrepareForUpdate(ctx kapi.Context, obj, old runtime.Object) { newImage := obj.(*api.Image) oldImage := old.(*api.Image) // image metadata cannot be altered newImage.DockerImageMetadata = oldImage.DockerImageMetadata newImage.DockerImageMetadataVersion = oldImage.DockerImageMetadataVersion newImage.DockerImageLayers = oldImage.DockerImageLayers if oldImage.DockerImageSignatures != nil { newImage.DockerImageSignatures = nil for _, v := range oldImage.DockerImageSignatures { newImage.DockerImageSignatures = append(newImage.DockerImageSignatures, v) } } var err error // allow an image update that results in the manifest matching the digest (the name) if newImage.DockerImageManifest != oldImage.DockerImageManifest { ok := true if len(newImage.DockerImageManifest) > 0 { ok, err = api.ManifestMatchesImage(oldImage, []byte(newImage.DockerImageManifest)) if err != nil { utilruntime.HandleError(fmt.Errorf("attempted to validate that a manifest change to %q matched the signature, but failed: %v", oldImage.Name, err)) } } if !ok { newImage.DockerImageManifest = oldImage.DockerImageManifest } } if newImage.DockerImageConfig != oldImage.DockerImageConfig { ok := true if len(newImage.DockerImageConfig) > 0 { ok, err = api.ImageConfigMatchesImage(newImage, []byte(newImage.DockerImageConfig)) if err != nil { utilruntime.HandleError(fmt.Errorf("attempted to validate that a new config for %q mentioned in the manifest, but failed: %v", oldImage.Name, err)) } } if !ok { newImage.DockerImageConfig = oldImage.DockerImageConfig } } if err = api.ImageWithMetadata(newImage); err != nil { utilruntime.HandleError(fmt.Errorf("Unable to update image metadata for %q: %v", newImage.Name, err)) } // clear signature fields that will be later set by server once it's able to parse the content s.clearSignatureDetails(newImage) }
// 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) }() }
// 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) utilruntime.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 } utilruntime.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) }, } }