예제 #1
0
파일: registry.go 프로젝트: joeshaw/shuttle
// 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
}
예제 #2
0
파일: config.go 프로젝트: joeshaw/shuttle
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)
	}
}
예제 #3
0
파일: config.go 프로젝트: joeshaw/shuttle
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)
		}
	}
}
예제 #4
0
파일: backend.go 프로젝트: joeshaw/shuttle
// 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()
		}
	}
}
예제 #5
0
파일: backend.go 프로젝트: joeshaw/shuttle
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
		}
	}
}
예제 #6
0
파일: registry.go 프로젝트: joeshaw/shuttle
// 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
}