<commit_before>package service

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/op/go-logging"

	"arvika.pulcy.com/pulcy/load-balancer/haproxy"
)

const (
	osExitDelay = time.Second * 3
	confPerm    = os.FileMode(0664) // rw-rw-r
)

var (
	globalOptions = []string{
		//		"chroot /var/lib/haproxy",
		"daemon",
		//		"user haproxy",
		//		"group haproxy",
		"log /dev/log local0",
		"tune.ssl.default-dh-param 2048",
		"ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:CAMELLIA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA",
	}
	defaultsOptions = []string{
		"mode http",
		"timeout connect 5000ms",
		"timeout client 50000ms",
		"timeout server 50000ms",
		"option forwardfor",
		"option http-server-close",
		"log global",
		"option httplog",
		"option dontlognull",
		"errorfile 400 /app/errors/400.http",
		"errorfile 403 /app/errors/403.http",
		"errorfile 408 /app/errors/408.http",
		"errorfile 500 /app/errors/500.http",
		"errorfile 502 /app/errors/502.http",
		"errorfile 503 /app/errors/503.http",
		"errorfile 504 /app/errors/504.http",
	}
)

type ServiceConfig struct {
	HaproxyConfPath string
	HaproxyPath     string
	HaproxyPidPath  string
}

type ServiceDependencies struct {
	Logger  *logging.Logger
	Backend Backend
}

type Service struct {
	ServiceConfig
	ServiceDependencies

	signalCounter uint32
	lastConfig    string
}

// NewService creates a new service instance.
func NewService(config ServiceConfig, deps ServiceDependencies) *Service {
	if config.HaproxyPath == "" {
		config.HaproxyPath = "haproxy"
	}
	if config.HaproxyPidPath == "" {
		config.HaproxyPidPath = "/var/run/haproxy.pid"
	}
	return &Service{
		ServiceConfig:       config,
		ServiceDependencies: deps,
	}
}

// Run starts the service and waits for OS signals to terminate it.
func (s *Service) Run() {
	go s.configLoop()
	s.listenSignals()
}

// configLoop updates the haproxy config, and then waits
// for changes in the backend.
func (s *Service) configLoop() {
	for {
		if err := s.updateHaproxy(); err != nil {
			s.Logger.Error("Failed to update haproxy: %#v", err)
		}
		if err := s.Backend.Watch(); err != nil {
			s.Logger.Error("Failed to watch for backend changes: %#v", err)
		}
	}
}

// update the haproxy configuration
func (s *Service) updateHaproxy() error {
	// Create a new config (in temp path)
	config, tempConf, err := s.createConfig()
	if err != nil {
		return maskAny(err)
	}

	// Cleanup afterwards
	defer os.Remove(tempConf)

	// If nothing has changed, don't do anything
	if s.lastConfig == config {
		return nil
	}

	// Validate the config
	if err := s.validateConfig(tempConf); err != nil {
		return maskAny(err)
	}

	// Move config to correct place
	os.Remove(s.HaproxyConfPath)
	if err := ioutil.WriteFile(s.HaproxyConfPath, []byte(config), confPerm); err != nil {
		s.Logger.Error("Cannot copy haproxy config to %s: %#v", s.HaproxyConfPath, err)
		return maskAny(err)
	}

	// Restart haproxy
	if err := s.restartHaproxy(); err != nil {
		return maskAny(err)
	}

	// Rember the current config
	s.lastConfig = config

	s.Logger.Info("Restarted haproxy")

	return nil
}

// createConfig creates a new haproxy configuration file.
// It returns the path of the new config file.
func (s *Service) createConfig() (string, string, error) {
	c := haproxy.NewConfig()
	c.Section("global").Add(globalOptions...)
	c.Section("defaults").Add(defaultsOptions...)

	// Fetch data from backend
	frontends, err := s.Backend.FrontEnds()
	if err != nil {
		return "", "", maskAny(err)
	}
	services, err := s.Backend.Services()
	if err != nil {
		return "", "", maskAny(err)
	}

	// Create config for all registrations
	frontEndSection := c.Section("frontend http-in")
	frontEndSection.Add(
		"bind *:80",
		"default_backend fallback",
	)
	for _, fr := range frontends {
		// Create acls
		for _, sel := range fr.Selectors {
			if sel.Domain != "" {
				frontEndSection.Add(fmt.Sprintf("acl %s hdr_dom(host) -i %s", fr.aclName(), sel.Domain))
			}
			if sel.PathPrefix != "" {
				frontEndSection.Add(fmt.Sprintf("acl %s path_beg %s", fr.aclName(), sel.PathPrefix))
			}
		}

		// Create link to backend
		frontEndSection.Add(fmt.Sprintf("use_backend %s if %s", fr.backendName(), fr.aclName()))

		// Create backend
		backendSection := c.Section(fmt.Sprintf("backend %s", fr.backendName()))
		backendSection.Add(
			"mode http",
			"balance roundrobin",
		)
		for _, service := range services {
			if service.Name != fr.Service {
				continue
			}
			for i, b := range service.Backends {
				id := fmt.Sprintf("%s-%d", service.Name, i)
				backendSection.Add(fmt.Sprintf("server %s %s", id, b))
			}
		}
	}

	// Create fallback backend
	fbbSection := c.Section("backend fallback")
	fbbSection.Add(
		"mode http",
		"balance roundrobin",
	)

	// Render config
	config := c.Render()
	s.Logger.Debug("Config:\n%s", config)

	// Create temp file first
	tempFile, err := ioutil.TempFile("", "haproxy")
	if err != nil {
		return "", "", maskAny(err)
	}
	defer tempFile.Close()
	if _, err := tempFile.WriteString(config); err != nil {
		return "", "", maskAny(err)
	}
	return config, tempFile.Name(), nil
}

// validateConfig calls haproxy to validate the given config file.
func (s *Service) validateConfig(confPath string) error {
	cmd := exec.Command(s.HaproxyPath, "-c", "-f", confPath)
	output, err := cmd.Output()
	if err != nil {
		s.Logger.Error("Error in haproxy config: %s", string(output))
		return maskAny(err)
	}
	return nil
}

// restartHaproxy restarts haproxy, killing previous instances
func (s *Service) restartHaproxy() error {
	args := []string{
		"-f",
		s.HaproxyConfPath,
		"-p",
		s.HaproxyPidPath,
	}
	if pid, err := ioutil.ReadFile(s.HaproxyPidPath); err == nil {
		args = append(args, "-sf", string(pid))
	}

	s.Logger.Debug("Starting haproxy with %#v", args)
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("%s %s", s.HaproxyPath, strings.Join(args, " ")))
	if err := cmd.Start(); err != nil {
		s.Logger.Error("Failed to start haproxy: %#v", err)
		return maskAny(err)
	}
	return nil
}

// close closes this service in a timely manor.
func (s *Service) close() {
	// Interrupt the process when closing is requested twice.
	if atomic.AddUint32(&s.signalCounter, 1) >= 2 {
		s.exitProcess()
	}

	s.Logger.Info("shutting down server in %s", osExitDelay.String())
	time.Sleep(osExitDelay)

	s.exitProcess()
}

// exitProcess terminates this process with exit code 1.
func (s *Service) exitProcess() {
	s.Logger.Info("shutting down server")
	os.Exit(0)
}

// listenSignals waits for incoming OS signals and acts upon them
func (s *Service) listenSignals() {
	// Set up channel on which to send signal notifications.
	// We must use a buffered channel or risk missing the signal
	// if we're not ready to receive when the signal is sent.
	c := make(chan os.Signal, 2)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)

	// Block until a signal is received.
	for {
		select {
		case sig := <-c:
			s.Logger.Info("server received signal %s", sig)
			go s.close()
		}
	}
}
<commit_msg>-sf -> -st<commit_after>package service

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/op/go-logging"

	"arvika.pulcy.com/pulcy/load-balancer/haproxy"
)

const (
	osExitDelay = time.Second * 3
	confPerm    = os.FileMode(0664) // rw-rw-r
)

var (
	globalOptions = []string{
		//		"chroot /var/lib/haproxy",
		"daemon",
		//		"user haproxy",
		//		"group haproxy",
		"log /dev/log local0",
		"tune.ssl.default-dh-param 2048",
		"ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:CAMELLIA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA",
	}
	defaultsOptions = []string{
		"mode http",
		"timeout connect 5000ms",
		"timeout client 50000ms",
		"timeout server 50000ms",
		"option forwardfor",
		"option http-server-close",
		"log global",
		"option httplog",
		"option dontlognull",
		"errorfile 400 /app/errors/400.http",
		"errorfile 403 /app/errors/403.http",
		"errorfile 408 /app/errors/408.http",
		"errorfile 500 /app/errors/500.http",
		"errorfile 502 /app/errors/502.http",
		"errorfile 503 /app/errors/503.http",
		"errorfile 504 /app/errors/504.http",
	}
)

type ServiceConfig struct {
	HaproxyConfPath string
	HaproxyPath     string
	HaproxyPidPath  string
}

type ServiceDependencies struct {
	Logger  *logging.Logger
	Backend Backend
}

type Service struct {
	ServiceConfig
	ServiceDependencies

	signalCounter uint32
	lastConfig    string
}

// NewService creates a new service instance.
func NewService(config ServiceConfig, deps ServiceDependencies) *Service {
	if config.HaproxyPath == "" {
		config.HaproxyPath = "haproxy"
	}
	if config.HaproxyPidPath == "" {
		config.HaproxyPidPath = "/var/run/haproxy.pid"
	}
	return &Service{
		ServiceConfig:       config,
		ServiceDependencies: deps,
	}
}

// Run starts the service and waits for OS signals to terminate it.
func (s *Service) Run() {
	go s.configLoop()
	s.listenSignals()
}

// configLoop updates the haproxy config, and then waits
// for changes in the backend.
func (s *Service) configLoop() {
	for {
		if err := s.updateHaproxy(); err != nil {
			s.Logger.Error("Failed to update haproxy: %#v", err)
		}
		if err := s.Backend.Watch(); err != nil {
			s.Logger.Error("Failed to watch for backend changes: %#v", err)
		}
	}
}

// update the haproxy configuration
func (s *Service) updateHaproxy() error {
	// Create a new config (in temp path)
	config, tempConf, err := s.createConfig()
	if err != nil {
		return maskAny(err)
	}

	// Cleanup afterwards
	defer os.Remove(tempConf)

	// If nothing has changed, don't do anything
	if s.lastConfig == config {
		return nil
	}

	// Validate the config
	if err := s.validateConfig(tempConf); err != nil {
		return maskAny(err)
	}

	// Move config to correct place
	os.Remove(s.HaproxyConfPath)
	if err := ioutil.WriteFile(s.HaproxyConfPath, []byte(config), confPerm); err != nil {
		s.Logger.Error("Cannot copy haproxy config to %s: %#v", s.HaproxyConfPath, err)
		return maskAny(err)
	}

	// Restart haproxy
	if err := s.restartHaproxy(); err != nil {
		return maskAny(err)
	}

	// Rember the current config
	s.lastConfig = config

	s.Logger.Info("Restarted haproxy")

	return nil
}

// createConfig creates a new haproxy configuration file.
// It returns the path of the new config file.
func (s *Service) createConfig() (string, string, error) {
	c := haproxy.NewConfig()
	c.Section("global").Add(globalOptions...)
	c.Section("defaults").Add(defaultsOptions...)

	// Fetch data from backend
	frontends, err := s.Backend.FrontEnds()
	if err != nil {
		return "", "", maskAny(err)
	}
	services, err := s.Backend.Services()
	if err != nil {
		return "", "", maskAny(err)
	}

	// Create config for all registrations
	frontEndSection := c.Section("frontend http-in")
	frontEndSection.Add(
		"bind *:80",
		"default_backend fallback",
	)
	for _, fr := range frontends {
		// Create acls
		for _, sel := range fr.Selectors {
			if sel.Domain != "" {
				frontEndSection.Add(fmt.Sprintf("acl %s hdr_dom(host) -i %s", fr.aclName(), sel.Domain))
			}
			if sel.PathPrefix != "" {
				frontEndSection.Add(fmt.Sprintf("acl %s path_beg %s", fr.aclName(), sel.PathPrefix))
			}
		}

		// Create link to backend
		frontEndSection.Add(fmt.Sprintf("use_backend %s if %s", fr.backendName(), fr.aclName()))

		// Create backend
		backendSection := c.Section(fmt.Sprintf("backend %s", fr.backendName()))
		backendSection.Add(
			"mode http",
			"balance roundrobin",
		)
		for _, service := range services {
			if service.Name != fr.Service {
				continue
			}
			for i, b := range service.Backends {
				id := fmt.Sprintf("%s-%d", service.Name, i)
				backendSection.Add(fmt.Sprintf("server %s %s", id, b))
			}
		}
	}

	// Create fallback backend
	fbbSection := c.Section("backend fallback")
	fbbSection.Add(
		"mode http",
		"balance roundrobin",
	)

	// Render config
	config := c.Render()
	s.Logger.Debug("Config:\n%s", config)

	// Create temp file first
	tempFile, err := ioutil.TempFile("", "haproxy")
	if err != nil {
		return "", "", maskAny(err)
	}
	defer tempFile.Close()
	if _, err := tempFile.WriteString(config); err != nil {
		return "", "", maskAny(err)
	}
	return config, tempFile.Name(), nil
}

