// Add a new service to the Registry. // Do not replace an existing service. func (s *ServiceRegistry) AddService(svcCfg client.ServiceConfig) error { s.Lock() defer s.Unlock() log.Debug("Adding service:", svcCfg.Name) if _, ok := s.svcs[svcCfg.Name]; ok { log.Debug("Service already exists:", svcCfg.Name) return ErrDuplicateService } s.setServiceDefaults(&svcCfg) svcCfg = svcCfg.SetDefaults() service := NewService(svcCfg) err := service.start() if err != nil { return err } s.svcs[service.Name] = service svcCfg.VirtualHosts = filterEmpty(svcCfg.VirtualHosts) for _, name := range svcCfg.VirtualHosts { vhost := s.vhosts[name] if vhost == nil { vhost = &VirtualHost{Name: name} s.vhosts[name] = vhost } vhost.Add(service) } return nil }
func writeStateConfig() { configMutex.Lock() defer configMutex.Unlock() if stateConfig == "" { log.Debug("No state file. Not saving changes") return } cfg := marshal(Registry.Config()) if len(cfg) == 0 { return } lastCfg, _ := ioutil.ReadFile(stateConfig) if bytes.Equal(cfg, lastCfg) { log.Println("No change in config") return } // We should probably write a temp file and mv for atomic update. err := ioutil.WriteFile(stateConfig, cfg, 0644) if err != nil { log.Println("Error saving config state:", err) } }
func loadConfig() { for _, cfgPath := range []string{stateConfig, defaultConfig} { if cfgPath == "" { continue } cfgData, err := ioutil.ReadFile(cfgPath) if err != nil { log.Warnln("Error reading config:", err) continue } var cfg client.Config err = json.Unmarshal(cfgData, &cfg) if err != nil { log.Warnln("Config error:", err) continue } log.Debug("Loaded config from:", cfgPath) if err := Registry.UpdateConfig(cfg); err != nil { log.Printf("Unable to load config: error: %s", err) } } }
// Periodically check the status of this backend func (b *Backend) healthCheck() { t := time.NewTicker(b.checkInterval) for { select { case <-b.stopCheck: log.Debug("Stopping backend", b.Name) t.Stop() return case <-t.C: b.check() } } }
func (b *Backend) check() { if b.CheckAddr == "" { return } up := true if c, e := net.DialTimeout("tcp", b.CheckAddr, b.dialTimeout); e == nil { c.(*net.TCPConn).SetLinger(0) c.Close() } else { log.Debug("Check error:", e) up = false } b.Lock() defer b.Unlock() if up { log.Debugf("Check OK for %s/%s", b.Name, b.CheckAddr) b.fallCount = 0 b.riseCount++ b.checkOK++ if b.riseCount >= b.rise { if !b.up { log.Debugf("Marking backend %s Up", b.Name) } b.up = true } } else { log.Debugf("Check failed for %s/%s", b.Name, b.CheckAddr) b.riseCount = 0 b.fallCount++ b.checkFail++ if b.fallCount >= b.fall { if b.up { log.Debugf("Marking backend %s Down", b.Name) } b.up = false } } }
// Replace the service's configuration, or update its list of backends. // Replacing a configuration will shutdown the existing service, and start a // new one, which will cause the listening socket to be temporarily // unavailable. func (s *ServiceRegistry) UpdateService(newCfg client.ServiceConfig) error { s.Lock() defer s.Unlock() log.Debug("Updating Service:", newCfg.Name) service, ok := s.svcs[newCfg.Name] if !ok { log.Debug("Service not found:", newCfg.Name) return ErrNoService } currentCfg := service.Config() newCfg.Merge(currentCfg) if err := service.UpdateConfig(newCfg); err != nil { return err } // Lots of looping here (including fetching the Config, but the cardinality // of Backends shouldn't be very large, and the default RoundRobin balancing // is much simpler with a slice. // we're going to update just the backends for this config // get a map of what's already running currentBackends := make(map[string]client.BackendConfig) for _, backendCfg := range currentCfg.Backends { currentBackends[backendCfg.Name] = backendCfg } // Keep existing backends when they have equivalent config. // Update changed backends, and add new ones. for _, newBackend := range newCfg.Backends { current, ok := currentBackends[newBackend.Name] if ok && current.Equal(newBackend) { log.Debugf("Backend %s/%s unchanged", service.Name, current.Name) // no change for this one delete(currentBackends, current.Name) continue } // we need to remove and re-add this backend log.Debugf("Updating Backend %s/%s", service.Name, newBackend.Name) service.remove(newBackend.Name) service.add(NewBackend(newBackend)) delete(currentBackends, newBackend.Name) } // remove any left over backends for name := range currentBackends { log.Debugf("Removing Backend %s/%s", service.Name, name) service.remove(name) } if currentCfg.Equal(newCfg) { log.Debugf("Service Unchanged %s", service.Name) return nil } // replace error pages if there's any change if !reflect.DeepEqual(service.errPagesCfg, newCfg.ErrorPages) { log.Debugf("Updating ErrorPages") service.errPagesCfg = newCfg.ErrorPages service.errorPages.Update(newCfg.ErrorPages) } s.updateVHosts(service, filterEmpty(newCfg.VirtualHosts)) return nil }