コード例 #1
0
ファイル: registry.go プロジェクト: skyfii/shuttle
func (s *ServiceRegistry) RemoveService(name string) error {
	s.Lock()
	defer s.Unlock()

	svc, ok := s.svcs[name]
	if ok {
		log.Debugf("DEBUG: Removing Service %s", svc.Name)
		delete(s.svcs, name)
		svc.stop()

		for host, vhost := range s.vhosts {
			vhost.Remove(svc)

			removeVhost := true
			for _, service := range s.svcs {
				for _, h := range service.VirtualHosts {
					if host == h {
						// FIXME: is this still correct? NOT TESTED!
						// vhost exists in another service, so leave it
						removeVhost = false
						break
					}
				}
			}
			if removeVhost {
				log.Debugf("DEBUG: Removing VirtualHost %s", host)
				delete(s.vhosts, host)

			}
		}

		return nil
	}
	return ErrNoService
}
コード例 #2
0
ファイル: backend.go プロジェクト: skyfii/shuttle
func (b *Backend) Proxy(srvConn, cliConn net.Conn) {
	log.Debugf("DEBUG: Initiating proxy: %s/%s-%s/%s",
		cliConn.RemoteAddr(),
		cliConn.LocalAddr(),
		srvConn.LocalAddr(),
		srvConn.RemoteAddr(),
	)

	// Backend is a pointer receiver so we can get the address of the fields,
	// but all updates will be done atomically.

	bConn := &shuttleConn{
		TCPConn:   srvConn.(*net.TCPConn),
		rwTimeout: b.rwTimeout,
		read:      &b.Rcvd,
		written:   &b.Sent,
	}
	// TODO: No way to force shutdown. Do we need it, or should we always just
	// let a connection run out?

	atomic.AddInt64(&b.Conns, 1)
	atomic.AddInt64(&b.Active, 1)
	defer atomic.AddInt64(&b.Active, -1)

	// channels to wait on close event
	backendClosed := make(chan bool, 1)
	clientClosed := make(chan bool, 1)

	go broker(bConn, cliConn, clientClosed, &b.Sent, &b.Errors)
	go broker(cliConn, bConn, backendClosed, &b.Rcvd, &b.Errors)

	// wait for one half of the proxy to exit, then trigger a shutdown of the
	// other half by calling CloseRead(). This will break the read loop in the
	// broker and fully close the connection.
	var waitFor chan bool
	select {
	case <-clientClosed:
		log.Debugf("DEBUG: Client %s/%s closed connection", cliConn.RemoteAddr(), cliConn.LocalAddr())
		// the client closed first, so any more packets here are invalid, and
		// we can SetLinger(0) to recycle the port faster.
		bConn.TCPConn.SetLinger(0)
		bConn.CloseRead()
		waitFor = backendClosed
	case <-backendClosed:
		log.Debugf("DEBUG: Server %s/%s closed connection", srvConn.RemoteAddr(), srvConn.LocalAddr())
		cliConn.(closeReader).CloseRead()
		waitFor = clientClosed
	}
	// wait for the other connection to close
	<-waitFor
}
コード例 #3
0
ファイル: registry.go プロジェクト: skyfii/shuttle
func (v *VirtualHost) Remove(svc *Service) {
	v.Lock()
	defer v.Unlock()

	found := -1
	for i, s := range v.services {
		if s.Name == svc.Name {
			found = i
			break
		}
	}

	if found < 0 {
		log.Debugf("DEBUG: Service %s not found under VirtualHost %s", svc.Name, v.Name)
		return
	}

	// safe way to get the backends info for logging
	svcCfg := svc.Config()

	// Now removing this Service
	for _, backend := range svcCfg.Backends {
		log.Printf("INFO: Removing backend http://%s from VirtualHost %s", backend.Addr, v.Name)
	}

	v.services = append(v.services[:found], v.services[found+1:]...)
}
コード例 #4
0
ファイル: backend.go プロジェクト: skyfii/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.Warnf("WARN: Backend check for %s failed with error:", e)
		up = false
	}

	b.Lock()
	defer b.Unlock()
	if up {
		log.Debugf("DEBUG: 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.Warnf("WARN: Marking backend %s Up", b.Name)
			}
			b.up = true
		}
	} else {
		log.Debugf("DEBUG: 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.Warnf("WARN: Marking backend %s Down", b.Name)
			}
			b.up = false
		}
	}
}
コード例 #5
0
ファイル: registry.go プロジェクト: skyfii/shuttle
// Add or update a Backend on an existing Service.
func (s *ServiceRegistry) AddBackend(svcName string, backendCfg client.BackendConfig) error {
	s.Lock()
	defer s.Unlock()

	service, ok := s.svcs[svcName]
	if !ok {
		return ErrNoService
	}

	log.Debugf("DEBUG: Adding Backend %s/%s", service.Name, backendCfg.Name)
	service.add(NewBackend(backendCfg))
	return nil
}
コード例 #6
0
ファイル: registry.go プロジェクト: skyfii/shuttle
// Remove a Backend from an existing Service.
func (s *ServiceRegistry) RemoveBackend(svcName, backendName string) error {
	s.Lock()
	defer s.Unlock()

	log.Debugf("DEBUG: Removing Backend %s/%s", svcName, backendName)
	service, ok := s.svcs[svcName]
	if !ok {
		return ErrNoService
	}

	if !service.remove(backendName) {
		return ErrNoBackend
	}
	return nil
}
コード例 #7
0
ファイル: registry.go プロジェクト: skyfii/shuttle
// Insert a service
// do nothing if the service already is registered
func (v *VirtualHost) Add(svc *Service) {
	v.Lock()
	defer v.Unlock()
	for _, s := range v.services {
		if s.Name == svc.Name {
			log.Debugf("DEBUG: Service %s already registered in VirtualHost %s", svc.Name, v.Name)
			return
		}
	}

	// TODO: is this the best place to log these?
	svcCfg := svc.Config()
	for _, backend := range svcCfg.Backends {
		log.Printf("INFO: Adding backend http://%s to VirtualHost %s", backend.Addr, v.Name)
	}
	v.services = append(v.services, svc)
}
コード例 #8
0
ファイル: registry.go プロジェクト: skyfii/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("DEBUG: Updating Service:", newCfg.Name)
	service, ok := s.svcs[newCfg.Name]
	if !ok {
		log.Debug("DEBUG: Service not found:", newCfg.Name)
		return ErrNoService
	}

	currentCfg := service.Config()
	newCfg = currentCfg.Merge(newCfg)

	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("DEBUG: 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.Warnf("WARN: 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("DEBUG: Removing Backend %s/%s", service.Name, name)
		service.remove(name)
	}

	if currentCfg.Equal(newCfg) {
		log.Debugf("DEBUG: Service Unchanged %s", service.Name)
		return nil
	}

	// replace error pages if there's any change
	if !reflect.DeepEqual(service.errPagesCfg, newCfg.ErrorPages) {
		log.Debugf("DEBUG: Updating ErrorPages")
		service.errPagesCfg = newCfg.ErrorPages
		service.errorPages.Update(newCfg.ErrorPages)
	}

	s.updateVHosts(service, filterEmpty(newCfg.VirtualHosts))

	return nil
}