// validateConfig calls haproxy to validate the given config file.
func (s *Service) validateConfig(confPath string) error {
	cmd := exec.Command(s.HaproxyPath, "-c", "-f", confPath)
	output, err := cmd.Output()
	if err != nil {
		s.Logger.Error("Error in haproxy config: %s", string(output))
		return maskAny(err)
	}
	return nil
}

// restartHaproxy restarts haproxy, killing previous instances
func (s *Service) restartHaproxy() error {
	args := []string{
		"-f",
		s.HaproxyConfPath,
		"-p",
		s.HaproxyPidPath,
	}
	if pid, err := ioutil.ReadFile(s.HaproxyPidPath); err == nil {
		args = append(args, "-st", string(pid))
	}

	s.Logger.Debug("Starting haproxy with %#v", args)
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("%s %s", s.HaproxyPath, strings.Join(args, " ")))
	if err := cmd.Start(); err != nil {
		s.Logger.Error("Failed to start haproxy: %#v", err)
		return maskAny(err)
	}
	return nil
}

// close closes this service in a timely manor.
func (s *Service) close() {
	// Interrupt the process when closing is requested twice.
	if atomic.AddUint32(&s.signalCounter, 1) >= 2 {
		s.exitProcess()
	}

	s.Logger.Info("shutting down server in %s", osExitDelay.String())
	time.Sleep(osExitDelay)

	s.exitProcess()
}

// exitProcess terminates this process with exit code 1.
func (s *Service) exitProcess() {
	s.Logger.Info("shutting down server")
	os.Exit(0)
}

// listenSignals waits for incoming OS signals and acts upon them
func (s *Service) listenSignals() {
	// Set up channel on which to send signal notifications.
	// We must use a buffered channel or risk missing the signal
	// if we're not ready to receive when the signal is sent.
	c := make(chan os.Signal, 2)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)

	// Block until a signal is received.
	for {
		select {
		case sig := <-c:
			s.Logger.Info("server received signal %s", sig)
			go s.close()
		}
	}
}
<|endoftext|>


<commit_before>package tcpreuse

import (
	"net"
	"testing"

	ma "github.com/multiformats/go-multiaddr"
)

func TestSingle(t *testing.T) {
	var trA Transport
	var trB Transport
	laddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
	listenerA, err := trA.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()
	listenerB, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB.Close()

	done := make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err := trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	<-done
	c.Close()
}

func TestTwoLocal(t *testing.T) {
	var trA Transport
	var trB Transport
	laddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
	listenerA, err := trA.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	done := make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err := trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	localPort := c.LocalAddr().(*net.TCPAddr).Port
	if localPort != listenerB1.Addr().(*net.TCPAddr).Port &&
		localPort != listenerB2.Addr().(*net.TCPAddr).Port {
		t.Fatal("didn't dial from one of our listener ports")
	}
	<-done
	c.Close()
}

func TestLocalAndUnspecified(t *testing.T) {
	var trA Transport
	var trB Transport
	laddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
	unspec, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
	listenerA, err := trA.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(unspec)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	done := make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err := trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	actual := c.LocalAddr().(*net.TCPAddr).Port
	expected := listenerB1.Addr().(*net.TCPAddr).Port
	if actual != expected {
		t.Errorf("expected to use port %d, used port %d", expected, actual)
	}
	<-done
	c.Close()

	// Closing the listener should reset the dialer.
	listenerB1.Close()

	done = make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err = trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	actual = c.LocalAddr().(*net.TCPAddr).Port
	expected = listenerB2.Addr().(*net.TCPAddr).Port
	if actual != expected {
		t.Errorf("expected to use port %d, used port %d", expected, actual)
	}
	<-done
	c.Close()
}
<commit_msg>test dialing with no listeners<commit_after>package tcpreuse

import (
	"net"
	"testing"

	ma "github.com/multiformats/go-multiaddr"
)

func TestNoneAndSingle(t *testing.T) {
	var trA Transport
	var trB Transport
	laddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
	listenerA, err := trA.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	done := make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err := trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	<-done
	c.Close()

	listenerB, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB.Close()

	done = make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err = trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	actual := c.LocalAddr().(*net.TCPAddr).Port
	expected := listenerB.Addr().(*net.TCPAddr).Port
	if actual != expected {
		t.Errorf("expected to use port %d, used port %d", expected, actual)
	}
	<-done
	c.Close()
}

func TestTwoLocal(t *testing.T) {
	var trA Transport
	var trB Transport
	laddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
	listenerA, err := trA.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	done := make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err := trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	localPort := c.LocalAddr().(*net.TCPAddr).Port
	if localPort != listenerB1.Addr().(*net.TCPAddr).Port &&
		localPort != listenerB2.Addr().(*net.TCPAddr).Port {
		t.Fatal("didn't dial from one of our listener ports")
	}
	<-done
	c.Close()
}

func TestLocalAndUnspecified(t *testing.T) {
	var trA Transport
	var trB Transport
	laddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/0")
	unspec, _ := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0")
	listenerA, err := trA.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerA.Close()

	listenerB1, err := trB.Listen(laddr)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB1.Close()

	listenerB2, err := trB.Listen(unspec)
	if err != nil {
		t.Fatal(err)
	}
	defer listenerB2.Close()

	done := make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err := trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	actual := c.LocalAddr().(*net.TCPAddr).Port
	expected := listenerB1.Addr().(*net.TCPAddr).Port
	if actual != expected {
		t.Errorf("expected to use port %d, used port %d", expected, actual)
	}
	<-done
	c.Close()

	// Closing the listener should reset the dialer.
	listenerB1.Close()

	done = make(chan struct{})
	go func() {
		defer close(done)
		c, err := listenerA.Accept()
		if err != nil {
			t.Fatal(err)
		}
		c.Close()
	}()

	c, err = trB.Dial(listenerA.Multiaddr())
	if err != nil {
		t.Fatal(err)
	}
	actual = c.LocalAddr().(*net.TCPAddr).Port
	expected = listenerB2.Addr().(*net.TCPAddr).Port
	if actual != expected {
		t.Errorf("expected to use port %d, used port %d", expected, actual)
	}
	<-done
	c.Close()
}
<|endoftext|>


<commit_before>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
	return []string{
		"donators",
		"donations",
		"donate",
		"supporters",
		"support",
		"patreon",
		"patreons",
		"credits",
	}
}

func (d *Donators) Response(trigger string, content string) string {
	return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 💓\nsomicidal minaiac 💞\nOokami 🖤\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nHomeboywill 💘\nRimbol 💕\nGenisphere 💖\nekgus 💗\nCPark 💞\njungoo 💕\nShawn 💗\nSaltiestPeach 💘\nBae Nja Min 💖\nhaerts 💓\ncasker 💞\nJean 💖\npooth 💘\nshb117 💕\nicecreamsmart 💓\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<commit_msg>[donators] adds L!<commit_after>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
	return []string{
		"donators",
		"donations",
		"donate",
		"supporters",
		"support",
		"patreon",
		"patreons",
		"credits",
	}
}

