forked from hudl/fargo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request hudl#5 from hudl/discoverDNS
Allow the use of DNS discovery for eureka connections
- Loading branch information
Showing
5 changed files
with
197 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package fargo | ||
|
||
// MIT Licensed (see README.md) - Copyright (c) 2013 Hudl <@Hudl> | ||
|
||
import ( | ||
"fmt" | ||
"github.com/franela/goreq" | ||
"github.com/miekg/dns" | ||
"time" | ||
) | ||
|
||
const azURL = "http://169.254.169.254/latest/meta-data/placement/availability-zone" | ||
|
||
var ErrNotInAWS = fmt.Errorf("Not in AWS") | ||
|
||
func discoverDNS(domain string, port int) (servers []string, ttl time.Duration, err error) { | ||
r, _ := region() | ||
|
||
// all DNS queries must use the FQDN | ||
domain = "txt." + r + "." + dns.Fqdn(domain) | ||
if _, ok := dns.IsDomainName(domain); !ok { | ||
err = fmt.Errorf("invalid domain name: '%s' is not a domain name", domain) | ||
return | ||
} | ||
regionRecords, ttl, err := findTXT(domain) | ||
if err != nil { | ||
return | ||
} | ||
|
||
for _, az := range regionRecords { | ||
instances, _, er := findTXT("txt." + dns.Fqdn(az)) | ||
if er != nil { | ||
continue | ||
} | ||
for _, instance := range instances { | ||
// format the service URL | ||
servers = append(servers, fmt.Sprintf("http://%s:%d/eureka/v2", instance, port)) | ||
} | ||
} | ||
return | ||
} | ||
|
||
func findTXT(fqdn string) ([]string, time.Duration, error) { | ||
defaultTTL := 120 * time.Second | ||
query := new(dns.Msg) | ||
query.SetQuestion(fqdn, dns.TypeTXT) | ||
response, err := dns.Exchange(query, dnsServerAddr) | ||
if err != nil { | ||
log.Error("Failure resolving name %s err=%s", fqdn, err.Error()) | ||
return nil, defaultTTL, err | ||
} | ||
if len(response.Answer) < 1 { | ||
err := fmt.Errorf("no Eureka discovery TXT record returned for name=%s", fqdn) | ||
log.Error("no answer for name=%s err=%s", fqdn, err.Error()) | ||
return nil, defaultTTL, err | ||
} | ||
if response.Answer[0].Header().Rrtype != dns.TypeTXT { | ||
err := fmt.Errorf("did not receive TXT record back from query specifying TXT record. This should never happen.") | ||
log.Error("Failure resolving name %s err=%s", fqdn, err.Error()) | ||
return nil, defaultTTL, err | ||
} | ||
txt := response.Answer[0].(*dns.TXT) | ||
ttl := response.Answer[0].Header().Ttl | ||
if ttl < 60 { | ||
ttl = 60 | ||
} | ||
|
||
return txt.Txt, time.Duration(ttl) * time.Second, nil | ||
} | ||
|
||
var dnsServerAddr string | ||
|
||
func init() { | ||
// Find a DNS server using the OS resolv.conf | ||
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf") | ||
dnsServerAddr = config.Servers[0] + ":" + config.Port | ||
} | ||
|
||
func region() (string, error) { | ||
zone, err := availabilityZone() | ||
if err != nil { | ||
log.Error("Could not retrieve availability zone err=%s", err.Error()) | ||
return "us-east-1", err | ||
} | ||
return zone[:len(zone)-1], nil | ||
} | ||
|
||
// defaults to us-east-1 if there's a problem | ||
func availabilityZone() (string, error) { | ||
response, err := goreq.Request{Uri: azURL}.Do() | ||
if err != nil { | ||
return "", err | ||
} | ||
if response.StatusCode < 200 || response.StatusCode >= 300 { | ||
body, _ := response.Body.ToString() | ||
return "", fmt.Errorf("bad response code: code %d does not indicate successful request, body=%s", | ||
response.StatusCode, | ||
body, | ||
) | ||
} | ||
zone, err := response.Body.ToString() | ||
if err != nil { | ||
return "", err | ||
} | ||
return zone[:len(zone)-1], nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package fargo | ||
|
||
// MIT Licensed (see README.md) - Copyright (c) 2013 Hudl <@Hudl> | ||
|
||
import ( | ||
. "github.com/smartystreets/goconvey/convey" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestGetNXDomain(t *testing.T) { | ||
Convey("Given nonexistent domain nxd.local.", t, func() { | ||
resp, _, err := findTXT("nxd.local.") | ||
So(err, ShouldNotBeNil) | ||
So(len(resp), ShouldEqual, 0) | ||
}) | ||
} | ||
|
||
func TestGetNetflixTestDomain(t *testing.T) { | ||
Convey("Given domain txt.us-east-1.discoverytest.netflix.net.", t, func() { | ||
// TODO: use a mock DNS server to eliminate dependency on netflix | ||
// keeping their discoverytest domain up | ||
resp, ttl, err := findTXT("txt.us-east-1.discoverytest.netflix.net.") | ||
So(err, ShouldBeNil) | ||
So(ttl, ShouldEqual, 60*time.Second) | ||
So(len(resp), ShouldEqual, 3) | ||
Convey("And the contents are zone records", func() { | ||
expected := map[string]bool{ | ||
"us-east-1c.us-east-1.discoverytest.netflix.net": true, | ||
"us-east-1d.us-east-1.discoverytest.netflix.net": true, | ||
"us-east-1e.us-east-1.discoverytest.netflix.net": true, | ||
} | ||
for _, item := range resp { | ||
_, ok := expected[item] | ||
So(ok, ShouldEqual, true) | ||
} | ||
Convey("And the zone records contain instances", func() { | ||
for _, record := range resp { | ||
servers, _, err := findTXT("txt." + record + ".") | ||
So(err, ShouldBeNil) | ||
So(len(servers) >= 1, ShouldEqual, true) | ||
// servers should be EC2 DNS names | ||
So(servers[0][0:4], ShouldEqual, "ec2-") | ||
} | ||
}) | ||
}) | ||
}) | ||
Convey("Autodiscover discoverytest.netflix.net.", t, func() { | ||
servers, ttl, err := discoverDNS("discoverytest.netflix.net", 7001) | ||
So(ttl, ShouldEqual, 60*time.Second) | ||
So(err, ShouldBeNil) | ||
So(len(servers), ShouldEqual, 4) | ||
Convey("Servers discovered should all be EC2 DNS names", func() { | ||
for _, s := range servers { | ||
So(s[0:11], ShouldEqual, "http://ec2-") | ||
} | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters