// ReconcileEndpoints sets the endpoints for the given apiserver service (ro or rw). // ReconcileEndpoints expects that the endpoints objects it manages will all be // managed only by ReconcileEndpoints; therefore, to understand this, you need only // understand the requirements and the body of this function. // // Requirements: // * All apiservers MUST use the same ports for their {rw, ro} services. // * All apiservers MUST use ReconcileEndpoints and only ReconcileEndpoints to manage the // endpoints for their {rw, ro} services. // * All apiservers MUST know and agree on the number of apiservers expected // to be running (c.masterCount). // * ReconcileEndpoints is called periodically from all apiservers. // func (c *Controller) ReconcileEndpoints(serviceName string, ip net.IP, endpointPorts []api.EndpointPort, reconcilePorts bool) error { ctx := api.NewDefaultContext() e, err := c.EndpointRegistry.GetEndpoints(ctx, serviceName) if err != nil { e = &api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: serviceName, Namespace: api.NamespaceDefault, Tenant: api.TenantDefault, }, } } // First, determine if the endpoint is in the format we expect (one // subset, ports matching endpointPorts, N IP addresses). formatCorrect, ipCorrect, portsCorrect := checkEndpointSubsetFormat(e, ip.String(), endpointPorts, c.MasterCount, reconcilePorts) if !formatCorrect { // Something is egregiously wrong, just re-make the endpoints record. e.Subsets = []api.EndpointSubset{{ Addresses: []api.EndpointAddress{{IP: ip.String()}}, Ports: endpointPorts, }} glog.Warningf("Resetting endpoints for master service %q to %v", serviceName, e) return c.EndpointRegistry.UpdateEndpoints(ctx, e) } if ipCorrect && portsCorrect { return nil } if !ipCorrect { // We *always* add our own IP address. e.Subsets[0].Addresses = append(e.Subsets[0].Addresses, api.EndpointAddress{IP: ip.String()}) // Lexicographic order is retained by this step. e.Subsets = endpoints.RepackSubsets(e.Subsets) // If too many IP addresses, remove the ones lexicographically after our // own IP address. Given the requirements stated at the top of // this function, this should cause the list of IP addresses to // become eventually correct. if addrs := &e.Subsets[0].Addresses; len(*addrs) > c.MasterCount { // addrs is a pointer because we're going to mutate it. for i, addr := range *addrs { if addr.IP == ip.String() { for len(*addrs) > c.MasterCount { // wrap around if necessary. remove := (i + 1) % len(*addrs) *addrs = append((*addrs)[:remove], (*addrs)[remove+1:]...) } break } } } } if !portsCorrect { // Reset ports. e.Subsets[0].Ports = endpointPorts } glog.Warningf("Resetting endpoints for master service %q to %v", serviceName, e) return c.EndpointRegistry.UpdateEndpoints(ctx, e) }
// SetEndpoints sets the endpoints for the given apiserver service (ro or rw). // SetEndpoints expects that the endpoints objects it manages will all be // managed only by SetEndpoints; therefore, to understand this, you need only // understand the requirements and the body of this function. // // Requirements: // * All apiservers MUST use the same ports for their {rw, ro} services. // * All apiservers MUST use SetEndpoints and only SetEndpoints to manage the // endpoints for their {rw, ro} services. // * All apiservers MUST know and agree on the number of apiservers expected // to be running (c.masterCount). // * SetEndpoints is called periodically from all apiservers. // func (c *Controller) SetEndpoints(serviceName string, ip net.IP, port int) error { ctx := api.NewDefaultContext() e, err := c.EndpointRegistry.GetEndpoints(ctx, serviceName) if err != nil { e = &api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: serviceName, Namespace: api.NamespaceDefault, }, } } // First, determine if the endpoint is in the format we expect (one // subset, one port, N IP addresses). formatCorrect, ipCorrect := checkEndpointSubsetFormat(e, ip.String(), port, c.MasterCount) if !formatCorrect { // Something is egregiously wrong, just re-make the endpoints record. e.Subsets = []api.EndpointSubset{{ Addresses: []api.EndpointAddress{{IP: ip.String()}}, Ports: []api.EndpointPort{{Port: port, Protocol: api.ProtocolTCP}}, }} glog.Warningf("Resetting endpoints for master service %q to %v", serviceName, e) return c.EndpointRegistry.UpdateEndpoints(ctx, e) } else if !ipCorrect { // We *always* add our own IP address; if there are too many IP // addresses, we remove the ones lexicographically after our // own IP address. Given the requirements stated at the top of // this function, this should cause the list of IP addresses to // become eventually correct. e.Subsets[0].Addresses = append(e.Subsets[0].Addresses, api.EndpointAddress{IP: ip.String()}) e.Subsets = endpoints.RepackSubsets(e.Subsets) if addrs := &e.Subsets[0].Addresses; len(*addrs) > c.MasterCount { // addrs is a pointer because we're going to mutate it. for i, addr := range *addrs { if addr.IP == ip.String() { for len(*addrs) > c.MasterCount { remove := (i + 1) % len(*addrs) *addrs = append((*addrs)[:remove], (*addrs)[remove+1:]...) } break } } } return c.EndpointRegistry.UpdateEndpoints(ctx, e) } // We didn't make any changes, no need to actually call update. return nil }
// Canonicalize normalizes the object after validation. func (endpointsStrategy) Canonicalize(obj runtime.Object) { endpoints := obj.(*api.Endpoints) endpoints.Subsets = endptspkg.RepackSubsets(endpoints.Subsets) }
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)) }() if !e.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 service %v", key) e.queue.Add(key) return } 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, nil) 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{} podHostNames := map[string]endpoints.HostRecord{} var tolerateUnreadyEndpoints bool if v, ok := service.Annotations[TolerateUnreadyEndpointsAnnotation]; ok { b, err := strconv.ParseBool(v) if err == nil { tolerateUnreadyEndpoints = b } else { glog.Errorf("Failed to parse annotation %v: %v", TolerateUnreadyEndpointsAnnotation, err) } } 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 := podutil.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 } if len(pod.Status.PodIP) == 0 { glog.V(5).Infof("Failed to find an IP for pod %s/%s", pod.Namespace, pod.Name) continue } if pod.DeletionTimestamp != nil { glog.V(5).Infof("Pod is being deleted %s/%s", pod.Namespace, pod.Name) continue } epp := api.EndpointPort{Name: portName, Port: int32(portNum), Protocol: portProto} epa := api.EndpointAddress{ IP: pod.Status.PodIP, TargetRef: &api.ObjectReference{ Kind: "Pod", Namespace: pod.ObjectMeta.Namespace, Name: pod.ObjectMeta.Name, UID: pod.ObjectMeta.UID, ResourceVersion: pod.ObjectMeta.ResourceVersion, }} hostname := getHostname(pod) if len(hostname) > 0 && getSubdomain(pod) == service.Name && service.Namespace == pod.Namespace { hostRecord := endpoints.HostRecord{ HostName: hostname, } // TODO: stop populating podHostNames annotation in 1.4 podHostNames[string(pod.Status.PodIP)] = hostRecord epa.Hostname = hostname } if tolerateUnreadyEndpoints || api.IsPodReady(pod) { subsets = append(subsets, api.EndpointSubset{ Addresses: []api.EndpointAddress{epa}, Ports: []api.EndpointPort{epp}, }) } else { glog.V(5).Infof("Pod is out of service: %v/%v", pod.Namespace, pod.Name) subsets = append(subsets, api.EndpointSubset{ NotReadyAddresses: []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 } } serializedPodHostNames := "" if len(podHostNames) > 0 { b, err := json.Marshal(podHostNames) if err != nil { glog.Errorf("Error updating endpoints. Marshalling of hostnames failed.: %v", err) e.queue.Add(key) // Retry return } serializedPodHostNames = string(b) } newAnnotations := make(map[string]string) newAnnotations[endpoints.PodHostnamesAnnotation] = serializedPodHostNames 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 newEndpoints.Annotations == nil { newEndpoints.Annotations = make(map[string]string) } if len(serializedPodHostNames) == 0 { delete(newEndpoints.Annotations, endpoints.PodHostnamesAnnotation) } else { newEndpoints.Annotations[endpoints.PodHostnamesAnnotation] = serializedPodHostNames } 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 } }
func (e *EndpointController) syncService(key string) error { 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.Indexer.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 { utilruntime.HandleError(fmt.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 nil } err = e.client.Core().Endpoints(namespace).Delete(name, nil) if err != nil && !errors.IsNotFound(err) { return err } return nil } 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 nil } 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).AsSelectorPreValidated()) if err != nil { // Since we're getting stuff from a local cache, it is // basically impossible to get this error. return err } subsets := []api.EndpointSubset{} podHostNames := map[string]endpoints.HostRecord{} var tolerateUnreadyEndpoints bool if v, ok := service.Annotations[TolerateUnreadyEndpointsAnnotation]; ok { b, err := strconv.ParseBool(v) if err == nil { tolerateUnreadyEndpoints = b } else { utilruntime.HandleError(fmt.Errorf("Failed to parse annotation %v: %v", TolerateUnreadyEndpointsAnnotation, err)) } } readyEps := 0 notReadyEps := 0 for i := range pods { // TODO: Do we need to copy here? pod := &(*pods[i]) for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] portName := servicePort.Name portProto := servicePort.Protocol portNum, err := podutil.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 } if len(pod.Status.PodIP) == 0 { glog.V(5).Infof("Failed to find an IP for pod %s/%s", pod.Namespace, pod.Name) continue } if pod.DeletionTimestamp != nil { glog.V(5).Infof("Pod is being deleted %s/%s", pod.Namespace, pod.Name) continue } epp := api.EndpointPort{Name: portName, Port: int32(portNum), Protocol: portProto} epa := api.EndpointAddress{ IP: pod.Status.PodIP, NodeName: &pod.Spec.NodeName, TargetRef: &api.ObjectReference{ Kind: "Pod", Namespace: pod.ObjectMeta.Namespace, Name: pod.ObjectMeta.Name, UID: pod.ObjectMeta.UID, ResourceVersion: pod.ObjectMeta.ResourceVersion, }} hostname := getHostname(pod) if len(hostname) > 0 && getSubdomain(pod) == service.Name && service.Namespace == pod.Namespace { hostRecord := endpoints.HostRecord{ HostName: hostname, } // TODO: stop populating podHostNames annotation in 1.4 podHostNames[string(pod.Status.PodIP)] = hostRecord epa.Hostname = hostname } if tolerateUnreadyEndpoints || api.IsPodReady(pod) { subsets = append(subsets, api.EndpointSubset{ Addresses: []api.EndpointAddress{epa}, Ports: []api.EndpointPort{epp}, }) readyEps++ } else { glog.V(5).Infof("Pod is out of service: %v/%v", pod.Namespace, pod.Name) subsets = append(subsets, api.EndpointSubset{ NotReadyAddresses: []api.EndpointAddress{epa}, Ports: []api.EndpointPort{epp}, }) notReadyEps++ } } } subsets = endpoints.RepackSubsets(subsets) // See if there's actually an update here. currentEndpoints, err := e.client.Core().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 { return err } } serializedPodHostNames := "" if len(podHostNames) > 0 { b, err := json.Marshal(podHostNames) if err != nil { return err } serializedPodHostNames = string(b) } newAnnotations := make(map[string]string) newAnnotations[endpoints.PodHostnamesAnnotation] = serializedPodHostNames 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 nil } newEndpoints := currentEndpoints newEndpoints.Subsets = subsets newEndpoints.Labels = service.Labels if newEndpoints.Annotations == nil { newEndpoints.Annotations = make(map[string]string) } if len(serializedPodHostNames) == 0 { delete(newEndpoints.Annotations, endpoints.PodHostnamesAnnotation) } else { newEndpoints.Annotations[endpoints.PodHostnamesAnnotation] = serializedPodHostNames } glog.V(4).Infof("Update endpoints for %v/%v, ready: %d not ready: %d", service.Namespace, service.Name, readyEps, notReadyEps) createEndpoints := len(currentEndpoints.ResourceVersion) == 0 if createEndpoints { // No previous endpoints, create them _, err = e.client.Core().Endpoints(service.Namespace).Create(newEndpoints) } else { // Pre-existing _, err = e.client.Core().Endpoints(service.Namespace).Update(newEndpoints) } if err != nil { if createEndpoints && errors.IsForbidden(err) { // A request is forbidden primarily for two reasons: // 1. namespace is terminating, endpoint creation is not allowed by default. // 2. policy is misconfigured, in which case no service would function anywhere. // Given the frequency of 1, we log at a lower level. glog.V(5).Infof("Forbidden from creating endpoints: %v", err) } return err } return 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 } if len(pod.Status.PodIP) == 0 { glog.V(5).Infof("Failed to find an IP for pod %s/%s", pod.Namespace, pod.Name) continue } if pod.DeletionTimestamp != nil { glog.V(5).Infof("Pod is being deleted %s/%s", pod.Namespace, pod.Name) continue } epp := api.EndpointPort{Name: portName, Port: portNum, Protocol: portProto} epa := api.EndpointAddress{IP: pod.Status.PodIP, TargetRef: &api.ObjectReference{ Kind: "Pod", Namespace: pod.ObjectMeta.Namespace, Name: pod.ObjectMeta.Name, UID: pod.ObjectMeta.UID, ResourceVersion: pod.ObjectMeta.ResourceVersion, }} if api.IsPodReady(pod) { subsets = append(subsets, api.EndpointSubset{ Addresses: []api.EndpointAddress{epa}, Ports: []api.EndpointPort{epp}, }) } else { glog.V(5).Infof("Pod is out of service: %v/%v", pod.Namespace, pod.Name) subsets = append(subsets, api.EndpointSubset{ NotReadyAddresses: []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 } }
// HACK(sttts): add annotations to the endpoint about the respective container ports func (e *endpointController) syncService(key string) error { 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 { utilruntime.HandleError(fmt.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 nil } err = e.client.Endpoints(namespace).Delete(name, nil) if err != nil && !errors.IsNotFound(err) { utilruntime.HandleError(fmt.Errorf("Error deleting endpoint %q: %v", key, err)) return err } return nil } 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 nil } 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. utilruntime.HandleError(fmt.Errorf("Error syncing service %q: %v", key, err)) return err } subsets := []api.EndpointSubset{} containerPortAnnotations := map[string]string{} // by <HostIP>:<Port> for i := range pods { // TODO: Do we need to copy here? pod := &(*pods[i]) for i := range service.Spec.Ports { servicePort := &service.Spec.Ports[i] portName := servicePort.Name portProto := servicePort.Protocol portNum, containerPort, 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 pod.DeletionTimestamp != nil { glog.V(5).Infof("Pod is being deleted %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: int32(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}}) containerPortAnnotations[fmt.Sprintf(meta.ContainerPortKeyFormat, portProto, pod.Status.HostIP, portNum)] = strconv.Itoa(containerPort) } } 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 { utilruntime.HandleError(fmt.Errorf("Error getting endpoints: %v", err)) return err } } 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 nil } newEndpoints := currentEndpoints newEndpoints.Subsets = subsets newEndpoints.Labels = service.Labels if newEndpoints.Annotations == nil { newEndpoints.Annotations = map[string]string{} } for hostIpPort, containerPort := range containerPortAnnotations { newEndpoints.Annotations[hostIpPort] = containerPort } 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 { utilruntime.HandleError(fmt.Errorf("Error updating endpoints: %v", err)) return err } return nil }
// PrepareForUpdate clears fields that are not allowed to be set by end users on update. func (endpointsStrategy) PrepareForUpdate(obj, old runtime.Object) { newEndpoints := obj.(*api.Endpoints) _ = old.(*api.Endpoints) newEndpoints.Subsets = endptspkg.RepackSubsets(newEndpoints.Subsets) }
// PrepareForCreate clears fields that are not allowed to be set by end users on creation. func (endpointsStrategy) PrepareForCreate(obj runtime.Object) { endpoints := obj.(*api.Endpoints) endpoints.Subsets = endptspkg.RepackSubsets(endpoints.Subsets) }
// ReconcileEndpoints lists keys in a special etcd directory. // Each key is expected to have a TTL of R+n, where R is the refresh interval // at which this function is called, and n is some small value. If an // apiserver goes down, it will fail to refresh its key's TTL and the key will // expire. ReconcileEndpoints will notice that the endpoints object is // different from the directory listing, and update the endpoints object // accordingly. func (r *leaseEndpointReconciler) ReconcileEndpoints(serviceName string, ip net.IP, endpointPorts []api.EndpointPort, reconcilePorts bool) error { ctx := api.NewDefaultContext() // Refresh the TTL on our key, independently of whether any error or // update conflict happens below. This makes sure that at least some of // the masters will add our endpoint. if err := r.masterLeases.UpdateLease(ip.String()); err != nil { return err } // Retrieve the current list of endpoints... e, err := r.endpointRegistry.GetEndpoints(ctx, serviceName) if err != nil { if !errors.IsNotFound(err) { return err } e = &api.Endpoints{ ObjectMeta: api.ObjectMeta{ Name: serviceName, Namespace: api.NamespaceDefault, }, } } // ... and the list of master IP keys from etcd masterIPs, err := r.masterLeases.ListLeases() if err != nil { return err } // Since we just refreshed our own key, assume that zero endpoints // returned from storage indicates an issue or invalid state, and thus do // not update the endpoints list based on the result. if len(masterIPs) == 0 { return fmt.Errorf("no master IPs were listed in storage, refusing to erase all endpoints for the kubernetes service") } // Next, we compare the current list of endpoints with the list of master IP keys formatCorrect, ipCorrect, portsCorrect := checkEndpointSubsetFormatWithLease(e, masterIPs, endpointPorts, reconcilePorts) if formatCorrect && ipCorrect && portsCorrect { return nil } if !formatCorrect { // Something is egregiously wrong, just re-make the endpoints record. e.Subsets = []api.EndpointSubset{{ Addresses: []api.EndpointAddress{}, Ports: endpointPorts, }} } if !formatCorrect || !ipCorrect { // repopulate the addresses according to the expected IPs from etcd e.Subsets[0].Addresses = make([]api.EndpointAddress, len(masterIPs)) for ind, ip := range masterIPs { e.Subsets[0].Addresses[ind] = api.EndpointAddress{IP: ip} } // Lexicographic order is retained by this step. e.Subsets = endpoints.RepackSubsets(e.Subsets) } if !portsCorrect { // Reset ports. e.Subsets[0].Ports = endpointPorts } glog.Warningf("Resetting endpoints for master service %q to %v", serviceName, masterIPs) return r.endpointRegistry.UpdateEndpoints(ctx, e) }