func (d *Donators) Response(trigger string, content string) string {
	return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 🌞\nsomicidal minaiac 💞\nOokami 🖤\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nHomeboywill 💘\nRimbol 💕\nGenisphere 💖\nekgus 💗\nCPark 💞\njungoo 💕\nShawn 💗\nSaltiestPeach 💘\nBae Nja Min 💖\nhaerts 💓\ncasker 💞\nJean 💖\npooth 💘\nshb117 💕\nicecreamsmart 💓\nL 💘\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// backups contains all the stand-alone backup-related functionality for
// juju state.
package backups

import (
	"io"
	"time"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils/filestorage"
)

var logger = loggo.GetLogger("juju.state.backups")

var (
	getFilesToBackUp = GetFilesToBackUp
	getDBDumper      = NewDBDumper
	runCreate        = create
	finishMeta       = func(meta *Metadata, result *createResult) error {
		return meta.MarkComplete(result.size, result.checksum)
	}
	storeArchive = StoreArchive
)

// StoreArchive sends the backup archive and its metadata to storage.
// It also sets the metadata's ID and Stored values.
func StoreArchive(stor filestorage.FileStorage, meta *Metadata, file io.Reader) error {
	id, err := stor.Add(meta, file)
	if err != nil {
		return errors.Trace(err)
	}
	meta.SetID(id)
	stored, err := stor.Metadata(id)
	if err != nil {
		return errors.Trace(err)
	}
	meta.SetStored(stored.Stored())
	return nil
}

// Backups is an abstraction around all juju backup-related functionality.
type Backups interface {

	// Create creates and stores a new juju backup archive.  It updates
	// the provided metadata.
	Create(meta *Metadata, paths Paths, dbInfo DBInfo) error

	// Get returns the metadata and archive file associated with the ID.
	Get(id string) (*Metadata, io.ReadCloser, error)

	// List returns the metadata for all stored backups.
	List() ([]*Metadata, error)

	// Remove deletes the backup from storage.
	Remove(id string) error
}

type backups struct {
	storage filestorage.FileStorage
}

// NewBackups creates a new Backups value using the provided file storage.
func NewBackups(stor filestorage.FileStorage) Backups {
	b := backups{
		storage: stor,
	}
	return &b
}

// Create creates and stores a new juju backup archive and updates the
// provided metadata.
func (b *backups) Create(meta *Metadata, paths Paths, dbInfo DBInfo) error {
	meta.Started = time.Now().UTC()

	// The metadata file will not contain the ID or the "finished" data.
	// However, that information is not as critical.  The alternatives
	// are either adding the metadata file to the archive after the fact
	// or adding placeholders here for the finished data and filling
	// them in afterward.  Neither is particularly trivial.
	metadataFile, err := meta.AsJSONBuffer()
	if err != nil {
		return errors.Annotate(err, "while preparing the metadata")
	}

	// Create the archive.
	filesToBackUp, err := getFilesToBackUp("", paths)
	if err != nil {
		return errors.Annotate(err, "while listing files to back up")
	}
	dumper, err := getDBDumper(dbInfo)
	if err != nil {
		return errors.Annotate(err, "while preparing for DB dump")
	}
	args := createArgs{filesToBackUp, dumper, metadataFile}
	result, err := runCreate(&args)
	if err != nil {
		return errors.Annotate(err, "while creating backup archive")
	}
	defer result.archiveFile.Close()

	// Finalize the metadata.
	err = finishMeta(meta, result)
	if err != nil {
		return errors.Annotate(err, "while updating metadata")
	}

	// Store the archive.
	err = storeArchive(b.storage, meta, result.archiveFile)
	if err != nil {
		return errors.Annotate(err, "while storing backup archive")
	}

	return nil
}

// Get pulls the associated metadata and archive file from environment storage.
func (b *backups) Get(id string) (*Metadata, io.ReadCloser, error) {
	rawmeta, archiveFile, err := b.storage.Get(id)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	meta, ok := rawmeta.(*Metadata)
	if !ok {
		return nil, nil, errors.New("did not get a backups.Metadata value from storage")
	}

	return meta, archiveFile, nil
}

// List returns the metadata for all stored backups.
func (b *backups) List() ([]*Metadata, error) {
	metaList, err := b.storage.List()
	if err != nil {
		return nil, errors.Trace(err)
	}
	result := make([]*Metadata, len(metaList))
	for i, meta := range metaList {
		m, ok := meta.(*Metadata)
		if !ok {
			msg := "expected backups.Metadata value from storage for %q, got %T"
			return nil, errors.Errorf(msg, meta.ID(), meta)
		}
		result[i] = m
	}
	return result, nil
}

// Remove deletes the backup from storage.
func (b *backups) Remove(id string) error {
	return errors.Trace(b.storage.Remove(id))
}
<commit_msg>Clean up a doc comment.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// backups contains all the stand-alone backup-related functionality for
// juju state.
package backups

import (
	"io"
	"time"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils/filestorage"
)

var logger = loggo.GetLogger("juju.state.backups")

var (
	getFilesToBackUp = GetFilesToBackUp
	getDBDumper      = NewDBDumper
	runCreate        = create
	finishMeta       = func(meta *Metadata, result *createResult) error {
		return meta.MarkComplete(result.size, result.checksum)
	}
	storeArchive = StoreArchive
)

// StoreArchive sends the backup archive and its metadata to storage.
// It also sets the metadata's ID and Stored values.
func StoreArchive(stor filestorage.FileStorage, meta *Metadata, file io.Reader) error {
	id, err := stor.Add(meta, file)
	if err != nil {
		return errors.Trace(err)
	}
	meta.SetID(id)
	stored, err := stor.Metadata(id)
	if err != nil {
		return errors.Trace(err)
	}
	meta.SetStored(stored.Stored())
	return nil
}

// Backups is an abstraction around all juju backup-related functionality.
type Backups interface {

	// Create creates and stores a new juju backup archive.  It updates
	// the provided metadata.
	Create(meta *Metadata, paths Paths, dbInfo DBInfo) error

	// Get returns the metadata and archive file associated with the ID.
	Get(id string) (*Metadata, io.ReadCloser, error)

	// List returns the metadata for all stored backups.
	List() ([]*Metadata, error)

	// Remove deletes the backup from storage.
	Remove(id string) error
}

type backups struct {
	storage filestorage.FileStorage
}

// NewBackups creates a new Backups value using the FileStorage provided.
func NewBackups(stor filestorage.FileStorage) Backups {
	b := backups{
		storage: stor,
	}
	return &b
}

// Create creates and stores a new juju backup archive and updates the
// provided metadata.
func (b *backups) Create(meta *Metadata, paths Paths, dbInfo DBInfo) error {
	meta.Started = time.Now().UTC()

	// The metadata file will not contain the ID or the "finished" data.
	// However, that information is not as critical.  The alternatives
	// are either adding the metadata file to the archive after the fact
	// or adding placeholders here for the finished data and filling
	// them in afterward.  Neither is particularly trivial.
	metadataFile, err := meta.AsJSONBuffer()
	if err != nil {
		return errors.Annotate(err, "while preparing the metadata")
	}

	// Create the archive.
	filesToBackUp, err := getFilesToBackUp("", paths)
	if err != nil {
		return errors.Annotate(err, "while listing files to back up")
	}
	dumper, err := getDBDumper(dbInfo)
	if err != nil {
		return errors.Annotate(err, "while preparing for DB dump")
	}
	args := createArgs{filesToBackUp, dumper, metadataFile}
	result, err := runCreate(&args)
	if err != nil {
		return errors.Annotate(err, "while creating backup archive")
	}
	defer result.archiveFile.Close()

	// Finalize the metadata.
	err = finishMeta(meta, result)
	if err != nil {
		return errors.Annotate(err, "while updating metadata")
	}

	// Store the archive.
	err = storeArchive(b.storage, meta, result.archiveFile)
	if err != nil {
		return errors.Annotate(err, "while storing backup archive")
	}

	return nil
}

// Get pulls the associated metadata and archive file from environment storage.
func (b *backups) Get(id string) (*Metadata, io.ReadCloser, error) {
	rawmeta, archiveFile, err := b.storage.Get(id)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	meta, ok := rawmeta.(*Metadata)
	if !ok {
		return nil, nil, errors.New("did not get a backups.Metadata value from storage")
	}

	return meta, archiveFile, nil
}

// List returns the metadata for all stored backups.
func (b *backups) List() ([]*Metadata, error) {
	metaList, err := b.storage.List()
	if err != nil {
		return nil, errors.Trace(err)
	}
	result := make([]*Metadata, len(metaList))
	for i, meta := range metaList {
		m, ok := meta.(*Metadata)
		if !ok {
			msg := "expected backups.Metadata value from storage for %q, got %T"
			return nil, errors.Errorf(msg, meta.ID(), meta)
		}
		result[i] = m
	}
	return result, nil
}

// Remove deletes the backup from storage.
func (b *backups) Remove(id string) error {
	return errors.Trace(b.storage.Remove(id))
}
<|endoftext|>


<commit_before>package logic

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"sync"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
)

func main() {
	fmt.Println("vim-go")
}

type Logic struct {
	Rules              []*Rule
	state              map[string]interface{}
	ActionProgressChan chan ActionProgress
	sync.RWMutex
}

func NewLogic() *Logic {
	l := &Logic{
		state:              make(map[string]interface{}),
		ActionProgressChan: make(chan ActionProgress, 100),
	}
	return l
}

func (l *Logic) AddRule(name string) *Rule {
	r := &Rule{Name_: name, Uuid_: uuid.New().String()}
	l.Lock()
	defer l.Unlock()
	l.Rules = append(l.Rules, r)
	return r
}

func (l *Logic) SetState(s map[string]interface{}) {
	l.Lock()
	l.state = s
	l.Unlock()
}

func (l *Logic) EvaluateRules() {
	for _, rule := range l.Rules {
		evaluation := l.evaluateRule(rule)
		if evaluation != rule.CondState() {
			rule.SetCondState(evaluation)
			if evaluation {
				logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running enter actions")
				rule.RunEnter(l.ActionProgressChan)
				rule.Lock()
				rule.Active_ = true
				rule.Unlock()
				continue
			}

			logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running exit actions")
			rule.RunExit(l.ActionProgressChan)
			rule.Lock()
			rule.Active_ = false
			rule.Unlock()
		}
	}
}
func (l *Logic) evaluateRule(r *Rule) bool {
	//TODO if rule.enabled is false return false here??? default enabled to true in existing rules or do migration?
	if len(r.Conditions()) == 0 {
		return false
	}

	if strings.ToLower(r.Operator()) == "and" || r.Operator() == "" {
		return l.evaluateRuleAnd(r)
	}
	if strings.ToLower(r.Operator()) == "or" {
		return l.evaluateRuleOr(r)
	}

	return false
}

func (l *Logic) evaluateRuleAnd(r *Rule) bool {
	for _, cond := range r.Conditions() {
		value, err := l.getValueToEvaluate(cond)
		if err != nil {
			logrus.Error(err)
			return false
		}

		if !cond.Check(value) {
			return false
		}
	}
	return true
}

func (l *Logic) evaluateRuleOr(r *Rule) bool {
	for _, cond := range r.Conditions() {
		value, err := l.getValueToEvaluate(cond)
		if err != nil {
			logrus.Error(err)
			return false
		}

		if cond.Check(value) {
			return true
		}
	}
	return false
}

func (l *Logic) getValueToEvaluate(cond RuleCondition) (interface{}, error) {
	if val, ok := l.state[cond.StatePath()]; ok {
		return val, nil
	}
	return nil, fmt.Errorf("logic: State \"%s\" not found", cond.StatePath())
}

func (l *Logic) SaveRulesToFile(path string) {
	configFile, err := os.Create(path)
	if err != nil {
		logrus.Error("creating config file", err.Error())
		return
	}
	var out bytes.Buffer
	b, err := json.Marshal(l.Rules)

	encoder := json.NewEncoder(configFile)

	encoder.SetIndent("", "\t")
	err = encoder.Encode(l.Rules)

	if err != nil {
		logrus.Error("error marshal json", err)
	}
}

/*
old way:

{
	"name": "All off",
	"enabled": true,
	"uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
	"conditions": [
		{
			"statePath": "Devices.fefe749b.Status",
			"comparator": "==",
			"value": "R1B0",
			"uuid": "efd2bd24-ac50-4147-bdf9-da3dd12c8f8a"
		}
	],
	"enterActions": [
		"c7d352bb-23f4-468c-b476-f76599c09a0d"
	]
},
*/
<commit_msg>Fix logic compile error<commit_after>package logic

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"sync"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
)

func main() {
	fmt.Println("vim-go")
}

type Logic struct {
	Rules              []*Rule
	state              map[string]interface{}
	ActionProgressChan chan ActionProgress
	sync.RWMutex
}

func NewLogic() *Logic {
	l := &Logic{
		state:              make(map[string]interface{}),
		ActionProgressChan: make(chan ActionProgress, 100),
	}
	return l
}

func (l *Logic) AddRule(name string) *Rule {
	r := &Rule{Name_: name, Uuid_: uuid.New().String()}
	l.Lock()
	defer l.Unlock()
	l.Rules = append(l.Rules, r)
	return r
}

func (l *Logic) SetState(s map[string]interface{}) {
	l.Lock()
	l.state = s
	l.Unlock()
}

func (l *Logic) EvaluateRules() {
	for _, rule := range l.Rules {
		evaluation := l.evaluateRule(rule)
		if evaluation != rule.CondState() {
			rule.SetCondState(evaluation)
			if evaluation {
				logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running enter actions")
				rule.RunEnter(l.ActionProgressChan)
				rule.Lock()
				rule.Active_ = true
				rule.Unlock()
				continue
			}

			logrus.Info("Rule: ", rule.Name(), " (", rule.Uuid(), ") - running exit actions")
			rule.RunExit(l.ActionProgressChan)
			rule.Lock()
			rule.Active_ = false
			rule.Unlock()
		}
	}
}
func (l *Logic) evaluateRule(r *Rule) bool {
	//TODO if rule.enabled is false return false here??? default enabled to true in existing rules or do migration?
	if len(r.Conditions()) == 0 {
		return false
	}

	if strings.ToLower(r.Operator()) == "and" || r.Operator() == "" {
		return l.evaluateRuleAnd(r)
	}
	if strings.ToLower(r.Operator()) == "or" {
		return l.evaluateRuleOr(r)
	}

	return false
}

func (l *Logic) evaluateRuleAnd(r *Rule) bool {
	for _, cond := range r.Conditions() {
		value, err := l.getValueToEvaluate(cond)
		if err != nil {
			logrus.Error(err)
			return false
		}

		if !cond.Check(value) {
			return false
		}
	}
	return true
}

func (l *Logic) evaluateRuleOr(r *Rule) bool {
	for _, cond := range r.Conditions() {
		value, err := l.getValueToEvaluate(cond)
		if err != nil {
			logrus.Error(err)
			return false
		}

		if cond.Check(value) {
			return true
		}
	}
	return false
}

func (l *Logic) getValueToEvaluate(cond RuleCondition) (interface{}, error) {
	if val, ok := l.state[cond.StatePath()]; ok {
		return val, nil
	}
	return nil, fmt.Errorf("logic: State \"%s\" not found", cond.StatePath())
}

func (l *Logic) SaveRulesToFile(path string) {
	configFile, err := os.Create(path)
	if err != nil {
		logrus.Error("creating config file", err.Error())
		return
	}
	encoder := json.NewEncoder(configFile)
	encoder.SetIndent("", "\t")
	err = encoder.Encode(l.Rules)
	if err != nil {
		logrus.Error("error marshal json", err)
	}
}

/*
old way:

{
	"name": "All off",
	"enabled": true,
	"uuid": "e8092b86-1261-44cd-ab64-38121df58a79",
	"conditions": [
		{
			"statePath": "Devices.fefe749b.Status",
			"comparator": "==",
			"value": "R1B0",
			"uuid": "efd2bd24-ac50-4147-bdf9-da3dd12c8f8a"
		}
	],
	"enterActions": [
		"c7d352bb-23f4-468c-b476-f76599c09a0d"
	]
},
*/
<|endoftext|>


<commit_before>// Copyright 2018 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package shadowsocks

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"time"

	"github.com/Jigsaw-Code/outline-ss-server/metrics"
	onet "github.com/Jigsaw-Code/outline-ss-server/net"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"github.com/shadowsocks/go-shadowsocks2/socks"
)

