func (h *HAproxy) Chan() chan catalog.ChangeEvent {
	return h.eventChannel
}
Chan is part of the catalog.Listener interface. Returns the channel we listen on.
package haproxy
import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"regexp"
	"sort"
	"strconv"
	"sync"
	"syscall"
	"text/template"
	"time"
	"github.com/NinesStack/sidecar/catalog"
	"github.com/NinesStack/sidecar/service"
	log "github.com/sirupsen/logrus"
)
type portset map[string]string
type portmap map[string]portset
type HAproxy struct {
	ReloadCmd      string `toml:"reload_cmd"`
	VerifyCmd      string `toml:"verify_cmd"`
	BindIP         string `toml:"bind_ip"`
	Template       string `toml:"template"`
	ConfigFile     string `toml:"config_file"`
	PidFile        string `toml:"pid_file"`
	User           string `toml:"user"`
	Group          string `toml:"group"`
	UseHostnames   bool   `toml:"use_hostnames"`
	eventChannel   chan catalog.ChangeEvent
	signalsHandled bool
	sigLock        sync.Mutex
	sigStopChan    chan struct{}
}
func New(configFile string, pidFile string) *HAproxy {
	reloadCmd := "haproxy -f " + configFile + " -p " + pidFile + " `[[ -f " + pidFile + " ]] && echo \"-sf $(cat " + pidFile + ")\"`"
	verifyCmd := "haproxy -c -f " + configFile
	proxy := HAproxy{
		ReloadCmd:  reloadCmd,
		VerifyCmd:  verifyCmd,
		Template:   "views/haproxy.cfg",
		ConfigFile: configFile,
		PidFile:    pidFile,
	}
	return &proxy
}
func (h *HAproxy) makePortmap(services map[string][]*service.Service) portmap {
	ports := make(portmap)
	for svcName, svcList := range services {
		if _, ok := ports[svcName]; !ok {
			ports[svcName] = make(portset, 5)
		}
		for _, service := range svcList {
			for _, port := range service.Ports {
				if port.Type == "tcp" && port.ServicePort != 0 {
					svcPort := strconv.FormatInt(port.ServicePort, 10)
					internalPort := strconv.FormatInt(port.Port, 10)
					ports[svcName][svcPort] = internalPort
				}
			}
		}
	}
	return ports
}
func sanitizeName(image string) string {
	replace := regexp.MustCompile("[^a-z0-9-]")
	return replace.ReplaceAllString(image, "-")
}
func findPortForService(svcPort string, svc *service.Service) string {
	matchPort, err := strconv.ParseInt(svcPort, 10, 64)
	if err != nil {
		log.Errorf("Invalid value from template ('%s') can't parse as int64: %s", svcPort, err.Error())
		return "-1"
	}
	for _, port := range svc.Ports {
		if port.ServicePort == matchPort {
			internalPort := strconv.FormatInt(port.Port, 10)
			return internalPort
		}
	}
	return "-1"
}
func (h *HAproxy) findIpForService(svcPort string, svc *service.Service) string {
	if h.UseHostnames {
		return svc.Hostname
	}
	matchPort, err := strconv.ParseInt(svcPort, 10, 64)
	if err != nil {
		log.Errorf("Invalid value from template ('%s') can't parse as int64: %s", svcPort, err.Error())
		return "-1"
	}
	for _, port := range svc.Ports {
		if port.ServicePort == matchPort {
			return port.IP
		}
	}
	return svc.Hostname
}
func (h *HAproxy) WriteConfig(state *catalog.ServicesState, output io.Writer) error {
	state.RLock()
	services := servicesWithPorts(state)
	ports := h.makePortmap(services)
	modes := getModes(state)
	state.RUnlock()
	data := struct {
		Services map[string][]*service.Service
		User     string
		Group    string
	}{
		Services: services,
		User:     h.User,
		Group:    h.Group,
	}
	funcMap := template.FuncMap{
		"now": time.Now().UTC,
		"getMode": func(k string) string {
			return modes[k]
		},
		"getPorts": func(k string) map[string]string {
			return ports[k]
		},
		"portFor":      findPortForService,
		"ipFor":        h.findIpForService,
		"bindIP":       func() string { return h.BindIP },
		"sanitizeName": sanitizeName,
	}
	t, err := template.New("haproxy").Funcs(funcMap).ParseFiles(h.Template)
	if err != nil {
		return fmt.Errorf("Error Parsing template '%s': %s", h.Template, err.Error())
	}
	buf := bytes.NewBuffer(make([]byte, 0, 65535))
	state.RLock()
	err = t.ExecuteTemplate(buf, path.Base(h.Template), data)
	state.RUnlock()
	if err != nil {
		return fmt.Errorf("Error executing template '%s': %s", h.Template, err.Error())
	}
	_, err = io.Copy(output, buf)
	if err != nil {
		return fmt.Errorf("Error writing template '%s': %s", h.Template, err.Error())
	}
	return nil
}
func (h *HAproxy) swallowSignals() {
	sigChan := make(chan os.Signal, 10)
	h.sigStopChan = make(chan struct{})
	go func() {
		for {
			select {
			case <-sigChan:
			case <-h.sigStopChan:
				break
			}
		}
	}()
	signal.Notify(sigChan, syscall.SIGTSTP, syscall.SIGTTIN, syscall.SIGTTOU)
}
func (h *HAproxy) ResetSignals() {
	h.sigLock.Lock()
	signal.Reset(syscall.SIGTSTP, syscall.SIGTTIN, syscall.SIGTTOU)
	select {
	case h.sigStopChan <- struct{}{}:  
	default:
	}
	h.sigLock.Unlock()
}
func (h *HAproxy) run(command string) error {
	cmd := exec.Command("/bin/bash", "-c", command)
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	h.sigLock.Lock()
	defer h.sigLock.Unlock()
	if !h.signalsHandled {
		log.Info("Setting up signal handlers")
		h.swallowSignals()
		h.signalsHandled = true
	}
	err := cmd.Run()
	if err != nil {
		err = fmt.Errorf("Error running '%s': %s\n%s\n%s", command, err, stdout, stderr)
	}
	return err
}
func (h *HAproxy) Reload() error {
	return h.run(h.ReloadCmd)
}
func (h *HAproxy) Verify() error {
	return h.run(h.VerifyCmd)
}
func (h *HAproxy) Watch(state *catalog.ServicesState) {
	h.eventChannel = make(chan catalog.ChangeEvent, 2)
	state.AddListener(h)
	for event := range h.eventChannel {
		log.Println("State change event from " + event.Service.Hostname)
		err := h.WriteAndReload(state)
		if err != nil {
			log.Error(err.Error())
		}
	}
	err := state.RemoveListener(h.Name())
	if err != nil {
		log.Warnf("Failed to remove HAProxy listener: %s", err)
	}
}
func (h *HAproxy) WriteAndReload(state *catalog.ServicesState) error {
	if h.ConfigFile == "" {
		return fmt.Errorf("Trying to write HAproxy config, but no filename specified!")
	}
	outfile, err := os.Create(h.ConfigFile)
	if err != nil {
		return fmt.Errorf("Unable to write to %s! (%s)", h.ConfigFile, err.Error())
	}
	if err := h.WriteConfig(state, outfile); err != nil {
		return err
	}
	if err = h.Verify(); err != nil {
		return fmt.Errorf("Failed to verify HAproxy config! (%s)", err.Error())
	}
	return h.Reload()
}
func (h *HAproxy) Name() string {
	return "HAproxy"
}
func (h *HAproxy) Managed() bool {
	return false
}