// Returns whatever error occurred along with a boolean indicator of whether it // should be retried. func (s *ServiceController) createLoadBalancerIfNeeded(key string, service *api.Service) (error, bool) { // Note: It is safe to just call EnsureLoadBalancer. 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 !wantsLoadBalancer(service) { needDelete := true _, exists, err := s.balancer.GetLoadBalancer(s.clusterName, service) if err != nil { return fmt.Errorf("Error getting LB for service %s: %v", key, err), retryable } if !exists { needDelete = false } if needDelete { glog.Infof("Deleting existing load balancer for service %s that no longer needs a load balancer.", key) s.eventRecorder.Event(service, api.EventTypeNormal, "DeletingLoadBalancer", "Deleting load balancer") if err := s.balancer.EnsureLoadBalancerDeleted(s.clusterName, service); err != nil { return err, retryable } s.eventRecorder.Event(service, api.EventTypeNormal, "DeletedLoadBalancer", "Deleted load balancer") } service.Status.LoadBalancer = api.LoadBalancerStatus{} } else { glog.V(2).Infof("Ensuring LB for service %s", key) // 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, api.EventTypeNormal, "CreatingLoadBalancer", "Creating load balancer") err := s.createLoadBalancer(service) if err != nil { return fmt.Errorf("Failed to create load balancer for service %s: %v", key, err), retryable } s.eventRecorder.Event(service, api.EventTypeNormal, "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 }
// 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 && !s.needsUpdate(appliedState, service) { glog.Infof("LB doesn't need update for service %s", namespacedName) return nil, notRetryable } // Note: It is safe to just call EnsureLoadBalancer. 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 wantsLoadBalancer(service) { 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, api.EventTypeNormal, "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, api.EventTypeNormal, "CreatedLoadBalancer", "Created load balancer") } else { needDelete := true if appliedState != nil { if !wantsLoadBalancer(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 EnsureLoadBalancerDeleted can cope, but we want to post meaningful events _, exists, err := s.balancer.GetLoadBalancer(service) 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, api.EventTypeNormal, "DeletingLoadBalancer", "Deleting load balancer") if err := s.balancer.EnsureLoadBalancerDeleted(service); err != nil { return err, retryable } s.eventRecorder.Event(service, api.EventTypeNormal, "DeletedLoadBalancer", "Deleted load balancer") } service.Status.LoadBalancer = api.LoadBalancerStatus{} } // 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) { glog.V(2).Infof("Not persisting unchanged LoadBalancerStatus to registry.") } else { 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 } } return nil, notRetryable }
// Returns whatever error occurred along with a boolean indicator of whether it // should be retried. func (s *ServiceController) createLoadBalancerIfNeeded(namespacedName types.NamespacedName, service, cachedService *api.Service) (error, bool) { if cachedService != nil && !needsUpdate(cachedService, service) { glog.Infof("LB already exists and doesn't need update for service %s", namespacedName) return nil, notRetryable } if cachedService != nil { // If the service already exists but needs to be updated, delete it so that // we can recreate it cleanly. if wantsExternalLoadBalancer(cachedService) { glog.Infof("Deleting existing load balancer for service %s that needs an updated load balancer.", namespacedName) if err := s.balancer.EnsureTCPLoadBalancerDeleted(s.loadBalancerName(cachedService), s.zone.Region); err != nil { return err, retryable } } } 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. status, 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 && api.LoadBalancerStatusEqual(status, &service.Status.LoadBalancer) { glog.Infof("LB already exists with status %s for previously uncached service %s", status, namespacedName) return nil, notRetryable } else if exists { glog.Infof("Deleting old LB for previously uncached service %s whose endpoint %s doesn't match the service's desired IPs %v", namespacedName, status, service.Spec.DeprecatedPublicIPs) if err := s.balancer.EnsureTCPLoadBalancerDeleted(s.loadBalancerName(service), s.zone.Region); err != nil { return err, retryable } } } // Save the state so we can avoid a write if it doesn't change previousState := api.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer) if !wantsExternalLoadBalancer(service) { glog.Infof("Not creating LB for service %s that doesn't want one.", namespacedName) service.Status.LoadBalancer = api.LoadBalancerStatus{} } else { glog.V(2).Infof("Creating LB for service %s", namespacedName) // The load balancer doesn't exist yet, so create it. err := s.createExternalLoadBalancer(service) if err != nil { return fmt.Errorf("failed to create external load balancer for service %s: %v", namespacedName, err), retryable } } // 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.Infof("Not persisting unchanged LoadBalancerStatus to registry.") } return nil, notRetryable }