func findAccessKey(clientConn onet.DuplexConn, cipherList map[string]shadowaead.Cipher) (string, onet.DuplexConn, error) {
	if len(cipherList) == 0 {
		return "", nil, errors.New("Empty cipher list")
	}
	// replayBuffer saves the bytes read from shadowConn, in order to allow for replays.
	var replayBuffer bytes.Buffer
	// Try each cipher until we find one that authenticates successfully.
	// This assumes that all ciphers are AEAD.
	// TODO: Reorder list to try previously successful ciphers first for the client IP.
	// TODO: Ban and log client IPs with too many failures too quick to protect against DoS.
	for id, cipher := range cipherList {
		logger.Debugf("Trying key %v", id)
		// tmpReader reads first from the replayBuffer and then from clientConn if it needs more
		// bytes. All bytes read from clientConn are saved in replayBuffer for future replays.
		tmpReader := io.MultiReader(bytes.NewReader(replayBuffer.Bytes()), io.TeeReader(clientConn, &replayBuffer))
		cipherReader := NewShadowsocksReader(tmpReader, cipher)
		// Read should read just enough data to authenticate the payload size.
		_, err := cipherReader.Read(make([]byte, 0))
		if err != nil {
			logger.Debugf("Failed key %v: %v", id, err)
			continue
		}
		logger.Debugf("Selected key %v", id)
		// We don't need to keep storing and replaying the bytes anymore, but we don't want to drop
		// those already read into the replayBuffer.
		ssr := NewShadowsocksReader(io.MultiReader(&replayBuffer, clientConn), cipher)
		ssw := NewShadowsocksWriter(clientConn, cipher)
		return id, onet.WrapConn(clientConn, ssr, ssw).(onet.DuplexConn), nil
	}
	return "", nil, fmt.Errorf("could not find valid key")
}

type tcpService struct {
	listener  *net.TCPListener
	ciphers   *map[string]shadowaead.Cipher
	m         metrics.ShadowsocksMetrics
	isRunning bool
}

func NewTCPService(listener *net.TCPListener, ciphers *map[string]shadowaead.Cipher, m metrics.ShadowsocksMetrics) TCPService {
	return &tcpService{listener: listener, ciphers: ciphers, m: m}
}

type TCPService interface {
	Start() error
	Stop() error
}

func (s *tcpService) Start() error {
	s.isRunning = true
	for s.isRunning {
		var clientConn onet.DuplexConn
		clientConn, err := s.listener.AcceptTCP()
		if err != nil {
			if !s.isRunning {
				return nil
			}
			logger.Errorf("Failed to accept: %v", err)
			return err
		}

		go func() (connError *onet.ConnectionError) {
			clientLocation, err := s.m.GetLocation(clientConn.RemoteAddr())
			if err != nil {
				logger.Warningf("Failed location lookup: %v", err)
			}
			logger.Debugf("Got location \"%v\" for IP %v", clientLocation, clientConn.RemoteAddr().String())
			s.m.AddOpenTCPConnection(clientLocation)
			defer func() {
				if r := recover(); r != nil {
					logger.Errorf("Panic in TCP handler: %v", r)
				}
			}()
			connStart := time.Now()
			clientConn.(*net.TCPConn).SetKeepAlive(true)
			keyID := ""
			var proxyMetrics metrics.ProxyMetrics
			clientConn = metrics.MeasureConn(clientConn, &proxyMetrics.ProxyClient, &proxyMetrics.ClientProxy)
			defer func() {
				connEnd := time.Now()
				connDuration := connEnd.Sub(connStart)
				clientConn.Close()
				status := "OK"
				if connError != nil {
					logger.Debugf("TCP Error: %v: %v", connError.Message, connError.Cause)
					status = connError.Status
				}
				logger.Debugf("Done with status %v, duration %v", status, connDuration)
				s.m.AddClosedTCPConnection(clientLocation, keyID, status, proxyMetrics, connDuration)
			}()

			keyID, clientConn, err := findAccessKey(clientConn, *s.ciphers)
			if err != nil {
				return &onet.ConnectionError{"ERR_CIPHER", "Failed to find a valid cipher", err}
			}

			tgtAddr, err := socks.ReadAddr(clientConn)
			if err != nil {
				return &onet.ConnectionError{"ERR_READ_ADDRESS", "Failed to get target address", err}
			}
			tgtTCPAddr, err := net.ResolveTCPAddr("tcp", tgtAddr.String())
			if err != nil {
				return &onet.ConnectionError{"ERR_RESOLVE_ADDRESS", fmt.Sprintf("Failed to resolve target address %v", tgtAddr.String()), err}
			}
			if !tgtTCPAddr.IP.IsGlobalUnicast() {
				return &onet.ConnectionError{"ERR_ADDRESS_INVALID", fmt.Sprintf("Target address is not global unicast: %v", tgtAddr.String()), err}
			}

			tgtTCPConn, err := net.DialTCP("tcp", nil, tgtTCPAddr)
			if err != nil {
				return &onet.ConnectionError{"ERR_CONNECT", "Failed to connect to target", err}
			}
			defer tgtTCPConn.Close()
			tgtTCPConn.SetKeepAlive(true)
			tgtConn := metrics.MeasureConn(tgtTCPConn, &proxyMetrics.ProxyTarget, &proxyMetrics.TargetProxy)

			// TODO: Disable logging in production. This is sensitive.
			logger.Debugf("proxy %s <-> %s", clientConn.RemoteAddr().String(), tgtConn.RemoteAddr().String())
			_, _, err = onet.Relay(clientConn, tgtConn)
			if err != nil {
				return &onet.ConnectionError{"ERR_RELAY", "Failed to relay traffic", err}
			}
			return nil
		}()
	}
	return nil
}

func (s *tcpService) Stop() error {
	s.isRunning = false
	return s.listener.Close()
}
<commit_msg>Log TCP loop errors<commit_after>// Copyright 2018 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package shadowsocks

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"net"
	"time"

	"github.com/Jigsaw-Code/outline-ss-server/metrics"
	onet "github.com/Jigsaw-Code/outline-ss-server/net"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
	"github.com/shadowsocks/go-shadowsocks2/socks"
)

func findAccessKey(clientConn onet.DuplexConn, cipherList map[string]shadowaead.Cipher) (string, onet.DuplexConn, error) {
	if len(cipherList) == 0 {
		return "", nil, errors.New("Empty cipher list")
	}
	// replayBuffer saves the bytes read from shadowConn, in order to allow for replays.
	var replayBuffer bytes.Buffer
	// Try each cipher until we find one that authenticates successfully.
	// This assumes that all ciphers are AEAD.
	// TODO: Reorder list to try previously successful ciphers first for the client IP.
	// TODO: Ban and log client IPs with too many failures too quick to protect against DoS.
	for id, cipher := range cipherList {
		logger.Debugf("Trying key %v", id)
		// tmpReader reads first from the replayBuffer and then from clientConn if it needs more
		// bytes. All bytes read from clientConn are saved in replayBuffer for future replays.
		tmpReader := io.MultiReader(bytes.NewReader(replayBuffer.Bytes()), io.TeeReader(clientConn, &replayBuffer))
		cipherReader := NewShadowsocksReader(tmpReader, cipher)
		// Read should read just enough data to authenticate the payload size.
		_, err := cipherReader.Read(make([]byte, 0))
		if err != nil {
			logger.Debugf("Failed key %v: %v", id, err)
			continue
		}
		logger.Debugf("Selected key %v", id)
		// We don't need to keep storing and replaying the bytes anymore, but we don't want to drop
		// those already read into the replayBuffer.
		ssr := NewShadowsocksReader(io.MultiReader(&replayBuffer, clientConn), cipher)
		ssw := NewShadowsocksWriter(clientConn, cipher)
		return id, onet.WrapConn(clientConn, ssr, ssw).(onet.DuplexConn), nil
	}
	return "", nil, fmt.Errorf("could not find valid key")
}

type tcpService struct {
	listener  *net.TCPListener
	ciphers   *map[string]shadowaead.Cipher
	m         metrics.ShadowsocksMetrics
	isRunning bool
}

func NewTCPService(listener *net.TCPListener, ciphers *map[string]shadowaead.Cipher, m metrics.ShadowsocksMetrics) TCPService {
	return &tcpService{listener: listener, ciphers: ciphers, m: m}
}

type TCPService interface {
	Start()
	Stop() error
}

func (s *tcpService) Start() {
	s.isRunning = true
	for s.isRunning {
		var clientConn onet.DuplexConn
		clientConn, err := s.listener.AcceptTCP()
		if err != nil {
			if !s.isRunning {
				return
			}
			logger.Errorf("Failed to accept: %v", err)
		}

		go func() (connError *onet.ConnectionError) {
			clientLocation, err := s.m.GetLocation(clientConn.RemoteAddr())
			if err != nil {
				logger.Warningf("Failed location lookup: %v", err)
			}
			logger.Debugf("Got location \"%v\" for IP %v", clientLocation, clientConn.RemoteAddr().String())
			s.m.AddOpenTCPConnection(clientLocation)
			defer func() {
				if r := recover(); r != nil {
					logger.Errorf("Panic in TCP handler: %v", r)
				}
			}()
			connStart := time.Now()
			clientConn.(*net.TCPConn).SetKeepAlive(true)
			keyID := ""
			var proxyMetrics metrics.ProxyMetrics
			clientConn = metrics.MeasureConn(clientConn, &proxyMetrics.ProxyClient, &proxyMetrics.ClientProxy)
			defer func() {
				connEnd := time.Now()
				connDuration := connEnd.Sub(connStart)
				clientConn.Close()
				status := "OK"
				if connError != nil {
					logger.Debugf("TCP Error: %v: %v", connError.Message, connError.Cause)
					status = connError.Status
				}
				logger.Debugf("Done with status %v, duration %v", status, connDuration)
				s.m.AddClosedTCPConnection(clientLocation, keyID, status, proxyMetrics, connDuration)
			}()

			keyID, clientConn, err := findAccessKey(clientConn, *s.ciphers)
			if err != nil {
				return &onet.ConnectionError{"ERR_CIPHER", "Failed to find a valid cipher", err}
			}

			tgtAddr, err := socks.ReadAddr(clientConn)
			if err != nil {
				return &onet.ConnectionError{"ERR_READ_ADDRESS", "Failed to get target address", err}
			}
			tgtTCPAddr, err := net.ResolveTCPAddr("tcp", tgtAddr.String())
			if err != nil {
				return &onet.ConnectionError{"ERR_RESOLVE_ADDRESS", fmt.Sprintf("Failed to resolve target address %v", tgtAddr.String()), err}
			}
			if !tgtTCPAddr.IP.IsGlobalUnicast() {
				return &onet.ConnectionError{"ERR_ADDRESS_INVALID", fmt.Sprintf("Target address is not global unicast: %v", tgtAddr.String()), err}
			}

			tgtTCPConn, err := net.DialTCP("tcp", nil, tgtTCPAddr)
			if err != nil {
				return &onet.ConnectionError{"ERR_CONNECT", "Failed to connect to target", err}
			}
			defer tgtTCPConn.Close()
			tgtTCPConn.SetKeepAlive(true)
			tgtConn := metrics.MeasureConn(tgtTCPConn, &proxyMetrics.ProxyTarget, &proxyMetrics.TargetProxy)

			// TODO: Disable logging in production. This is sensitive.
			logger.Debugf("proxy %s <-> %s", clientConn.RemoteAddr().String(), tgtConn.RemoteAddr().String())
			_, _, err = onet.Relay(clientConn, tgtConn)
			if err != nil {
				return &onet.ConnectionError{"ERR_RELAY", "Failed to relay traffic", err}
			}
			return nil
		}()
	}
}

func (s *tcpService) Stop() error {
	s.isRunning = false
	return s.listener.Close()
}
<|endoftext|>


<commit_before>package consultant

