// TODO: Move this back to scheduler as a helper function that takes a Store, // rather than a method of StoreToServiceLister. func (s *StoreToServiceLister) GetPodServices(pod *api.Pod) (services []api.Service, err error) { var selector labels.Selector var service api.Service for _, m := range s.Store.List() { service = *m.(*api.Service) // consider only services that are in the same namespace as the pod if service.Namespace != pod.Namespace { continue } if service.Spec.Selector == nil { // services with nil selectors match nothing, not everything. continue } selector = labels.Set(service.Spec.Selector).AsSelector() if selector.Matches(labels.Set(pod.Labels)) { services = append(services, service) } } if len(services) == 0 { err = fmt.Errorf("Could not find service for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
// WatchControllers begins watching for new, changed, or deleted controllers. func (r *Registry) WatchControllers(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) { if !field.Empty() { return nil, fmt.Errorf("field selectors are not supported on replication controllers") } version, err := tools.ParseWatchResourceVersion(resourceVersion, "replicationControllers") if err != nil { return nil, err } key := makeControllerListKey(ctx) return r.WatchList(key, version, func(obj runtime.Object) bool { controller, ok := obj.(*api.ReplicationController) if !ok { // Must be an error: return true to propagate to upper level. return true } match := label.Matches(labels.Set(controller.Labels)) if match { pods, err := r.pods.ListPods(ctx, labels.Set(controller.Spec.Selector).AsSelector()) if err != nil { glog.Warningf("Error listing pods: %v", err) // No object that's useable so drop it on the floor return false } if pods == nil { glog.Warningf("Pods list is nil. This should never happen...") // No object that's useable so drop it on the floor return false } controller.Status.Replicas = len(pods.Items) } return match }) }
// GetPodControllers returns a list of controllers managing a pod. Returns an error only if no matching controllers are found. func (s *StoreToControllerLister) GetPodControllers(pod *api.Pod) (controllers []api.ReplicationController, err error) { var selector labels.Selector var rc api.ReplicationController if len(pod.Labels) == 0 { err = fmt.Errorf("No controllers found for pod %v because it has no labels", pod.Name) return } for _, m := range s.Store.List() { rc = *m.(*api.ReplicationController) if rc.Namespace != pod.Namespace { continue } labelSet := labels.Set(rc.Spec.Selector) selector = labels.Set(rc.Spec.Selector).AsSelector() // If an rc with a nil or empty selector creeps in, it should match nothing, not everything. if labelSet.AsSelector().Empty() || !selector.Matches(labels.Set(pod.Labels)) { continue } controllers = append(controllers, rc) } if len(controllers) == 0 { err = fmt.Errorf("Could not find controllers for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
func MappingTypeForPod(pod *api.Pod) HostPortMappingType { filter := map[string]string{ PortMappingLabelKey: string(HostPortMappingFixed), } selector := labels.Set(filter).AsSelector() if selector.Matches(labels.Set(pod.Labels)) { return HostPortMappingFixed } return HostPortMappingWildcard }
func verifyExpectedRcsExistAndGetExpectedPods(c *client.Client) ([]string, error) { expectedPods := []string{} // Iterate over the labels that identify the replication controllers that we // want to check. The rcLabels contains the value values for the k8s-app key // that identify the replication controllers that we want to check. Using a label // rather than an explicit name is preferred because the names will typically have // a version suffix e.g. heapster-monitoring-v1 and this will change after a rolling // update e.g. to heapster-monitoring-v2. By using a label query we can check for the // situaiton when a heapster-monitoring-v1 and heapster-monitoring-v2 replication controller // is running (which would be an error except during a rolling update). for _, rcLabel := range rcLabels { rcList, err := c.ReplicationControllers(api.NamespaceDefault).List(labels.Set{"k8s-app": rcLabel}.AsSelector()) if err != nil { return nil, err } if len(rcList.Items) != 1 { return nil, fmt.Errorf("expected to find one replica for RC with label %s but got %d", rcLabel, len(rcList.Items)) } for _, rc := range rcList.Items { podList, err := c.Pods(api.NamespaceDefault).List(labels.Set(rc.Spec.Selector).AsSelector(), fields.Everything()) if err != nil { return nil, err } for _, pod := range podList.Items { expectedPods = append(expectedPods, string(pod.UID)) } } } return expectedPods, nil }
// TODO Move to labels package. func formatLabels(labelMap map[string]string) string { l := labels.Set(labelMap).String() if l == "" { l = "<none>" } return l }
func (d *PodDescriber) Describe(namespace, name string) (string, error) { rc := d.ReplicationControllers(namespace) pc := d.Pods(namespace) pod, err := pc.Get(name) if err != nil { eventsInterface := d.Events(namespace) events, err2 := eventsInterface.List( labels.Everything(), eventsInterface.GetFieldSelector(&name, &namespace, nil, nil)) if err2 == nil && len(events.Items) > 0 { return tabbedString(func(out io.Writer) error { fmt.Fprintf(out, "Pod '%v': error '%v', but found events.\n", name, err) DescribeEvents(events, out) return nil }) } return "", err } var events *api.EventList if ref, err := api.GetReference(pod); err != nil { glog.Errorf("Unable to construct reference to '%#v': %v", pod, err) } else { ref.Kind = "" events, _ = d.Events(namespace).Search(ref) } rcs, err := getReplicationControllersForLabels(rc, labels.Set(pod.Labels)) if err != nil { return "", err } return describePod(pod, rcs, events) }
// CalculateNodeLabelPriority checks whether a particular label exists on a minion or not, regardless of its value. // If presence is true, prioritizes minions that have the specified label, regardless of value. // If presence is false, prioritizes minions that do not have the specified label. func (n *NodeLabelPrioritizer) CalculateNodeLabelPriority(pod *api.Pod, podLister algorithm.PodLister, minionLister algorithm.MinionLister) (algorithm.HostPriorityList, error) { var score int minions, err := minionLister.List() if err != nil { return nil, err } labeledMinions := map[string]bool{} for _, minion := range minions.Items { exists := labels.Set(minion.Labels).Has(n.label) labeledMinions[minion.Name] = (exists && n.presence) || (!exists && !n.presence) } result := []algorithm.HostPriority{} //score int - scale of 0-10 // 0 being the lowest priority and 10 being the highest for minionName, success := range labeledMinions { if success { score = 10 } else { score = 0 } result = append(result, algorithm.HostPriority{Host: minionName, Score: score}) } return result, nil }
func PodMatchesNodeLabels(pod *api.Pod, node *api.Node) bool { if len(pod.Spec.NodeSelector) == 0 { return true } selector := labels.SelectorFromSet(pod.Spec.NodeSelector) return selector.Matches(labels.Set(node.Labels)) }
// List returns []*api.Pod matching a query. func (f FakePodLister) List(s labels.Selector) (selected []*api.Pod, err error) { for _, pod := range f { if s.Matches(labels.Set(pod.Labels)) { selected = append(selected, pod) } } return selected, nil }
// MatchPodTemplate returns a generic matcher for a given label and field selector. func MatchPodTemplate(label labels.Selector, field fields.Selector) generic.Matcher { return generic.MatcherFunc(func(obj runtime.Object) (bool, error) { podObj, ok := obj.(*api.PodTemplate) if !ok { return false, fmt.Errorf("not a pod template") } return label.Matches(labels.Set(podObj.Labels)), nil }) }
// Matcher returns a generic matcher for a given label and field selector. func Matcher(label labels.Selector, field fields.Selector) generic.Matcher { return generic.MatcherFunc(func(obj runtime.Object) (bool, error) { sa, ok := obj.(*api.Secret) if !ok { return false, fmt.Errorf("not a secret") } fields := SelectableFields(sa) return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil }) }
// MatchPersistentVolume returns a generic matcher for a given label and field selector. func MatchPersistentVolumes(label labels.Selector, field fields.Selector) generic.Matcher { return generic.MatcherFunc(func(obj runtime.Object) (bool, error) { persistentvolumeObj, ok := obj.(*api.PersistentVolume) if !ok { return false, fmt.Errorf("not a persistentvolume") } fields := PersistentVolumeToSelectableFields(persistentvolumeObj) return label.Matches(labels.Set(persistentvolumeObj.Labels)) && field.Matches(fields), nil }) }
// MatchNamespace returns a generic matcher for a given label and field selector. func MatchNamespace(label labels.Selector, field fields.Selector) generic.Matcher { return generic.MatcherFunc(func(obj runtime.Object) (bool, error) { namespaceObj, ok := obj.(*api.Namespace) if !ok { return false, fmt.Errorf("not a namespace") } fields := NamespaceToSelectableFields(namespaceObj) return label.Matches(labels.Set(namespaceObj.Labels)) && field.Matches(fields), nil }) }
// GetPodServices gets the services that have the selector that match the labels on the given pod func (f FakeServiceLister) GetPodServices(pod *api.Pod) (services []api.Service, err error) { var selector labels.Selector for _, service := range f { // consider only services that are in the same namespace as the pod if service.Namespace != pod.Namespace { continue } selector = labels.Set(service.Spec.Selector).AsSelector() if selector.Matches(labels.Set(pod.Labels)) { services = append(services, service) } } if len(services) == 0 { err = fmt.Errorf("Could not find service for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
// MatchResourceQuota returns a generic matcher for a given label and field selector. func MatchResourceQuota(label labels.Selector, field fields.Selector) generic.Matcher { return generic.MatcherFunc(func(obj runtime.Object) (bool, error) { resourcequotaObj, ok := obj.(*api.ResourceQuota) if !ok { return false, fmt.Errorf("not a resourcequota") } fields := ResourceQuotaToSelectableFields(resourcequotaObj) return label.Matches(labels.Set(resourcequotaObj.Labels)) && field.Matches(fields), nil }) }
// Please note that selector is filtering among the pods that have gotten into // the store; there may have been some filtering that already happened before // that. func (s storePodsNamespacer) List(selector labels.Selector) (pods api.PodList, err error) { list := api.PodList{} for _, m := range s.store.List() { pod := m.(*api.Pod) if s.namespace == api.NamespaceAll || s.namespace == pod.Namespace { if selector.Matches(labels.Set(pod.Labels)) { list.Items = append(list.Items, *pod) } } } return list, nil }
// syncReplicationController will sync the rc with the given key if it has had its expectations fulfilled, meaning // it did not expect to see any more of its pods created or deleted. This function is not meant to be invoked // concurrently with the same key. func (rm *ReplicationManager) syncReplicationController(key string) error { startTime := time.Now() defer func() { glog.V(4).Infof("Finished syncing controller %q (%v)", key, time.Now().Sub(startTime)) }() obj, exists, err := rm.controllerStore.Store.GetByKey(key) if !exists { glog.Infof("Replication Controller has been deleted %v", key) rm.expectations.DeleteExpectations(key) return nil } if err != nil { glog.Infof("Unable to retrieve rc %v from store: %v", key, err) rm.queue.Add(key) return err } controller := *obj.(*api.ReplicationController) if !rm.podStoreSynced() { // Sleep so we give the pod reflector goroutine a chance to run. time.Sleep(PodStoreSyncedPollPeriod) glog.Infof("Waiting for pods controller to sync, requeuing rc %v", controller.Name) rm.enqueueController(&controller) return nil } // Check the expectations of the rc before counting active pods, otherwise a new pod can sneak in // and update the expectations after we've retrieved active pods from the store. If a new pod enters // the store after we've checked the expectation, the rc sync is just deferred till the next relist. rcNeedsSync := rm.expectations.SatisfiedExpectations(&controller) podList, err := rm.podStore.Pods(controller.Namespace).List(labels.Set(controller.Spec.Selector).AsSelector()) if err != nil { glog.Errorf("Error getting pods for rc %q: %v", key, err) rm.queue.Add(key) return err } // TODO: Do this in a single pass, or use an index. filteredPods := filterActivePods(podList.Items) if rcNeedsSync { rm.manageReplicas(filteredPods, &controller) } // Always updates status as pods come up or die. if err := updateReplicaCount(rm.qingClient.ReplicationControllers(controller.Namespace), controller, len(filteredPods)); err != nil { // Multiple things could lead to this update failing. Requeuing the controller ensures // we retry with some fairness. glog.V(2).Infof("Failed to update replica count for controller %v, requeuing", controller.Name) rm.enqueueController(&controller) } return nil }
// MatchNode returns a generic matcher for a given label and field selector. func MatchNode(label labels.Selector, field fields.Selector) generic.Matcher { return &generic.SelectionPredicate{ Label: label, Field: field, GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { nodeObj, ok := obj.(*api.Node) if !ok { return nil, nil, fmt.Errorf("not a node") } return labels.Set(nodeObj.ObjectMeta.Labels), NodeToSelectableFields(nodeObj), nil }, } }
// Scales RC to a random size within [0.5*size, 1.5*size] and lists all the pods afterwards. // Scaling happens always based on original size, not the current size. func scaleRC(wg *sync.WaitGroup, config *RCConfig) { defer GinkgoRecover() defer wg.Done() resizingTime := 3 * time.Minute sleepUpTo(resizingTime) newSize := uint(rand.Intn(config.Replicas) + config.Replicas/2) expectNoError(ScaleRC(config.Client, config.Namespace, config.Name, newSize), fmt.Sprintf("scaling rc %s for the first time", config.Name)) selector := labels.SelectorFromSet(labels.Set(map[string]string{"name": config.Name})) _, err := config.Client.Pods(config.Namespace).List(selector, fields.Everything()) expectNoError(err, fmt.Sprintf("listing pods from rc %v", config.Name)) }
// MatchController is the filter used by the generic etcd backend to route // watch events from etcd to clients of the apiserver only interested in specific // labels/fields. func MatchController(label labels.Selector, field fields.Selector) generic.Matcher { return &generic.SelectionPredicate{ Label: label, Field: field, GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { rc, ok := obj.(*api.ReplicationController) if !ok { return nil, nil, fmt.Errorf("Given object is not a replication controller.") } return labels.Set(rc.ObjectMeta.Labels), ControllerToSelectableFields(rc), nil }, } }
// Please note that selector is filtering among the pods that have gotten into // the store; there may have been some filtering that already happened before // that. // // TODO: converge on the interface in pkg/client. func (s *StoreToPodLister) List(selector labels.Selector) (pods []*api.Pod, err error) { // TODO: it'd be great to just call // s.Pods(api.NamespaceAll).List(selector), however then we'd have to // remake the list.Items as a []*api.Pod. So leave this separate for // now. for _, m := range s.Store.List() { pod := m.(*api.Pod) if selector.Matches(labels.Set(pod.Labels)) { pods = append(pods, pod) } } return pods, nil }
// MatchPod returns a generic matcher for a given label and field selector. func MatchPod(label labels.Selector, field fields.Selector) generic.Matcher { return &generic.SelectionPredicate{ Label: label, Field: field, GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { pod, ok := obj.(*api.Pod) if !ok { return nil, nil, fmt.Errorf("not a pod") } return labels.Set(pod.ObjectMeta.Labels), PodToSelectableFields(pod), nil }, } }
func (r RealPodControl) createReplica(namespace string, controller *api.ReplicationController) error { desiredLabels := make(labels.Set) for k, v := range controller.Spec.Template.Labels { desiredLabels[k] = v } desiredAnnotations := make(labels.Set) for k, v := range controller.Spec.Template.Annotations { desiredAnnotations[k] = v } createdByRef, err := api.GetReference(controller) if err != nil { return fmt.Errorf("unable to get controller reference: %v", err) } createdByRefJson, err := latest.Codec.Encode(&api.SerializedReference{ Reference: *createdByRef, }) if err != nil { return fmt.Errorf("unable to serialize controller reference: %v", err) } desiredAnnotations[CreatedByAnnotation] = string(createdByRefJson) // use the dash (if the name isn't too long) to make the pod name a bit prettier prefix := fmt.Sprintf("%s-", controller.Name) if ok, _ := validation.ValidatePodName(prefix, true); !ok { prefix = controller.Name } pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ Labels: desiredLabels, Annotations: desiredAnnotations, GenerateName: prefix, }, } if err := api.Scheme.Convert(&controller.Spec.Template.Spec, &pod.Spec); err != nil { return fmt.Errorf("unable to convert pod template: %v", err) } if labels.Set(pod.Labels).AsSelector().Empty() { return fmt.Errorf("unable to create pod replica, no labels") } if newPod, err := r.qingClient.Pods(namespace).Create(pod); err != nil { r.recorder.Eventf(controller, "failedCreate", "Error creating: %v", err) return fmt.Errorf("unable to create pod replica: %v", err) } else { glog.V(4).Infof("Controller %v created pod %v", controller.Name, newPod.Name) r.recorder.Eventf(controller, "successfulCreate", "Created pod: %v", newPod.Name) } return nil }
func (rs *REST) List(ctx api.Context, label labels.Selector, field fields.Selector) (runtime.Object, error) { list, err := rs.registry.ListServices(ctx) if err != nil { return nil, err } var filtered []api.Service for _, service := range list.Items { if label.Matches(labels.Set(service.Labels)) { filtered = append(filtered, service) } } list.Items = filtered return list, err }
func TestEtcdListControllersLabelsMatch(t *testing.T) { storage, fakeClient := newStorage(t) ctx := api.NewDefaultContext() key := makeControllerListKey(ctx) key = etcdtest.AddPrefix(key) controller := validController controller.Labels = map[string]string{"k": "v"} controller.Name = "bar" fakeClient.Data[key] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ Nodes: []*etcd.Node{ { Value: runtime.EncodeOrDie(latest.Codec, &validController), }, { Value: runtime.EncodeOrDie(latest.Codec, &controller), }, }, }, }, E: nil, } testLabels := labels.SelectorFromSet(labels.Set(controller.Labels)) objList, err := storage.List(ctx, testLabels, fields.Everything()) if err != nil { t.Errorf("unexpected error: %v", err) } controllers, _ := objList.(*api.ReplicationControllerList) if len(controllers.Items) != 1 || controllers.Items[0].Name != controller.Name || !testLabels.Matches(labels.Set(controllers.Items[0].Labels)) { t.Errorf("Unexpected controller list: %#v for query with labels %#v", controllers, testLabels) } }
// CheckNodeLabelPresence checks whether all of the specified labels exists on a minion or not, regardless of their value // If "presence" is false, then returns false if any of the requested labels matches any of the minion's labels, // otherwise returns true. // If "presence" is true, then returns false if any of the requested labels does not match any of the minion's labels, // otherwise returns true. // // Consider the cases where the minions are placed in regions/zones/racks and these are identified by labels // In some cases, it is required that only minions that are part of ANY of the defined regions/zones/racks be selected // // Alternately, eliminating minions that have a certain label, regardless of value, is also useful // A minion may have a label with "retiring" as key and the date as the value // and it may be desirable to avoid scheduling new pods on this minion func (n *NodeLabelChecker) CheckNodeLabelPresence(pod *api.Pod, existingPods []*api.Pod, node string) (bool, error) { var exists bool minion, err := n.info.GetNodeInfo(node) if err != nil { return false, err } minionLabels := labels.Set(minion.Labels) for _, label := range n.labels { exists = minionLabels.Has(label) if (exists && !n.presence) || (!exists && n.presence) { return false, nil } } return true, nil }
func runReplicationControllerTest(c *client.Client) { clientAPIVersion := c.APIVersion() data, err := ioutil.ReadFile("cmd/integration/" + clientAPIVersion + "-controller.json") if err != nil { glog.Fatalf("Unexpected error: %v", err) } var controller api.ReplicationController if err := api.Scheme.DecodeInto(data, &controller); err != nil { glog.Fatalf("Unexpected error: %v", err) } glog.Infof("Creating replication controllers") updated, err := c.ReplicationControllers("test").Create(&controller) if err != nil { glog.Fatalf("Unexpected error: %v", err) } glog.Infof("Done creating replication controllers") // In practice the controller doesn't need 60s to create a handful of pods, but network latencies on CI // systems have been observed to vary unpredictably, so give the controller enough time to create pods. // Our e2e scalability tests will catch controllers that are *actually* slow. if err := wait.Poll(time.Second, time.Second*60, client.ControllerHasDesiredReplicas(c, updated)); err != nil { glog.Fatalf("FAILED: pods never created %v", err) } // Poll till we can retrieve the status of all pods matching the given label selector from their minions. // This involves 3 operations: // - The scheduler must assign all pods to a minion // - The assignment must reflect in a `List` operation against the apiserver, for labels matching the selector // - We need to be able to query the qinglet on that minion for information about the pod if err := wait.Poll( time.Second, time.Second*30, podsOnMinions(c, "test", labels.Set(updated.Spec.Selector).AsSelector())); err != nil { glog.Fatalf("FAILED: pods never started running %v", err) } glog.Infof("Pods created") }
// CheckServiceAffinity ensures that only the minions that match the specified labels are considered for scheduling. // The set of labels to be considered are provided to the struct (ServiceAffinity). // The pod is checked for the labels and any missing labels are then checked in the minion // that hosts the service pods (peers) for the given pod. // // We add an implicit selector requiring some particular value V for label L to a pod, if: // - L is listed in the ServiceAffinity object that is passed into the function // - the pod does not have any NodeSelector for L // - some other pod from the same service is already scheduled onto a minion that has value V for label L func (s *ServiceAffinity) CheckServiceAffinity(pod *api.Pod, existingPods []*api.Pod, node string) (bool, error) { var affinitySelector labels.Selector // check if the pod being scheduled has the affinity labels specified in its NodeSelector affinityLabels := map[string]string{} nodeSelector := labels.Set(pod.Spec.NodeSelector) labelsExist := true for _, l := range s.labels { if nodeSelector.Has(l) { affinityLabels[l] = nodeSelector.Get(l) } else { // the current pod does not specify all the labels, look in the existing service pods labelsExist = false } } // skip looking at other pods in the service if the current pod defines all the required affinity labels if !labelsExist { services, err := s.serviceLister.GetPodServices(pod) if err == nil { // just use the first service and get the other pods within the service // TODO: a separate predicate can be created that tries to handle all services for the pod selector := labels.SelectorFromSet(services[0].Spec.Selector) servicePods, err := s.podLister.List(selector) if err != nil { return false, err } // consider only the pods that belong to the same namespace nsServicePods := []*api.Pod{} for _, nsPod := range servicePods { if nsPod.Namespace == pod.Namespace { nsServicePods = append(nsServicePods, nsPod) } } if len(nsServicePods) > 0 { // consider any service pod and fetch the minion its hosted on otherMinion, err := s.nodeInfo.GetNodeInfo(nsServicePods[0].Spec.NodeName) if err != nil { return false, err } for _, l := range s.labels { // If the pod being scheduled has the label value specified, do not override it if _, exists := affinityLabels[l]; exists { continue } if labels.Set(otherMinion.Labels).Has(l) { affinityLabels[l] = labels.Set(otherMinion.Labels).Get(l) } } } } } // if there are no existing pods in the service, consider all minions if len(affinityLabels) == 0 { affinitySelector = labels.Everything() } else { affinitySelector = labels.Set(affinityLabels).AsSelector() } minion, err := s.nodeInfo.GetNodeInfo(node) if err != nil { return false, err } // check if the minion matches the selector return affinitySelector.Matches(labels.Set(minion.Labels)), nil }
func (e *endpointController) syncService(key string) { startTime := time.Now() defer func() { glog.V(4).Infof("Finished syncing service %q endpoints. (%v)", key, time.Now().Sub(startTime)) }() obj, exists, err := e.serviceStore.Store.GetByKey(key) if err != nil || !exists { // Delete the corresponding endpoint, as the service has been deleted. // TODO: Please note that this will delete an endpoint when a // service is deleted. However, if we're down at the time when // the service is deleted, we will miss that deletion, so this // doesn't completely solve the problem. See #6877. namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { glog.Errorf("Need to delete endpoint with key %q, but couldn't understand the key: %v", key, err) // Don't retry, as the key isn't going to magically become understandable. return } err = e.client.Endpoints(namespace).Delete(name) if err != nil && !errors.IsNotFound(err) { glog.Errorf("Error deleting endpoint %q: %v", key, err) e.queue.Add(key) // Retry } return } service := obj.(*api.Service) if service.Spec.Selector == nil { // services without a selector receive no endpoints from this controller; // these services will receive the endpoints that are created out-of-band via the REST API. return } glog.V(5).Infof("About to update endpoints for service %q", key) pods, err := e.podStore.Pods(service.Namespace).List(labels.Set(service.Spec.Selector).AsSelector()) if err != nil { // Since we're getting stuff from a local cache, it is // basically impossible to get this error. glog.Errorf("Error syncing service %q: %v", key, err) e.queue.Add(key) // Retry return } subsets := []api.EndpointSubset{} for i := range pods.Items { pod := &pods.Items[i] for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] portName := servicePort.Name portProto := servicePort.Protocol portNum, err := findPort(pod, servicePort) if err != nil { glog.V(4).Infof("Failed to find port for service %s/%s: %v", service.Namespace, service.Name, err) continue } // HACK(jdef): use HostIP instead of pod.CurrentState.PodIP for generic mesos compat if len(pod.Status.HostIP) == 0 { glog.V(4).Infof("Failed to find a host IP for pod %s/%s", pod.Namespace, pod.Name) continue } if !api.IsPodReady(pod) { glog.V(5).Infof("Pod is out of service: %v/%v", pod.Namespace, pod.Name) continue } // HACK(jdef): use HostIP instead of pod.CurrentState.PodIP for generic mesos compat epp := api.EndpointPort{Name: portName, Port: portNum, Protocol: portProto} epa := api.EndpointAddress{IP: pod.Status.HostIP, TargetRef: &api.ObjectReference{ Kind: "Pod", Namespace: pod.ObjectMeta.Namespace, Name: pod.ObjectMeta.Name, UID: pod.ObjectMeta.UID, ResourceVersion: pod.ObjectMeta.ResourceVersion, }} subsets = append(subsets, api.EndpointSubset{Addresses: []api.EndpointAddress{epa}, Ports: []api.EndpointPort{epp}}) } } subsets = endpoints.RepackSubsets(subsets) // See if there's actually an update here. currentEndpoints, err := e.client.Endpoints(service.Namespace).Get(service.Name) if err != nil { if errors.IsNotFound(err) { currentEndpoints = &api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: service.Name, Labels: service.Labels, }, } } else { glog.Errorf("Error getting endpoints: %v", err) e.queue.Add(key) // Retry return } } if reflect.DeepEqual(currentEndpoints.Subsets, subsets) && reflect.DeepEqual(currentEndpoints.Labels, service.Labels) { glog.V(5).Infof("endpoints are equal for %s/%s, skipping update", service.Namespace, service.Name) return } newEndpoints := currentEndpoints newEndpoints.Subsets = subsets newEndpoints.Labels = service.Labels if len(currentEndpoints.ResourceVersion) == 0 { // No previous endpoints, create them _, err = e.client.Endpoints(service.Namespace).Create(newEndpoints) } else { // Pre-existing _, err = e.client.Endpoints(service.Namespace).Update(newEndpoints) } if err != nil { glog.Errorf("Error updating endpoints: %v", err) e.queue.Add(key) // Retry } }