diff --git a/glide.lock b/glide.lock index d4bb9669..29a91daa 100644 --- a/glide.lock +++ b/glide.lock @@ -1,6 +1,11 @@ -hash: 521f9762f9a2bb54ab827d2d6e8264d0b3e34321b3b785ea4e38b30fc0ca26c2 -updated: 2017-11-01T16:53:18.861075104-05:00 +hash: 4a298c8c8ed295ed4129f76a616ed15f35c4ed0e86460e917b0a30d399f2d5d1 +updated: 2017-11-06T15:34:24.417738-05:00 imports: +- name: github.com/benschw/srv-lb + version: c87a20cc531f1babdf710dc3db91dd69becb0e8b + subpackages: + - dns + - lb - name: github.com/docker/docker version: ad969f1aa782478725a7f338cf963fa82f484609 subpackages: @@ -32,6 +37,10 @@ imports: version: ad28ea4487f05916463e2423a55166280e8254b5 - name: github.com/hashicorp/go-version version: fc61389e27c71d120f87031ca8c88a3428f372dd +- name: github.com/miekg/dns + version: 822ae18e7187e1bbde923a37081f6c1b8e9ba68a + subpackages: + - internal/socket - name: github.com/opencontainers/runc version: 9d7831e41d3ef428b67685eeb27f2b4a22a92391 subpackages: diff --git a/glide.yaml b/glide.yaml index 6ee9cb9f..8753c674 100644 --- a/glide.yaml +++ b/glide.yaml @@ -8,3 +8,8 @@ import: subpackages: - websocket - package: github.com/hashicorp/go-version +- package: github.com/benschw/srv-lb + subpackages: + - dns + - lb +- package: github.com/miekg/dns diff --git a/resolver/dns.go b/resolver/dns.go new file mode 100644 index 00000000..9626421e --- /dev/null +++ b/resolver/dns.go @@ -0,0 +1,41 @@ +package resolver + +import ( + "strings" + + "github.com/benschw/srv-lb/lb" +) + +// DNSConfig accepts an address and optional load balancer config +type DNSConfig struct { + Addr string + LbCfg *lb.Config +} + +// ResolveSrvAddr returns a load-balanced host:port based on DNS SRV lookup (or `addr` if not a hostname) +func ResolveSrvAddr(dnsCfg DNSConfig) (string, error) { + addr := dnsCfg.Addr + + if v := strings.Split(addr, ":"); len(v) < 2 { + var cfg *lb.Config + var err error + if dnsCfg.LbCfg == nil { + cfg, err = lb.DefaultConfig() + + if err != nil { + return addr, err + } + } else { + cfg = dnsCfg.LbCfg + } + + l := lb.New(cfg, addr) + resolvAddr, err := l.Next() + + if err == nil { + addr = resolvAddr.String() + } + } + + return addr, nil +} diff --git a/resolver/dns_test.go b/resolver/dns_test.go new file mode 100644 index 00000000..0e75285a --- /dev/null +++ b/resolver/dns_test.go @@ -0,0 +1,29 @@ +package resolver + +import ( + "testing" + + "github.com/benschw/srv-lb/lb" +) + +func TestSrvLookup(t *testing.T) { + lbCfg, _ := lb.DefaultConfig() + lbCfg.Strategy = MockStrategy + dnsCfg := DNSConfig{Addr: "foo.example.com", LbCfg: lbCfg} + + addr, _ := ResolveSrvAddr(dnsCfg) + + if addr != "1.2.3.4:1234" { + t.Error("expected address string of 1.2.3.4:1234, got:", addr) + } +} + +func TestIpPassthrough(t *testing.T) { + dnsCfg := DNSConfig{Addr: "10.0.0.1:1234"} + + addr, _ := ResolveSrvAddr(dnsCfg) + + if addr != "10.0.0.1:1234" { + t.Error("expected output to equal input (10.0.0.1:1234), got:", addr) + } +} diff --git a/resolver/lb_mock_strategy.go b/resolver/lb_mock_strategy.go new file mode 100644 index 00000000..499dc5c3 --- /dev/null +++ b/resolver/lb_mock_strategy.go @@ -0,0 +1,30 @@ +package resolver + +import ( + "github.com/benschw/srv-lb/dns" + "github.com/benschw/srv-lb/lb" +) + +// MockStrategy is used for testing DNS load balancing +const MockStrategy lb.StrategyType = "mock" + +// New creates a new instance of the load balancer +func New(lib dns.Lookup) lb.GenericLoadBalancer { + lb := new(MockClb) + lb.dnsLib = lib + return lb +} + +// MockClb contains the dnslib +type MockClb struct { + dnsLib dns.Lookup +} + +// Next gets the next server in the available nodes +func (lb *MockClb) Next(name string) (dns.Address, error) { + return dns.Address{Address: "1.2.3.4", Port: 1234}, nil +} + +func init() { + lb.RegisterStrategy(MockStrategy, New) +} diff --git a/transports/tcp/tcp.go b/transports/tcp/tcp.go index 84d2ade9..60339e02 100644 --- a/transports/tcp/tcp.go +++ b/transports/tcp/tcp.go @@ -4,6 +4,7 @@ import ( "net" "github.com/gliderlabs/logspout/adapters/raw" + "github.com/gliderlabs/logspout/resolver" "github.com/gliderlabs/logspout/router" ) @@ -21,7 +22,12 @@ func rawTCPAdapter(route *router.Route) (router.LogAdapter, error) { type tcpTransport int func (t *tcpTransport) Dial(addr string, options map[string]string) (net.Conn, error) { - raddr, err := net.ResolveTCPAddr("tcp", addr) + daddr, err := resolver.ResolveSrvAddr(resolver.DNSConfig{Addr: addr}) + if err != nil { + return nil, err + } + + raddr, err := net.ResolveTCPAddr("tcp", daddr) if err != nil { return nil, err } diff --git a/transports/tls/tls.go b/transports/tls/tls.go index 4844b0a5..9495d4cc 100644 --- a/transports/tls/tls.go +++ b/transports/tls/tls.go @@ -5,6 +5,7 @@ import ( "net" "github.com/gliderlabs/logspout/adapters/raw" + "github.com/gliderlabs/logspout/resolver" "github.com/gliderlabs/logspout/router" ) @@ -22,7 +23,12 @@ func rawTLSAdapter(route *router.Route) (router.LogAdapter, error) { type tlsTransport int func (t *tlsTransport) Dial(addr string, options map[string]string) (net.Conn, error) { - conn, err := tls.Dial("tcp", addr, nil) + daddr, err := resolver.ResolveSrvAddr(resolver.DNSConfig{Addr: addr}) + if err != nil { + return nil, err + } + + conn, err := tls.Dial("tcp", daddr, nil) if err != nil { return nil, err } diff --git a/transports/udp/udp.go b/transports/udp/udp.go index b960c389..08f133ad 100644 --- a/transports/udp/udp.go +++ b/transports/udp/udp.go @@ -4,6 +4,7 @@ import ( "net" "github.com/gliderlabs/logspout/adapters/raw" + "github.com/gliderlabs/logspout/resolver" "github.com/gliderlabs/logspout/router" ) @@ -26,7 +27,12 @@ func rawUDPAdapter(route *router.Route) (router.LogAdapter, error) { type udpTransport int func (t *udpTransport) Dial(addr string, options map[string]string) (net.Conn, error) { - raddr, err := net.ResolveUDPAddr("udp", addr) + daddr, err := resolver.ResolveSrvAddr(resolver.DNSConfig{Addr: addr}) + if err != nil { + return nil, err + } + + raddr, err := net.ResolveUDPAddr("udp", daddr) if err != nil { return nil, err }