import (
	"errors"
	"fmt"
	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/watch"
	"github.com/myENA/consultant/log"
	"math"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

// SiblingCallback is the prototype for a callback that can be registered in a SiblingLocator.  It will be called
// whenever a running watcher receives a new service list from Consul, or optionally when a call to Current is made.
//
// "index" will be "math.MaxUint64" when called using "Current"
type SiblingCallback func(index uint64, siblings Siblings)

// Sibling is a thread-safe representation of the Consul api.ServiceEntry object returned by the Health().Service() api
type Sibling struct {
	Node    api.Node
	Service api.AgentService
	Checks  api.HealthChecks
}

// Siblings is provided to any callbacks
type Siblings []Sibling

// SiblingLocatorConfig is used to construct a SiblingLocator.  All values except ServiceTags are required.
type SiblingLocatorConfig struct {
	ServiceID   string // REQUIRED ID of service you want to find siblings for.  Used to exclude local service from responses
	ServiceName string // REQUIRED name of service

	NodeName    string   // OPTIONAL name of node where service was registered.  Used to exclude local service from responses.  Will use node client is connected to if not defined.
	ServiceTags []string // OPTIONAL tags to require when looking for siblings
	AllowStale  bool     // OPTIONAL allow "stale" values
	Datacenter  string   // OPTIONAL consul datacenter.  Will use value from Client if left blank
	Token       string   // OPTIONAL consul acl token.  Will use value from Client if left blank
}

// SiblingLocator provides a way for a local service to find other services registered in Consul that share it's name
// and tags (if any).
type SiblingLocator struct {
	mu sync.Mutex

	client *Client
	log    log.Logger

	config *SiblingLocatorConfig

	callbacks        map[string]SiblingCallback
	lazyCallbackName uint64

	wp *watch.Plan
}

func NewSiblingLocator(client *Client, config SiblingLocatorConfig) (*SiblingLocator, error) {
	// construct new sibling locator
	sl := &SiblingLocator{
		client:    client,
		config:    &config,
		callbacks: make(map[string]SiblingCallback),
	}

	// verify service id is set
	sl.config.ServiceID = strings.TrimSpace(sl.config.ServiceID)
	if "" == sl.config.ServiceID {
		return nil, errors.New("\"ServiceID\" cannot be empty")
	}

	// verify service name is set
	sl.config.ServiceName = strings.TrimSpace(sl.config.ServiceName)
	if "" == sl.config.ServiceName {
		return nil, errors.New("\"ServiceName\" cannot be empty")
	}

	// verify node name is set, using client node if not
	sl.config.NodeName = strings.TrimSpace(sl.config.NodeName)
	if "" == sl.config.NodeName {
		sl.config.NodeName = sl.client.MyNode()
	}

	// verify datacenter is set, using client datacenter if not
	sl.config.Datacenter = strings.TrimSpace(sl.config.Datacenter)
	if "" == sl.config.Datacenter {
		sl.config.Datacenter = sl.client.config.Datacenter
	}

	// verify token is set, using client token if not
	sl.config.Token = strings.TrimSpace(sl.config.Token)
	if "" == sl.config.Token {
		sl.config.Token = sl.client.config.Token
	}

	// create copy of tags, if necessary
	if nil == sl.config.ServiceTags || 0 == len(sl.config.ServiceTags) {
		sl.config.ServiceTags = make([]string, 0)
	} else {
		tmp := make([]string, len(sl.config.ServiceTags))
		copy(tmp, sl.config.ServiceTags)
		sl.config.ServiceTags = tmp
	}

	// set up log slugs
	sl.log = log.New(fmt.Sprintf("sibling-locator-%s", sl.config.ServiceName))

	return sl, nil
}

// NewSiblingLocatorWithCatalogService will construct a SiblingLocator from a consul api catalog service struct
func NewSiblingLocatorWithCatalogService(c *Client, cs *api.CatalogService) (*SiblingLocator, error) {
	conf := &SiblingLocatorConfig{
		NodeName:    cs.Node,
		ServiceID:   cs.ServiceID,
		ServiceName: cs.ServiceName,
	}

	if nil == cs.ServiceTags {
		conf.ServiceTags = make([]string, 0)
	} else {
		conf.ServiceTags = make([]string, len(cs.ServiceTags))
		copy(conf.ServiceTags, cs.ServiceTags)
	}

	return NewSiblingLocator(c, *conf)
}

// NewSiblingLocatorWithAgentService will construct a SiblingLocator from a consul api node and agent service struct
func NewSiblingLocatorWithAgentService(c *Client, n *api.Node, as *api.AgentService) (*SiblingLocator, error) {
	conf := &SiblingLocatorConfig{
		NodeName:    n.Node,
		ServiceID:   as.ID,
		ServiceName: as.Service,
	}

	if nil == as.Tags {
		conf.ServiceTags = make([]string, 0)
	} else {
		conf.ServiceTags = make([]string, len(as.Tags))
		copy(conf.ServiceTags, as.Tags)
	}

	return NewSiblingLocator(c, *conf)
}

func (sl *SiblingLocator) AddCallback(name string, cb SiblingCallback) string {
	sl.mu.Lock()

	name = strings.TrimSpace(name)
	if name == "" {
		name = strconv.FormatUint(sl.lazyCallbackName, 10)
		sl.lazyCallbackName++
	}

	sl.callbacks[name] = cb

	sl.mu.Unlock()

	return name
}

func (sl *SiblingLocator) RemoveCallback(name string) {
	sl.mu.Lock()
	delete(sl.callbacks, name)
	sl.mu.Unlock()
}

// StartWatcher will spin up a Consul WatchPlan that watches for other registered services with the same name
// and set of tags.
//
// - passingOnly will limit the response to only registrations deemed "healthy"
func (sl *SiblingLocator) StartWatcher(passingOnly bool) error {
	sl.mu.Lock()
	if sl.wp != nil && !sl.wp.IsStopped() {
		sl.log.Print("watcher is already running")
		sl.mu.Unlock()
		return nil
	}

	var err error
	var tag string

	if nil != sl.config.ServiceTags && 1 == len(sl.config.ServiceTags) {
		tag = sl.config.ServiceTags[0]
	}

	// try to build watchplan
	sl.wp, err = WatchService(sl.config.ServiceName, tag, passingOnly, sl.config.AllowStale, sl.config.Datacenter, sl.config.Token)
	if err != nil {
		sl.mu.Unlock()
		return fmt.Errorf("unable to create watch plan: %v", err)
	}
	sl.wp.Handler = sl.watchHandler

	go func() {
		err := sl.wp.Run(sl.client.config.Address)
		sl.mu.Lock()
		if err != nil {
			sl.log.Printf("Watch Plan stopped with error: %s", err)
		} else {
			sl.log.Print("Watch Plan stopped without error")
		}
		sl.wp = nil
		sl.mu.Unlock()
	}()

	sl.mu.Unlock()

	return nil
}

// RemoveCallbacks will empty out the map of registered callbacks
func (sl *SiblingLocator) RemoveCallbacks() {
	sl.mu.Lock()
	sl.callbacks = make(map[string]SiblingCallback)
	sl.mu.Unlock()
}

// StopWatcher will stop the sibling watchplan.  If the plan was previously stopped, this is a noop.
func (sl *SiblingLocator) StopWatcher() {
	sl.mu.Lock()
	if sl.wp != nil && !sl.wp.IsStopped() {
		sl.wp.Stop()
		sl.wp = nil
	}
	sl.mu.Unlock()
}

// Current will immediately execute a Health().Service() call, returning and optionally sending the result to
// any registered callbacks
//
// - passingOnly will limit the response to only registrations deemed "healthy"
//
// - sendToCallbacks will send the results to any callbacks registered at time of execution.  Your callbacks can
// determine the difference between a watcher update and a "Current" call by looking for math.MaxUint64 as the index value
func (sl *SiblingLocator) Current(passingOnly, sendToCallbacks bool) (Siblings, error) {
	var tag string
	if nil != sl.config.ServiceTags && 1 == len(sl.config.ServiceTags) {
		tag = sl.config.ServiceTags[0]
	}

	svcs, _, err := sl.client.Health().Service(sl.config.ServiceName, tag, passingOnly, &api.QueryOptions{
		Datacenter: sl.config.Datacenter,
		Token:      sl.config.Token,
		AllowStale: sl.config.AllowStale,
	})
	if err != nil {
		return nil, fmt.Errorf("unable to locate current siblings: %v", err)
	}

	if sendToCallbacks {
		sl.sendToCallbacks(math.MaxUint64, svcs)
	}

	return buildSiblingList(sl.config.NodeName, sl.config.ServiceID, sl.config.ServiceTags, svcs), nil
}

func (sl *SiblingLocator) watchHandler(index uint64, data interface{}) {
	svcs, ok := data.([]*api.ServiceEntry)
	if !ok {
		sl.log.Printf("Watch Handler expected to see \"[]*api.ServiceEntry\", got "+
			"\"%s\" instead...",
			reflect.TypeOf(data).Kind().String())
		return
	}

	sl.sendToCallbacks(index, svcs)
}

func (sl *SiblingLocator) sendToCallbacks(index uint64, svcs []*api.ServiceEntry) {
	sl.mu.Lock()
	for _, receiver := range sl.callbacks {
		go receiver(index, buildSiblingList(sl.config.NodeName, sl.config.ServiceID, sl.config.ServiceTags, svcs))
	}
	sl.mu.Unlock()
}

func buildSiblingList(localNode, localID string, tags []string, svcs []*api.ServiceEntry) Siblings {
	siblings := make(Siblings, 0)

serviceLoop:
	for _, svc := range svcs {
		// omit myself
		if svc.Node.Node == localNode && svc.Service.ID == localID {
			continue serviceLoop
		}

	tagLoop:
		for _, t := range tags {
			for _, st := range svc.Service.Tags {
				if t == st {
					continue tagLoop
				}
			}
			continue serviceLoop
		}

		// add siblings
		siblings = append(siblings, buildSibling(svc))
	}

	return siblings
}

func buildSibling(svc *api.ServiceEntry) Sibling {
	node := *svc.Node
	service := *svc.Service

	tmp := make(map[string]string)
	for k, v := range node.TaggedAddresses {
		tmp[k] = v
	}
	node.TaggedAddresses = tmp

	tmp = make(map[string]string)
	for k, v := range node.Meta {
		tmp[k] = v
	}
	node.Meta = tmp

	tmp1 := make([]string, len(service.Tags))
	copy(tmp1, service.Tags)
	service.Tags = tmp1

	checks := make(api.HealthChecks, len(svc.Checks))
	for i, c := range svc.Checks {
		checks[i] = new(api.HealthCheck)
		*checks[i] = *c
	}

	return Sibling{
		Node:    node,
		Service: service,
		Checks:  checks,
	}
}
<commit_msg>simplifying sibling locator implementation<commit_after>package consultant

import (
	"errors"
	"fmt"
	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/watch"
	"github.com/myENA/consultant/log"
	"math"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

// SiblingCallback is the prototype for a callback that can be registered in a SiblingLocator.  It will be called
// whenever a running watcher receives a new service list from Consul, or optionally when a call to Current is made.
//
// "index" will be "math.MaxUint64" when called using "Current"
type SiblingCallback func(index uint64, siblings Siblings)

// Sibling
type Sibling struct {
	Node    api.Node
	Service api.AgentService
	Checks  api.HealthChecks
}

// Siblings is provided to any callbacks
type Siblings []Sibling

// SiblingLocatorConfig is used to construct a SiblingLocator.  All values except ServiceTags are required.
type SiblingLocatorConfig struct {
	ServiceID   string // REQUIRED ID of service you want to find siblings for.  Used to exclude local service from responses
	ServiceName string // REQUIRED name of service

	NodeName    string   // OPTIONAL name of node where service was registered.  Used to exclude local service from responses.  Will use node client is connected to if not defined.
	ServiceTags []string // OPTIONAL tags to require when looking for siblings
	AllowStale  bool     // OPTIONAL allow "stale" values
	Datacenter  string   // OPTIONAL consul datacenter.  Will use value from Client if left blank
	Token       string   // OPTIONAL consul acl token.  Will use value from Client if left blank
}

// DEPRECATED - This will be replaced in the near future.
//
// SiblingLocator provides a way for a local service to find other services registered in Consul that share it's name
// and tags (if any).
type SiblingLocator struct {
	mu sync.Mutex

	client *Client
	log    log.Logger

	config *SiblingLocatorConfig

	callbacks        map[string]SiblingCallback
	lazyCallbackName uint64

	wp *watch.Plan
}

func NewSiblingLocator(client *Client, config SiblingLocatorConfig) (*SiblingLocator, error) {
	// construct new sibling locator
	sl := &SiblingLocator{
		client:    client,
		config:    &config,
		callbacks: make(map[string]SiblingCallback),
	}

	// verify service id is set
	sl.config.ServiceID = strings.TrimSpace(sl.config.ServiceID)
	if "" == sl.config.ServiceID {
		return nil, errors.New("\"ServiceID\" cannot be empty")
	}

	// verify service name is set
	sl.config.ServiceName = strings.TrimSpace(sl.config.ServiceName)
	if "" == sl.config.ServiceName {
		return nil, errors.New("\"ServiceName\" cannot be empty")
	}

	// verify node name is set, using client node if not
	sl.config.NodeName = strings.TrimSpace(sl.config.NodeName)
	if "" == sl.config.NodeName {
		sl.config.NodeName = sl.client.MyNode()
	}

	// verify datacenter is set, using client datacenter if not
	sl.config.Datacenter = strings.TrimSpace(sl.config.Datacenter)
	if "" == sl.config.Datacenter {
		sl.config.Datacenter = sl.client.config.Datacenter
	}

	// verify token is set, using client token if not
	sl.config.Token = strings.TrimSpace(sl.config.Token)
	if "" == sl.config.Token {
		sl.config.Token = sl.client.config.Token
	}

	// create copy of tags, if necessary
	if nil == sl.config.ServiceTags || 0 == len(sl.config.ServiceTags) {
		sl.config.ServiceTags = make([]string, 0)
	} else {
		tmp := make([]string, len(sl.config.ServiceTags))
		copy(tmp, sl.config.ServiceTags)
		sl.config.ServiceTags = tmp
	}

	// set up log slugs
	sl.log = log.New(fmt.Sprintf("sibling-locator-%s", sl.config.ServiceName))

	return sl, nil
}

// NewSiblingLocatorWithCatalogService will construct a SiblingLocator from a consul api catalog service struct
func NewSiblingLocatorWithCatalogService(c *Client, cs *api.CatalogService) (*SiblingLocator, error) {
	conf := &SiblingLocatorConfig{
		NodeName:    cs.Node,
		ServiceID:   cs.ServiceID,
		ServiceName: cs.ServiceName,
	}

	if nil == cs.ServiceTags {
		conf.ServiceTags = make([]string, 0)
	} else {
		conf.ServiceTags = make([]string, len(cs.ServiceTags))
		copy(conf.ServiceTags, cs.ServiceTags)
	}

	return NewSiblingLocator(c, *conf)
}

// NewSiblingLocatorWithAgentService will construct a SiblingLocator from a consul api node and agent service struct
func NewSiblingLocatorWithAgentService(c *Client, n *api.Node, as *api.AgentService) (*SiblingLocator, error) {
	conf := &SiblingLocatorConfig{
		NodeName:    n.Node,
		ServiceID:   as.ID,
		ServiceName: as.Service,
	}

	if nil == as.Tags {
		conf.ServiceTags = make([]string, 0)
	} else {
		conf.ServiceTags = make([]string, len(as.Tags))
		copy(conf.ServiceTags, as.Tags)
	}

	return NewSiblingLocator(c, *conf)
}

func (sl *SiblingLocator) AddCallback(name string, cb SiblingCallback) string {
	sl.mu.Lock()

	name = strings.TrimSpace(name)
	if name == "" {
		name = strconv.FormatUint(sl.lazyCallbackName, 10)
		sl.lazyCallbackName++
	}

	sl.callbacks[name] = cb

	sl.mu.Unlock()

	return name
}

func (sl *SiblingLocator) RemoveCallback(name string) {
	sl.mu.Lock()
	delete(sl.callbacks, name)
	sl.mu.Unlock()
}

// StartWatcher will spin up a Consul WatchPlan that watches for other registered services with the same name
// and set of tags.
//
// - passingOnly will limit the response to only registrations deemed "healthy"
func (sl *SiblingLocator) StartWatcher(passingOnly bool) error {
	sl.mu.Lock()
	if sl.wp != nil && !sl.wp.IsStopped() {
		sl.log.Print("watcher is already running")
		sl.mu.Unlock()
		return nil
	}

	var err error
	var tag string

	if nil != sl.config.ServiceTags && 1 == len(sl.config.ServiceTags) {
		tag = sl.config.ServiceTags[0]
	}

	// try to build watchplan
	sl.wp, err = WatchService(sl.config.ServiceName, tag, passingOnly, sl.config.AllowStale, sl.config.Datacenter, sl.config.Token)
	if err != nil {
		sl.mu.Unlock()
		return fmt.Errorf("unable to create watch plan: %v", err)
	}
	sl.wp.Handler = sl.watchHandler

	go func() {
		err := sl.wp.Run(sl.client.config.Address)
		sl.mu.Lock()
		if err != nil {
			sl.log.Printf("Watch Plan stopped with error: %s", err)
		} else {
			sl.log.Print("Watch Plan stopped without error")
		}
		sl.wp = nil
		sl.mu.Unlock()
	}()

	sl.mu.Unlock()

	return nil
}

// RemoveCallbacks will empty out the map of registered callbacks
func (sl *SiblingLocator) RemoveCallbacks() {
	sl.mu.Lock()
	sl.callbacks = make(map[string]SiblingCallback)
	sl.mu.Unlock()
}

// StopWatcher will stop the sibling watchplan.  If the plan was previously stopped, this is a noop.
func (sl *SiblingLocator) StopWatcher() {
	sl.mu.Lock()
	if sl.wp != nil && !sl.wp.IsStopped() {
		sl.wp.Stop()
		sl.wp = nil
	}
	sl.mu.Unlock()
}

// Current will immediately execute a Health().Service() call, returning and optionally sending the result to
// any registered callbacks
//
// - passingOnly will limit the response to only registrations deemed "healthy"
//
// - sendToCallbacks will send the results to any callbacks registered at time of execution.  Your callbacks can
// determine the difference between a watcher update and a "Current" call by looking for math.MaxUint64 as the index value
func (sl *SiblingLocator) Current(passingOnly, sendToCallbacks bool) (Siblings, error) {
	var tag string
	if nil != sl.config.ServiceTags && 1 == len(sl.config.ServiceTags) {
		tag = sl.config.ServiceTags[0]
	}

	svcs, _, err := sl.client.Health().Service(sl.config.ServiceName, tag, passingOnly, &api.QueryOptions{
		Datacenter: sl.config.Datacenter,
		Token:      sl.config.Token,
		AllowStale: sl.config.AllowStale,
	})
	if err != nil {
		return nil, fmt.Errorf("unable to locate current siblings: %v", err)
	}

	if sendToCallbacks {
		sl.sendToCallbacks(math.MaxUint64, svcs)
	}

	return buildSiblingList(sl.config.NodeName, sl.config.ServiceID, sl.config.ServiceTags, svcs), nil
}

func (sl *SiblingLocator) watchHandler(index uint64, data interface{}) {
	svcs, ok := data.([]*api.ServiceEntry)
	if !ok {
		sl.log.Printf("Watch Handler expected to see \"[]*api.ServiceEntry\", got "+
			"\"%s\" instead...",
			reflect.TypeOf(data).Kind().String())
		return
	}

	sl.sendToCallbacks(index, svcs)
}

func (sl *SiblingLocator) sendToCallbacks(index uint64, svcs []*api.ServiceEntry) {
	sl.mu.Lock()
	for _, receiver := range sl.callbacks {
		go receiver(index, buildSiblingList(sl.config.NodeName, sl.config.ServiceID, sl.config.ServiceTags, svcs))
	}
	sl.mu.Unlock()
}

func buildSiblingList(localNode, localID string, tags []string, svcs []*api.ServiceEntry) Siblings {
	siblings := make(Siblings, 0)

serviceLoop:
	for _, svc := range svcs {
		// omit myself
		if svc.Node.Node == localNode && svc.Service.ID == localID {
			continue serviceLoop
		}

	tagLoop:
		for _, t := range tags {
			for _, st := range svc.Service.Tags {
				if t == st {
					continue tagLoop
				}
			}
			continue serviceLoop
		}

		// add siblings
		siblings = append(siblings, buildSibling(svc))
	}

	return siblings
}

func buildSibling(svc *api.ServiceEntry) Sibling {
	return Sibling{
		*svc.Node,
		*svc.Service,
		svc.Checks,
	}
}
<|endoftext|>


<commit_before>package simpleyaml

import (
	"testing"
)

var data = []byte(`
name: smallfish
age: 99
float: 3.14159
bool: true
0: IntKey
emails:
   - xxx@xx.com
   - yyy@yy.com
bb:
    cc:
        dd:
            - 111
            - 222
            - 333
        ee: aaa
`)

func TestBool(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("bool").Bool()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != true {
		t.Fatal("match bool failed")
	}
}

func TestString(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("name").String()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != "smallfish" {
		t.Fatal("match name failed")
	}
}

func TestStringFromIntKey(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get(0).String()
	if err != nil {
		t.Fatal("get yaml failed")
	}

	t.Log(v)
	if v != "IntKey" {
		t.Fatal("match IntKey failed")
	}
}

func TestFloat(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}

	v, err := y.Get("float").Float()
	if err != nil {
		t.Fatal("get yaml failed", err)
	}
	t.Log(v)
	if v != 3.14159 {
		t.Fatal("match float failed")
	}
}

func TestInt(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("age").Int()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != 99 {
		t.Fatal("match age failed")
	}
}

func TestGetIndex(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("bb").Get("cc").Get("dd").GetIndex(1).Int()
	t.Log(v)
	if err != nil {
		t.Fatal("match bb.cc.ee[1] failed")
	}
}

func TestString2(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("bb").Get("cc").Get("ee").String()
	t.Log(v)
	if err != nil {
		t.Fatal("match bb.cc.ee failed")
	}
	if v != "aaa" {
		t.Fatal("bb.cc.ee not equal bbb")
	}
}

func TestGetPath(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.GetPath("bb", "cc", "ee").String()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != "aaa" {
		t.Fatal("aa.bb.cc.ee not equal bbb")
	}
}

func TestArray(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("emails").Array()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if len(v) != 2 {
		t.Fatal("emails length not equal 2")
	}
}

func TestMap(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	if !y.IsMap() {
		t.Fatal("map check failed")
	}

	keys, err := y.GetMapKeys()
	if err != nil {
		t.Fatal("get keys from map is failed")
	}
	if len(keys) != 6 {
		t.Fatal("fail to check number of keys")
	}
}

<commit_msg>add IsFound testcase #7<commit_after>package simpleyaml

import (
	"testing"
)

var data = []byte(`
name: smallfish
age: 99
float: 3.14159
bool: true
0: IntKey
emails:
   - xxx@xx.com
   - yyy@yy.com
bb:
    cc:
        dd:
            - 111
            - 222
            - 333
        ee: aaa
`)

func TestBool(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("bool").Bool()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != true {
		t.Fatal("match bool failed")
	}
}

func TestString(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("name").String()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != "smallfish" {
		t.Fatal("match name failed")
	}
}

func TestStringFromIntKey(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get(0).String()
	if err != nil {
		t.Fatal("get yaml failed")
	}

	t.Log(v)
	if v != "IntKey" {
		t.Fatal("match IntKey failed")
	}
}

func TestFloat(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}

	v, err := y.Get("float").Float()
	if err != nil {
		t.Fatal("get yaml failed", err)
	}
	t.Log(v)
	if v != 3.14159 {
		t.Fatal("match float failed")
	}
}

