func (proxier *Proxier) sameConfig(info *serviceInfo, service *api.Service, port *api.ServicePort) bool { if info.protocol != port.Protocol || info.port != port.Port || info.nodePort != port.NodePort { return false } if !info.clusterIP.Equal(net.ParseIP(service.Spec.ClusterIP)) { return false } if !ipsEqual(info.externalIPs, service.Spec.ExternalIPs) { return false } if !api.LoadBalancerStatusEqual(&info.loadBalancerStatus, &service.Status.LoadBalancer) { return false } if info.sessionAffinityType != service.Spec.SessionAffinity { return false } return true }
// Returns whatever error occurred along with a boolean indicator of whether it // should be retried. func (s *ServiceController) createLoadBalancerIfNeeded(namespacedName types.NamespacedName, service, appliedState *api.Service) (error, bool) { if appliedState != nil && !needsUpdate(appliedState, service) { glog.Infof("LB already exists and doesn't need update for service %s", namespacedName) return nil, notRetryable } // Note: It is safe to just call EnsureTCPLoadBalancer. But, on some clouds that requires a delete & create, // which may involve service interruption. Also, we would like user-friendly events. // Save the state so we can avoid a write if it doesn't change previousState := api.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer) if !wantsExternalLoadBalancer(service) { needDelete := true if appliedState != nil { if !wantsExternalLoadBalancer(appliedState) { needDelete = false } } else { // If we don't have any cached memory of the load balancer, we have to ask // the cloud provider for what it knows about it. // Technically EnsureTCPLoadBalancerDeleted can cope, but we want to post meaningful events _, exists, err := s.balancer.GetTCPLoadBalancer(s.loadBalancerName(service), s.zone.Region) if err != nil { return fmt.Errorf("Error getting LB for service %s: %v", namespacedName, err), retryable } if !exists { needDelete = false } } if needDelete { glog.Infof("Deleting existing load balancer for service %s that no longer needs a load balancer.", namespacedName) s.eventRecorder.Event(service, "DeletingLoadBalancer", "Deleting load balancer") if err := s.balancer.EnsureTCPLoadBalancerDeleted(s.loadBalancerName(service), s.zone.Region); err != nil { return err, retryable } s.eventRecorder.Event(service, "DeletedLoadBalancer", "Deleted load balancer") } service.Status.LoadBalancer = api.LoadBalancerStatus{} } else { glog.V(2).Infof("Ensuring LB for service %s", namespacedName) // TODO: We could do a dry-run here if wanted to avoid the spurious cloud-calls & events when we restart // The load balancer doesn't exist yet, so create it. s.eventRecorder.Event(service, "CreatingLoadBalancer", "Creating load balancer") err := s.createLoadBalancer(service) if err != nil { return fmt.Errorf("Failed to create load balancer for service %s: %v", namespacedName, err), retryable } s.eventRecorder.Event(service, "CreatedLoadBalancer", "Created load balancer") } // Write the state if changed // TODO: Be careful here ... what if there were other changes to the service? if !api.LoadBalancerStatusEqual(previousState, &service.Status.LoadBalancer) { if err := s.persistUpdate(service); err != nil { return fmt.Errorf("Failed to persist updated status to apiserver, even after retries. Giving up: %v", err), notRetryable } } else { glog.V(2).Infof("Not persisting unchanged LoadBalancerStatus to registry.") } return nil, notRetryable }