func TestInt(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("age").Int()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != 99 {
		t.Fatal("match age failed")
	}
}

func TestGetIndex(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("bb").Get("cc").Get("dd").GetIndex(1).Int()
	t.Log(v)
	if err != nil {
		t.Fatal("match bb.cc.ee[1] failed")
	}
}

func TestString2(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("bb").Get("cc").Get("ee").String()
	t.Log(v)
	if err != nil {
		t.Fatal("match bb.cc.ee failed")
	}
	if v != "aaa" {
		t.Fatal("bb.cc.ee not equal bbb")
	}
}

func TestGetPath(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.GetPath("bb", "cc", "ee").String()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if v != "aaa" {
		t.Fatal("aa.bb.cc.ee not equal bbb")
	}
}

func TestArray(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	v, err := y.Get("emails").Array()
	if err != nil {
		t.Fatal("get yaml failed")
	}
	t.Log(v)
	if len(v) != 2 {
		t.Fatal("emails length not equal 2")
	}
}

func TestMap(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}
	if !y.IsMap() {
		t.Fatal("map check failed")
	}

	keys, err := y.GetMapKeys()
	if err != nil {
		t.Fatal("get keys from map is failed")
	}
	if len(keys) != 6 {
		t.Fatal("fail to check number of keys")
	}
}

func TestIsFound(t *testing.T) {
	y, err := NewYaml(data)
	if err != nil {
		t.Fatal("init yaml failed")
	}

	if !y.Get("name").IsFound() { // name is exists
		t.Fatal("found name failed")
	}

	if y.Get("xx").IsFound() { // xx is not exists
		t.Fatal("found xx failed")
	}

}
<|endoftext|>


<commit_before>// Package notification handles all notifications for a job. This includes
// build and deployment notifications.
package notification

import (
	"leeroy/config"
	"leeroy/logging"
	"log"
)

// Notify send build and deployment notifications for a job.
func Notify(c *config.Config, j *logging.Job, kind string) {
	if kindSupported(kind) == false {
		log.Fatal("unsupported notification type", kind)
		return
	}

	not := notificationFromJob(j, c)
	not.kind = kind

	// always notify the person who comitted
	go email(c, not, j.Email)

	repo, err := c.ConfigForRepo(j.URL)

	if err != nil {
		log.Println("could not find repo", j.URL)
		return
	}

	for _, n := range repo.Notify {
		switch n.Service {
		case "email":
			// Arguments for email are the mail addresses to notify
			for mail := range n.Arguments {
				go email(c, not, mail)
			}
		case "slack":
			go slack(not, n.Arguments["endpoint"], n.Arguments["channel"])
		case "hipchat":
			go hipchat(not, n.Arguments["key"], n.Arguments["channel"])
		case "campfire":
			go campfire(not, n.Arguments["id"], n.Arguments["room"], n.Arguments["key"])
		default:
			log.Println("Notification not supported", n.Service)
		}
	}
}

// Check if kind is a supported notification type.
func kindSupported(kind string) bool {
	for _, k := range kinds {
		if k == kind {
			return true
		}
	}
	return false
}
<commit_msg>call Notification.render()<commit_after>// Package notification handles all notifications for a job. This includes
// build and deployment notifications.
package notification

import (
	"leeroy/config"
	"leeroy/logging"
	"log"
)

// Notify send build and deployment notifications for a job.
func Notify(c *config.Config, j *logging.Job, kind string) {
	if kindSupported(kind) == false {
		log.Fatal("unsupported notification type", kind)
		return
	}

	not := notificationFromJob(j, c)
	not.kind = kind
	not.render()

	// always notify the person who comitted
	go email(c, not, j.Email)

	repo, err := c.ConfigForRepo(j.URL)

	if err != nil {
		log.Println("could not find repo", j.URL)
		return
	}

	for _, n := range repo.Notify {
		switch n.Service {
		case "email":
			// Arguments for email are the mail addresses to notify
			for mail := range n.Arguments {
				go email(c, not, mail)
			}
		case "slack":
			go slack(not, n.Arguments["endpoint"], n.Arguments["channel"])
		case "hipchat":
			go hipchat(not, n.Arguments["key"], n.Arguments["channel"])
		case "campfire":
			go campfire(not, n.Arguments["id"], n.Arguments["room"], n.Arguments["key"])
		default:
			log.Println("Notification not supported", n.Service)
		}
	}
}

// Check if kind is a supported notification type.
func kindSupported(kind string) bool {
	for _, k := range kinds {
		if k == kind {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package charm_test

import (
	"archive/zip"
	"bytes"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju/go/charm"
	"os"
	"path/filepath"
	"syscall"
)

func (s *S) TestReadDir(c *C) {
	path := s.repo.DirPath("dummy")
	dir, err := charm.ReadDir(path)
	c.Assert(err, IsNil)
	c.Assert(dir.Path, Equals, path)
}

func (s *S) TestReadDirWithoutConfig(c *C) {
	path := s.repo.DirPath("varnish")
	dir, err := charm.ReadDir(path)
	c.Assert(err, IsNil)

	// A lacking config.yaml file still causes a proper
	// Config value to be returned.
	c.Assert(dir.Config().Options, HasLen, 0)
}

func (s *S) TestBundleTo(c *C) {
	dir := s.repo.Dir(c, "dummy")
	path := filepath.Join(c.MkDir(), "bundle.charm")
	file, err := os.Create(path)
	c.Assert(err, IsNil)
	err = dir.BundleTo(file)
	file.Close()
	c.Assert(err, IsNil)

	zipr, err := zip.OpenReader(path)
	c.Assert(err, IsNil)
	defer zipr.Close()

	var metaf, instf, emptyf, revf, symf *zip.File
	for _, f := range zipr.File {
		c.Logf("Bundled file: %s", f.Name)
		switch f.Name {
		case "revision":
			revf = f
		case "metadata.yaml":
			metaf = f
		case "hooks/install":
			instf = f
		case "hooks/symlink":
			symf = f
		case "empty/":
			emptyf = f
		case "build/ignored":
			c.Errorf("bundle includes build/*: %s", f.Name)
		case ".ignored", ".dir/ignored":
			c.Errorf("bundle includes .* entries: %s", f.Name)
		}
	}

	c.Assert(revf, NotNil)
	reader, err := revf.Open()
	c.Assert(err, IsNil)
	data, err := ioutil.ReadAll(reader)
	reader.Close()
	c.Assert(err, IsNil)
	c.Assert(string(data), Equals, "1")

	c.Assert(metaf, NotNil)
	reader, err = metaf.Open()
	c.Assert(err, IsNil)
	meta, err := charm.ReadMeta(reader)
	reader.Close()
	c.Assert(err, IsNil)
	c.Assert(meta.Name, Equals, "dummy")

	c.Assert(instf, NotNil)
	// Despite it being 0751, we pack and unpack it as 0755.
	c.Assert(instf.Mode()&0777, Equals, os.FileMode(0755))

	c.Assert(symf, NotNil)
	c.Assert(symf.Mode()&0777, Equals, os.FileMode(0777))
	reader, err = symf.Open()
	c.Assert(err, IsNil)
	data, err = ioutil.ReadAll(reader)
	reader.Close()
	c.Assert(err, IsNil)
	c.Assert(string(data), Equals, "../target")

	c.Assert(emptyf, NotNil)
	c.Assert(emptyf.Mode()&os.ModeType, Equals, os.ModeDir)
	// Despite it being 0750, we pack and unpack it as 0755.
	c.Assert(emptyf.Mode()&0777, Equals, os.FileMode(0755))
}

func (s *S) TestBundleToWithBadType(c *C) {
	charmDir := s.repo.ClonedDirPath(c, "dummy")
	badFile := filepath.Join(charmDir, "hooks", "badfile")

	// Symlink targeting a path outside of the charm.
	err := os.Symlink("../../target", badFile)
	c.Assert(err, IsNil)

	dir, err := charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	err = dir.BundleTo(&bytes.Buffer{})
	c.Assert(err, ErrorMatches, `symlink "hooks/badfile" links out of charm: "../../target"`)

	// Symlink targeting an absolute path.
	os.Remove(badFile)
	err = os.Symlink("/target", badFile)
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	err = dir.BundleTo(&bytes.Buffer{})
	c.Assert(err, ErrorMatches, `symlink "hooks/badfile" is absolute: "/target"`)

	// Can't bundle special files either.
	os.Remove(badFile)
	err = syscall.Mkfifo(badFile, 0644)
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	err = dir.BundleTo(&bytes.Buffer{})
	c.Assert(err, ErrorMatches, `file is a named pipe: "hooks/badfile"`)
}

func (s *S) TestDirRevisionFile(c *C) {
	charmDir := s.repo.ClonedDirPath(c, "dummy")
	revPath := filepath.Join(charmDir, "revision")

	// Missing revision file
	err := os.Remove(revPath)
	c.Assert(err, IsNil)

	dir, err := charm.ReadDir(charmDir)
	c.Assert(err, IsNil)
	c.Assert(dir.Revision(), Equals, 0)

	// Missing revision file with old revision in metadata
	file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0)
	c.Assert(err, IsNil)
	_, err = file.Write([]byte("\nrevision: 1234\n"))
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)
	c.Assert(dir.Revision(), Equals, 1234)

	// Revision file with bad content
	err = ioutil.WriteFile(revPath, []byte("garbage"), 0666)
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, ErrorMatches, "invalid revision file")
	c.Assert(dir, IsNil)
}

func (s *S) TestDirSetRevision(c *C) {
	dir := s.repo.ClonedDir(c, "dummy")
	c.Assert(dir.Revision(), Equals, 1)
	dir.SetRevision(42)
	c.Assert(dir.Revision(), Equals, 42)

	var b bytes.Buffer
	err := dir.BundleTo(&b)
	c.Assert(err, IsNil)

	bundle, err := charm.ReadBundleBytes(b.Bytes())
	c.Assert(bundle.Revision(), Equals, 42)
}

func (s *S) TestDirSetDiskRevision(c *C) {
	charmDir := s.repo.ClonedDirPath(c, "dummy")
	dir, err := charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	c.Assert(dir.Revision(), Equals, 1)
	dir.SetDiskRevision(42)
	c.Assert(dir.Revision(), Equals, 42)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)
	c.Assert(dir.Revision(), Equals, 42)
}
<commit_msg>cleanup<commit_after>package charm_test

import (
	"archive/zip"
	"bytes"
	"io/ioutil"
	. "launchpad.net/gocheck"
	"launchpad.net/juju/go/charm"
	"os"
	"path/filepath"
	"syscall"
)

func (s *S) TestReadDir(c *C) {
	path := s.repo.DirPath("dummy")
	dir, err := charm.ReadDir(path)
	c.Assert(err, IsNil)
	checkDummy(c, dir, path)
}

func (s *S) TestReadDirWithoutConfig(c *C) {
	path := s.repo.DirPath("varnish")
	dir, err := charm.ReadDir(path)
	c.Assert(err, IsNil)

	// A lacking config.yaml file still causes a proper
	// Config value to be returned.
	c.Assert(dir.Config().Options, HasLen, 0)
}

func (s *S) TestBundleTo(c *C) {
	dir := s.repo.Dir(c, "dummy")
	path := filepath.Join(c.MkDir(), "bundle.charm")
	file, err := os.Create(path)
	c.Assert(err, IsNil)
	err = dir.BundleTo(file)
	file.Close()
	c.Assert(err, IsNil)

	zipr, err := zip.OpenReader(path)
	c.Assert(err, IsNil)
	defer zipr.Close()

	var metaf, instf, emptyf, revf, symf *zip.File
	for _, f := range zipr.File {
		c.Logf("Bundled file: %s", f.Name)
		switch f.Name {
		case "revision":
			revf = f
		case "metadata.yaml":
			metaf = f
		case "hooks/install":
			instf = f
		case "hooks/symlink":
			symf = f
		case "empty/":
			emptyf = f
		case "build/ignored":
			c.Errorf("bundle includes build/*: %s", f.Name)
		case ".ignored", ".dir/ignored":
			c.Errorf("bundle includes .* entries: %s", f.Name)
		}
	}

	c.Assert(revf, NotNil)
	reader, err := revf.Open()
	c.Assert(err, IsNil)
	data, err := ioutil.ReadAll(reader)
	reader.Close()
	c.Assert(err, IsNil)
	c.Assert(string(data), Equals, "1")

	c.Assert(metaf, NotNil)
	reader, err = metaf.Open()
	c.Assert(err, IsNil)
	meta, err := charm.ReadMeta(reader)
	reader.Close()
	c.Assert(err, IsNil)
	c.Assert(meta.Name, Equals, "dummy")

	c.Assert(instf, NotNil)
	// Despite it being 0751, we pack and unpack it as 0755.
	c.Assert(instf.Mode()&0777, Equals, os.FileMode(0755))

	c.Assert(symf, NotNil)
	c.Assert(symf.Mode()&0777, Equals, os.FileMode(0777))
	reader, err = symf.Open()
	c.Assert(err, IsNil)
	data, err = ioutil.ReadAll(reader)
	reader.Close()
	c.Assert(err, IsNil)
	c.Assert(string(data), Equals, "../target")

	c.Assert(emptyf, NotNil)
	c.Assert(emptyf.Mode()&os.ModeType, Equals, os.ModeDir)
	// Despite it being 0750, we pack and unpack it as 0755.
	c.Assert(emptyf.Mode()&0777, Equals, os.FileMode(0755))
}

func (s *S) TestBundleToWithBadType(c *C) {
	charmDir := s.repo.ClonedDirPath(c, "dummy")
	badFile := filepath.Join(charmDir, "hooks", "badfile")

	// Symlink targeting a path outside of the charm.
	err := os.Symlink("../../target", badFile)
	c.Assert(err, IsNil)

	dir, err := charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	err = dir.BundleTo(&bytes.Buffer{})
	c.Assert(err, ErrorMatches, `symlink "hooks/badfile" links out of charm: "../../target"`)

	// Symlink targeting an absolute path.
	os.Remove(badFile)
	err = os.Symlink("/target", badFile)
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	err = dir.BundleTo(&bytes.Buffer{})
	c.Assert(err, ErrorMatches, `symlink "hooks/badfile" is absolute: "/target"`)

	// Can't bundle special files either.
	os.Remove(badFile)
	err = syscall.Mkfifo(badFile, 0644)
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	err = dir.BundleTo(&bytes.Buffer{})
	c.Assert(err, ErrorMatches, `file is a named pipe: "hooks/badfile"`)
}

func (s *S) TestDirRevisionFile(c *C) {
	charmDir := s.repo.ClonedDirPath(c, "dummy")
	revPath := filepath.Join(charmDir, "revision")

	// Missing revision file
	err := os.Remove(revPath)
	c.Assert(err, IsNil)

	dir, err := charm.ReadDir(charmDir)
	c.Assert(err, IsNil)
	c.Assert(dir.Revision(), Equals, 0)

	// Missing revision file with old revision in metadata
	file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0)
	c.Assert(err, IsNil)
	_, err = file.Write([]byte("\nrevision: 1234\n"))
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)
	c.Assert(dir.Revision(), Equals, 1234)

	// Revision file with bad content
	err = ioutil.WriteFile(revPath, []byte("garbage"), 0666)
	c.Assert(err, IsNil)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, ErrorMatches, "invalid revision file")
	c.Assert(dir, IsNil)
}

func (s *S) TestDirSetRevision(c *C) {
	dir := s.repo.ClonedDir(c, "dummy")
	c.Assert(dir.Revision(), Equals, 1)
	dir.SetRevision(42)
	c.Assert(dir.Revision(), Equals, 42)

	var b bytes.Buffer
	err := dir.BundleTo(&b)
	c.Assert(err, IsNil)

	bundle, err := charm.ReadBundleBytes(b.Bytes())
	c.Assert(bundle.Revision(), Equals, 42)
}

func (s *S) TestDirSetDiskRevision(c *C) {
	charmDir := s.repo.ClonedDirPath(c, "dummy")
	dir, err := charm.ReadDir(charmDir)
	c.Assert(err, IsNil)

	c.Assert(dir.Revision(), Equals, 1)
	dir.SetDiskRevision(42)
	c.Assert(dir.Revision(), Equals, 42)

	dir, err = charm.ReadDir(charmDir)
	c.Assert(err, IsNil)
	c.Assert(dir.Revision(), Equals, 42)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math/rand"
	"net"
	"os"
	"time"
)

const (
	BOOTREQUEST = 1
	BOOTREPLY   = 2
)

const (
	OPTION_MSG_TYPE = 0x35
	OPTION_END      = 0xff
)

const (
	MSG_TYPE_DISCOVER = 1
	MSG_TYPE_OFFER    = 2
	MSG_TYPE_REQUEST  = 3
	MSG_TYPE_DECLINE  = 4
	MSG_TYPE_ACK      = 5
	MSG_TYPE_NACK     = 6
	MSG_TYPE_RELEASE  = 7
)

const (
	FLAG_BROADCAST = 1 << 15
)

const (
	PACKET_SIZE    = 548
	HTYPE_ETHERNET = 1
	MAGIC          = 0x63825363
)

type IPv4Address [4]byte

func (a *IPv4Address) String() string {
	return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
}

type DHCPOptions [308]byte

type DHCPPacket struct {
	Op      byte
	Htype   byte
	Hlen    byte
	Hops    byte
	Xid     uint32
	Secs    uint16
	Flags   uint16
	Ciaddr  IPv4Address // client IP address
	Yiaddr  IPv4Address // your IP address
	Siaddr  IPv4Address // server IP address
	Giaddr  IPv4Address // gateway IP address
	Chaddr  [16]byte    // client hardware address
	Bootp   [192]byte
	Magic   uint32
	Options DHCPOptions
}

func (p *DHCPPacket) parseMAC(s string) error {
	hw, err := net.ParseMAC(s)
	if err == nil {
		copy(p.Chaddr[0:6], hw)
	}
	return err
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
		os.Exit(1)
	}
}

func sendPacket(conn net.Conn, p *DHCPPacket) error {
	var buffer bytes.Buffer
	err := binary.Write(&buffer, binary.BigEndian, *p)
	if err != nil {
		return err
	}
	_, err = conn.Write(buffer.Bytes())
	return err
}

func receivePacket(conn *net.UDPConn, timeout time.Duration) (*DHCPPacket, *net.UDPAddr, error) {
	conn.SetReadDeadline(time.Now().Add(timeout))
	var b [1024]byte
	_, remote, err := conn.ReadFromUDP(b[:])
	if err != nil {
		return nil, remote, err
	}
	var p DHCPPacket
	err = binary.Read(bytes.NewReader(b[:]), binary.BigEndian, &p)
	return &p, remote, err
}

func sendUDPPacket(p *DHCPPacket, a string) {
	addr, err := net.ResolveUDPAddr("udp4", a)
	checkError(err)
	conn, err := net.DialUDP("udp4", nil, addr)
	checkError(err)
	defer conn.Close()
	sendPacket(conn, p)
}

func NewDHCPDiscoverPacket() *DHCPPacket {
	p := &DHCPPacket{
		Op:    BOOTREQUEST,
		Htype: HTYPE_ETHERNET,
		Hlen:  6,
		Hops:  0,
		Xid:   rand.Uint32(),
		Secs:  0xffff,
		Flags: FLAG_BROADCAST,
		Magic: MAGIC,
		Options: DHCPOptions{OPTION_MSG_TYPE, 1, MSG_TYPE_DISCOVER,
			OPTION_END},
	}

	return p
}

func showOffer(p *DHCPPacket) {
	fmt.Println("Client IP address :", p.Ciaddr.String())
	fmt.Println("Your IP address   :", p.Yiaddr.String())
	fmt.Println("Server IP address :", p.Siaddr.String())
	fmt.Println("Relay IP address  :", p.Giaddr.String())
	fmt.Println()
}

func main() {
	iface := "wlp3s0"
	mac := ""
	timeout := 5 * time.Second

	// Get interface MAC address
	ifaces, err := net.Interfaces()
	checkError(err)
	for _, i := range ifaces {
		if i.Name == iface {
			mac = i.HardwareAddr.String()
		}
	}
	fmt.Printf("Interface: %s [%s]\n", iface, mac)

	// Set up server
	addr, err := net.ResolveUDPAddr("udp4", ":68")
	checkError(err)
	conn, err := net.ListenUDP("udp4", addr)
	checkError(err)
	defer conn.Close()

	// Send discover packet
	fmt.Println("Send DHCP discover\n")
	p := NewDHCPDiscoverPacket()
	p.parseMAC(mac)
	sendUDPPacket(p, net.IPv4bcast.String() + ":67")

	t := time.Now()
	for time.Since(t) < timeout {
		o, remote, err := receivePacket(conn, timeout)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
			break
		}
		fmt.Println("Receive DHCP offer from", remote.IP.String())
		showOffer(o)
	}
	fmt.Println("No more offers.")
}
<commit_msg>Set network interface in command line<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"os"
	"time"
)

const (
	BOOTREQUEST = 1
	BOOTREPLY   = 2
)

const (
	OPTION_MSG_TYPE = 0x35
	OPTION_END      = 0xff
)

const (
	MSG_TYPE_DISCOVER = 1
	MSG_TYPE_OFFER    = 2
	MSG_TYPE_REQUEST  = 3
	MSG_TYPE_DECLINE  = 4
	MSG_TYPE_ACK      = 5
	MSG_TYPE_NACK     = 6
	MSG_TYPE_RELEASE  = 7
)

const (
	FLAG_BROADCAST = 1 << 15
)

const (
	PACKET_SIZE    = 548
	HTYPE_ETHERNET = 1
	MAGIC          = 0x63825363
)

type IPv4Address [4]byte

func (a *IPv4Address) String() string {
	return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
}

type DHCPOptions [308]byte

type DHCPPacket struct {
	Op      byte
	Htype   byte
	Hlen    byte
	Hops    byte
	Xid     uint32
	Secs    uint16
	Flags   uint16
	Ciaddr  IPv4Address // client IP address
	Yiaddr  IPv4Address // your IP address
	Siaddr  IPv4Address // server IP address
	Giaddr  IPv4Address // gateway IP address
	Chaddr  [16]byte    // client hardware address
	Bootp   [192]byte
	Magic   uint32
	Options DHCPOptions
}

func (p *DHCPPacket) parseMAC(s string) error {
	hw, err := net.ParseMAC(s)
	if err == nil {
		copy(p.Chaddr[0:6], hw)
	}
	return err
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err.Error())
		os.Exit(1)
	}
}

func sendPacket(conn net.Conn, p *DHCPPacket) error {
	var buffer bytes.Buffer
	err := binary.Write(&buffer, binary.BigEndian, *p)
	if err != nil {
		return err
	}
	_, err = conn.Write(buffer.Bytes())
	return err
}

func receivePacket(conn *net.UDPConn, timeout time.Duration) (*DHCPPacket, *net.UDPAddr, error) {
	conn.SetReadDeadline(time.Now().Add(timeout))
	var b [1024]byte
	_, remote, err := conn.ReadFromUDP(b[:])
	if err != nil {
		return nil, remote, err
	}
	var p DHCPPacket
	err = binary.Read(bytes.NewReader(b[:]), binary.BigEndian, &p)
	return &p, remote, err
}

func sendUDPPacket(p *DHCPPacket, a string) {
	addr, err := net.ResolveUDPAddr("udp4", a)
	checkError(err)
	conn, err := net.DialUDP("udp4", nil, addr)
	checkError(err)
	defer conn.Close()
	sendPacket(conn, p)
}

func NewDHCPDiscoverPacket() *DHCPPacket {
	p := &DHCPPacket{
		Op:    BOOTREQUEST,
		Htype: HTYPE_ETHERNET,
		Hlen:  6,
		Hops:  0,
		Xid:   rand.Uint32(),
		Secs:  0xffff,
		Flags: FLAG_BROADCAST,
		Magic: MAGIC,
		Options: DHCPOptions{OPTION_MSG_TYPE, 1, MSG_TYPE_DISCOVER,
			OPTION_END},
	}

	return p
}

func showOffer(p *DHCPPacket) {
	fmt.Println("Client IP address :", p.Ciaddr.String())
	fmt.Println("Your IP address   :", p.Yiaddr.String())
	fmt.Println("Server IP address :", p.Siaddr.String())
	fmt.Println("Relay IP address  :", p.Giaddr.String())
	fmt.Println()
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s <iface>\n", os.Args[0])
	flag.PrintDefaults()
}

func getMAC(s string) (string,error) {
	ifaces, err := net.Interfaces()
	checkError(err)
	for _, i := range ifaces {
		if i.Name == s {
			return i.HardwareAddr.String(),nil
		}
	}
	return "", fmt.Errorf("%s: no such interface", s)
}

func main() {
	flag.Usage = usage
	flag.Parse()

	iface := flag.Arg(0)
	if iface == "" {
		usage()
		os.Exit(1)
	}

	mac := ""
	timeout := 5 * time.Second

	mac,err := getMAC(iface)
	checkError(err)

	fmt.Printf("Interface: %s [%s]\n", iface, mac)

	// Set up server
	addr, err := net.ResolveUDPAddr("udp4", ":68")
	checkError(err)
	conn, err := net.ListenUDP("udp4", addr)
	checkError(err)
	defer conn.Close()

	// Send discover packet
	fmt.Println("Send DHCP discover\n")
	p := NewDHCPDiscoverPacket()
	p.parseMAC(mac)
	sendUDPPacket(p, net.IPv4bcast.String() + ":67")

	t := time.Now()
	for time.Since(t) < timeout {
		o, remote, err := receivePacket(conn, timeout)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err.Error())
			break
		}
		fmt.Println("Receive DHCP offer from", remote.IP.String())
		showOffer(o)
	}
	fmt.Println("No more offers.")
}
<|endoftext|>