From b412c67aa6bc9b1dd0128d253a35f9a4d0073739 Mon Sep 17 00:00:00 2001 From: xenolf Date: Mon, 29 Feb 2016 03:48:41 +0100 Subject: [PATCH 1/5] Move providers out of ACME package. --- acme/dns_challenge.go | 18 ------- cli_handlers.go | 15 ++++-- .../dns/cloudflare/cloudflare.go | 17 ++++--- .../dns/cloudflare/cloudflare_test.go | 2 +- .../dns/digitalocean/digitalocean.go | 8 ++-- .../dns/digitalocean/digitalocean_test.go | 2 +- .../dns/dnsimple/dnsimple.go | 10 ++-- .../dns/dnsimple/dnsimple_test.go | 2 +- .../dns/rfc2136/rfc2136.go | 7 +-- .../dns/rfc2136/rfc2136_test.go | 2 +- .../dns/route53/route53.go | 10 ++-- .../dns/route53/route53_test.go | 2 +- providers/dns/utils.go | 47 +++++++++++++++++++ 13 files changed, 93 insertions(+), 49 deletions(-) rename acme/dns_challenge_cloudflare.go => providers/dns/cloudflare/cloudflare.go (93%) rename acme/dns_challenge_cloudflare_test.go => providers/dns/cloudflare/cloudflare_test.go (98%) rename acme/dns_challenge_digitalocean.go => providers/dns/digitalocean/digitalocean.go (95%) rename acme/dns_challenge_digitalocean_test.go => providers/dns/digitalocean/digitalocean_test.go (99%) rename acme/dns_challenge_dnsimple.go => providers/dns/dnsimple/dnsimple.go (94%) rename acme/dns_challenge_dnsimple_test.go => providers/dns/dnsimple/dnsimple_test.go (99%) rename acme/dns_challenge_rfc2136.go => providers/dns/rfc2136/rfc2136.go (94%) rename acme/dns_challenge_rfc2136_test.go => providers/dns/rfc2136/rfc2136_test.go (99%) rename acme/dns_challenge_route53.go => providers/dns/route53/route53.go (94%) rename acme/dns_challenge_route53_test.go => providers/dns/route53/route53_test.go (99%) create mode 100644 providers/dns/utils.go diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index 533431ca..49c1a264 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -234,24 +234,6 @@ func findZoneByFqdn(fqdn, nameserver string) (string, error) { return "", fmt.Errorf("NS %s did not return the expected SOA record in the authority section", nameserver) } -// toFqdn converts the name into a fqdn appending a trailing dot. -func toFqdn(name string) string { - n := len(name) - if n == 0 || name[n-1] == '.' { - return name - } - return name + "." -} - -// unFqdn converts the fqdn into a name removing the trailing dot. -func unFqdn(name string) string { - n := len(name) - if n != 0 && name[n-1] == '.' { - return name[:n-1] - } - return name -} - // clearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. func clearFqdnCache() { fqdnToZone = map[string]string{} diff --git a/cli_handlers.go b/cli_handlers.go index 9fc2ca0e..d360a407 100644 --- a/cli_handlers.go +++ b/cli_handlers.go @@ -11,6 +11,11 @@ import ( "github.com/codegangsta/cli" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/providers/dns/cloudflare" + "github.com/xenolf/lego/providers/dns/digitalocean" + "github.com/xenolf/lego/providers/dns/dnsimple" + "github.com/xenolf/lego/providers/dns/rfc2136" + "github.com/xenolf/lego/providers/dns/route53" ) func checkFolder(path string) error { @@ -67,23 +72,23 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { var provider acme.ChallengeProvider switch c.GlobalString("dns") { case "cloudflare": - provider, err = acme.NewDNSProviderCloudFlare("", "") + provider, err = cloudflare.NewDNSProviderCloudFlare("", "") case "digitalocean": authToken := os.Getenv("DO_AUTH_TOKEN") - provider, err = acme.NewDNSProviderDigitalOcean(authToken) + provider, err = digitalocean.NewDNSProviderDigitalOcean(authToken) case "dnsimple": - provider, err = acme.NewDNSProviderDNSimple("", "") + provider, err = dnsimple.NewDNSProviderDNSimple("", "") case "route53": awsRegion := os.Getenv("AWS_REGION") - provider, err = acme.NewDNSProviderRoute53("", "", awsRegion) + provider, err = route53.NewDNSProviderRoute53("", "", awsRegion) case "rfc2136": nameserver := os.Getenv("RFC2136_NAMESERVER") tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") - provider, err = acme.NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret) + provider, err = rfc2136.NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret) case "manual": provider, err = acme.NewDNSProviderManual() } diff --git a/acme/dns_challenge_cloudflare.go b/providers/dns/cloudflare/cloudflare.go similarity index 93% rename from acme/dns_challenge_cloudflare.go rename to providers/dns/cloudflare/cloudflare.go index b5bf6d1f..2b9337c4 100644 --- a/acme/dns_challenge_cloudflare.go +++ b/providers/dns/cloudflare/cloudflare.go @@ -1,4 +1,4 @@ -package acme +package cloudflare import ( "bytes" @@ -9,6 +9,9 @@ import ( "os" "strings" "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/providers/dns" ) // CloudFlareAPIURL represents the API endpoint to call. @@ -39,7 +42,7 @@ func NewDNSProviderCloudFlare(cloudflareEmail, cloudflareKey string) (*DNSProvid // Present creates a TXT record to fulfil the dns-01 challenge func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error { - fqdn, value, _ := DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneID, err := c.getHostedZoneID(fqdn) if err != nil { return err @@ -47,7 +50,7 @@ func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error { rec := cloudFlareRecord{ Type: "TXT", - Name: unFqdn(fqdn), + Name: dns.UnFqdn(fqdn), Content: value, TTL: 120, } @@ -67,7 +70,7 @@ func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (c *DNSProviderCloudFlare) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := DNS01Record(domain, keyAuth) + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) record, err := c.findTxtRecord(fqdn) if err != nil { @@ -102,7 +105,7 @@ func (c *DNSProviderCloudFlare) getHostedZoneID(fqdn string) (string, error) { var hostedZone HostedZone for _, zone := range zones { - name := toFqdn(zone.Name) + name := dns.ToFqdn(zone.Name) if strings.HasSuffix(fqdn, name) { if len(zone.Name) > len(hostedZone.Name) { hostedZone = zone @@ -134,7 +137,7 @@ func (c *DNSProviderCloudFlare) findTxtRecord(fqdn string) (*cloudFlareRecord, e } for _, rec := range records { - if rec.Name == unFqdn(fqdn) && rec.Type == "TXT" { + if rec.Name == dns.UnFqdn(fqdn) && rec.Type == "TXT" { return &rec, nil } } @@ -163,7 +166,7 @@ func (c *DNSProviderCloudFlare) makeRequest(method, uri string, body io.Reader) req.Header.Set("X-Auth-Email", c.authEmail) req.Header.Set("X-Auth-Key", c.authKey) - req.Header.Set("User-Agent", userAgent()) + //req.Header.Set("User-Agent", userAgent()) client := http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) diff --git a/acme/dns_challenge_cloudflare_test.go b/providers/dns/cloudflare/cloudflare_test.go similarity index 98% rename from acme/dns_challenge_cloudflare_test.go rename to providers/dns/cloudflare/cloudflare_test.go index e628eba1..27b5c357 100644 --- a/acme/dns_challenge_cloudflare_test.go +++ b/providers/dns/cloudflare/cloudflare_test.go @@ -1,4 +1,4 @@ -package acme +package cloudflare import ( "os" diff --git a/acme/dns_challenge_digitalocean.go b/providers/dns/digitalocean/digitalocean.go similarity index 95% rename from acme/dns_challenge_digitalocean.go rename to providers/dns/digitalocean/digitalocean.go index 176439e6..34750265 100644 --- a/acme/dns_challenge_digitalocean.go +++ b/providers/dns/digitalocean/digitalocean.go @@ -1,4 +1,4 @@ -package acme +package digitalocean import ( "bytes" @@ -6,6 +6,8 @@ import ( "fmt" "net/http" "sync" + + "github.com/xenolf/lego/acme" ) // DNSProviderDigitalOcean is an implementation of the DNSProvider interface @@ -45,7 +47,7 @@ func (d *DNSProviderDigitalOcean) Present(domain, token, keyAuth string) error { } `json:"domain_record"` } - fqdn, value, _ := DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, domain) reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value} @@ -88,7 +90,7 @@ func (d *DNSProviderDigitalOcean) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters func (d *DNSProviderDigitalOcean) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := DNS01Record(domain, keyAuth) + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() diff --git a/acme/dns_challenge_digitalocean_test.go b/providers/dns/digitalocean/digitalocean_test.go similarity index 99% rename from acme/dns_challenge_digitalocean_test.go rename to providers/dns/digitalocean/digitalocean_test.go index cf62090e..52bdedac 100644 --- a/acme/dns_challenge_digitalocean_test.go +++ b/providers/dns/digitalocean/digitalocean_test.go @@ -1,4 +1,4 @@ -package acme +package digitalocean import ( "fmt" diff --git a/acme/dns_challenge_dnsimple.go b/providers/dns/dnsimple/dnsimple.go similarity index 94% rename from acme/dns_challenge_dnsimple.go rename to providers/dns/dnsimple/dnsimple.go index 56b96f97..8b4f95e1 100644 --- a/acme/dns_challenge_dnsimple.go +++ b/providers/dns/dnsimple/dnsimple.go @@ -1,4 +1,4 @@ -package acme +package dnsimple import ( "fmt" @@ -6,6 +6,8 @@ import ( "strings" "github.com/weppos/dnsimple-go/dnsimple" + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/providers/dns" ) // DNSProviderDNSimple is an implementation of the DNSProvider interface. @@ -33,7 +35,7 @@ func NewDNSProviderDNSimple(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProviderD // Present creates a TXT record to fulfil the dns-01 challenge. func (c *DNSProviderDNSimple) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := DNS01Record(domain, keyAuth) + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zoneID, zoneName, err := c.getHostedZone(domain) if err != nil { @@ -51,7 +53,7 @@ func (c *DNSProviderDNSimple) Present(domain, token, keyAuth string) error { // CleanUp removes the TXT record matching the specified parameters. func (c *DNSProviderDNSimple) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := DNS01Record(domain, keyAuth) + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) records, err := c.findTxtRecords(domain, fqdn) if err != nil { @@ -122,7 +124,7 @@ func (c *DNSProviderDNSimple) newTxtRecord(zone, fqdn, value string, ttl int) *d } func (c *DNSProviderDNSimple) extractRecordName(fqdn, domain string) string { - name := unFqdn(fqdn) + name := dns.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/acme/dns_challenge_dnsimple_test.go b/providers/dns/dnsimple/dnsimple_test.go similarity index 99% rename from acme/dns_challenge_dnsimple_test.go rename to providers/dns/dnsimple/dnsimple_test.go index 3a85f1f9..c5091949 100644 --- a/acme/dns_challenge_dnsimple_test.go +++ b/providers/dns/dnsimple/dnsimple_test.go @@ -1,4 +1,4 @@ -package acme +package dnsimple import ( "os" diff --git a/acme/dns_challenge_rfc2136.go b/providers/dns/rfc2136/rfc2136.go similarity index 94% rename from acme/dns_challenge_rfc2136.go rename to providers/dns/rfc2136/rfc2136.go index 7c6900de..b2832d50 100644 --- a/acme/dns_challenge_rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -1,4 +1,4 @@ -package acme +package rfc2136 import ( "fmt" @@ -7,6 +7,7 @@ import ( "time" "github.com/miekg/dns" + "github.com/xenolf/lego/acme" ) // DNSProviderRFC2136 is an implementation of the ChallengeProvider interface that @@ -47,13 +48,13 @@ func NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret string // Present creates a TXT record using the specified parameters func (r *DNSProviderRFC2136) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := DNS01Record(domain, keyAuth) + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) return r.changeRecord("INSERT", fqdn, value, ttl) } // CleanUp removes the TXT record matching the specified parameters func (r *DNSProviderRFC2136) CleanUp(domain, token, keyAuth string) error { - fqdn, value, ttl := DNS01Record(domain, keyAuth) + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) return r.changeRecord("REMOVE", fqdn, value, ttl) } diff --git a/acme/dns_challenge_rfc2136_test.go b/providers/dns/rfc2136/rfc2136_test.go similarity index 99% rename from acme/dns_challenge_rfc2136_test.go rename to providers/dns/rfc2136/rfc2136_test.go index 1a0ed5cc..2b8fb9cd 100644 --- a/acme/dns_challenge_rfc2136_test.go +++ b/providers/dns/rfc2136/rfc2136_test.go @@ -1,4 +1,4 @@ -package acme +package rfc2136 import ( "bytes" diff --git a/acme/dns_challenge_route53.go b/providers/dns/route53/route53.go similarity index 94% rename from acme/dns_challenge_route53.go rename to providers/dns/route53/route53.go index 43e42dab..fc8cd8cc 100644 --- a/acme/dns_challenge_route53.go +++ b/providers/dns/route53/route53.go @@ -1,4 +1,4 @@ -package acme +package route53 import ( "fmt" @@ -7,6 +7,8 @@ import ( "github.com/mitchellh/goamz/aws" "github.com/mitchellh/goamz/route53" + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/providers/dns" ) // DNSProviderRoute53 is an implementation of the DNSProvider interface @@ -43,14 +45,14 @@ func NewDNSProviderRoute53(awsAccessKey, awsSecretKey, awsRegionName string) (*D // Present creates a TXT record using the specified parameters func (r *DNSProviderRoute53) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := DNS01Record(domain, keyAuth) + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` return r.changeRecord("UPSERT", fqdn, value, ttl) } // CleanUp removes the TXT record matching the specified parameters func (r *DNSProviderRoute53) CleanUp(domain, token, keyAuth string) error { - fqdn, value, ttl := DNS01Record(domain, keyAuth) + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` return r.changeRecord("DELETE", fqdn, value, ttl) } @@ -69,7 +71,7 @@ func (r *DNSProviderRoute53) changeRecord(action, fqdn, value string, ttl int) e return err } - return waitFor(90, 5, func() (bool, error) { + return dns.WaitFor(90, 5, func() (bool, error) { status, err := r.client.GetChange(resp.ChangeInfo.ID) if err != nil { return false, err diff --git a/acme/dns_challenge_route53_test.go b/providers/dns/route53/route53_test.go similarity index 99% rename from acme/dns_challenge_route53_test.go rename to providers/dns/route53/route53_test.go index 2c58b263..03f1fa8b 100644 --- a/acme/dns_challenge_route53_test.go +++ b/providers/dns/route53/route53_test.go @@ -1,4 +1,4 @@ -package acme +package route53 import ( "net/http" diff --git a/providers/dns/utils.go b/providers/dns/utils.go new file mode 100644 index 00000000..9df2a8bb --- /dev/null +++ b/providers/dns/utils.go @@ -0,0 +1,47 @@ +package dns + +import ( + "fmt" + "time" +) + +// ToFqdn converts the name into a fqdn appending a trailing dot. +func ToFqdn(name string) string { + n := len(name) + if n == 0 || name[n-1] == '.' { + return name + } + return name + "." +} + +// UnFqdn converts the fqdn into a name removing the trailing dot. +func UnFqdn(name string) string { + n := len(name) + if n != 0 && name[n-1] == '.' { + return name[:n-1] + } + return name +} + +// WaitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds. +func WaitFor(timeout, interval int, f func() (bool, error)) error { + var lastErr string + timeup := time.After(time.Duration(timeout) * time.Second) + for { + select { + case <-timeup: + return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr) + default: + } + + stop, err := f() + if stop { + return nil + } + if err != nil { + lastErr = err.Error() + } + + time.Sleep(time.Duration(interval) * time.Second) + } +} From 9008ec69493c278e1e1da187a95552f101d9cf5b Mon Sep 17 00:00:00 2001 From: xenolf Date: Fri, 11 Mar 2016 03:20:25 +0100 Subject: [PATCH 2/5] Move functions from dns package back into ACME. --- acme/dns_challenge.go | 38 +++++++++++++++------ acme/dns_challenge_test.go | 2 +- providers/dns/cloudflare/cloudflare.go | 7 ++-- providers/dns/dnsimple/dnsimple.go | 3 +- providers/dns/rfc2136/rfc2136.go | 2 +- providers/dns/route53/route53.go | 3 +- providers/dns/utils.go | 47 -------------------------- 7 files changed, 35 insertions(+), 67 deletions(-) delete mode 100644 providers/dns/utils.go diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index 49c1a264..361c76c5 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -69,7 +69,7 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { logf("[INFO][%s] Checking DNS record propagation...", domain) - err = waitFor(30, 2, func() (bool, error) { + err = WaitFor(30, 2, func() (bool, error) { return preCheckDNS(fqdn, value) }) if err != nil { @@ -160,7 +160,7 @@ func dnsQuery(fqdn string, rtype uint16, nameserver string, recursive bool) (in func lookupNameservers(fqdn string) ([]string, error) { var authoritativeNss []string - zone, err := findZoneByFqdn(fqdn, recursiveNameserver) + zone, err := FindZoneByFqdn(fqdn, recursiveNameserver) if err != nil { return nil, err } @@ -182,8 +182,8 @@ func lookupNameservers(fqdn string) ([]string, error) { return nil, fmt.Errorf("Could not determine authoritative nameservers") } -// findZoneByFqdn determines the zone of the given fqdn -func findZoneByFqdn(fqdn, nameserver string) (string, error) { +// FindZoneByFqdn determines the zone of the given fqdn +func FindZoneByFqdn(fqdn, nameserver string) (string, error) { // Do we have it cached? if zone, ok := fqdnToZone[fqdn]; ok { return zone, nil @@ -208,8 +208,8 @@ func findZoneByFqdn(fqdn, nameserver string) (string, error) { if soa, ok := ans.(*dns.SOA); ok { zone := soa.Hdr.Name // If we ended up on one of the TLDs, it means the domain did not exist. - publicsuffix, _ := publicsuffix.PublicSuffix(unFqdn(zone)) - if publicsuffix == unFqdn(zone) { + publicsuffix, _ := publicsuffix.PublicSuffix(UnFqdn(zone)) + if publicsuffix == UnFqdn(zone) { return "", fmt.Errorf("Could not determine zone authoritatively") } fqdnToZone[fqdn] = zone @@ -223,8 +223,8 @@ func findZoneByFqdn(fqdn, nameserver string) (string, error) { if soa, ok := ns.(*dns.SOA); ok { zone := soa.Hdr.Name // If we ended up on one of the TLDs, it means the domain did not exist. - publicsuffix, _ := publicsuffix.PublicSuffix(unFqdn(zone)) - if publicsuffix == unFqdn(zone) { + publicsuffix, _ := publicsuffix.PublicSuffix(UnFqdn(zone)) + if publicsuffix == UnFqdn(zone) { return "", fmt.Errorf("Could not determine zone authoritatively") } fqdnToZone[fqdn] = zone @@ -239,8 +239,26 @@ func clearFqdnCache() { fqdnToZone = map[string]string{} } -// waitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds. -func waitFor(timeout, interval int, f func() (bool, error)) error { +// ToFqdn converts the name into a fqdn appending a trailing dot. +func ToFqdn(name string) string { + n := len(name) + if n == 0 || name[n-1] == '.' { + return name + } + return name + "." +} + +// UnFqdn converts the fqdn into a name removing the trailing dot. +func UnFqdn(name string) string { + n := len(name) + if n != 0 && name[n-1] == '.' { + return name[:n-1] + } + return name +} + +// WaitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds. +func WaitFor(timeout, interval int, f func() (bool, error)) error { var lastErr string timeup := time.After(time.Duration(timeout) * time.Second) for { diff --git a/acme/dns_challenge_test.go b/acme/dns_challenge_test.go index 54d5e095..760c7991 100644 --- a/acme/dns_challenge_test.go +++ b/acme/dns_challenge_test.go @@ -167,7 +167,7 @@ func TestCheckAuthoritativeNssErr(t *testing.T) { func TestWaitForTimeout(t *testing.T) { c := make(chan error) go func() { - err := waitFor(3, 1, func() (bool, error) { + err := WaitFor(3, 1, func() (bool, error) { return false, nil }) c <- err diff --git a/providers/dns/cloudflare/cloudflare.go b/providers/dns/cloudflare/cloudflare.go index 2b9337c4..d531dfc6 100644 --- a/providers/dns/cloudflare/cloudflare.go +++ b/providers/dns/cloudflare/cloudflare.go @@ -11,7 +11,6 @@ import ( "time" "github.com/xenolf/lego/acme" - "github.com/xenolf/lego/providers/dns" ) // CloudFlareAPIURL represents the API endpoint to call. @@ -50,7 +49,7 @@ func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error { rec := cloudFlareRecord{ Type: "TXT", - Name: dns.UnFqdn(fqdn), + Name: acme.UnFqdn(fqdn), Content: value, TTL: 120, } @@ -105,7 +104,7 @@ func (c *DNSProviderCloudFlare) getHostedZoneID(fqdn string) (string, error) { var hostedZone HostedZone for _, zone := range zones { - name := dns.ToFqdn(zone.Name) + name := acme.ToFqdn(zone.Name) if strings.HasSuffix(fqdn, name) { if len(zone.Name) > len(hostedZone.Name) { hostedZone = zone @@ -137,7 +136,7 @@ func (c *DNSProviderCloudFlare) findTxtRecord(fqdn string) (*cloudFlareRecord, e } for _, rec := range records { - if rec.Name == dns.UnFqdn(fqdn) && rec.Type == "TXT" { + if rec.Name == acme.UnFqdn(fqdn) && rec.Type == "TXT" { return &rec, nil } } diff --git a/providers/dns/dnsimple/dnsimple.go b/providers/dns/dnsimple/dnsimple.go index 8b4f95e1..0c9f03e2 100644 --- a/providers/dns/dnsimple/dnsimple.go +++ b/providers/dns/dnsimple/dnsimple.go @@ -7,7 +7,6 @@ import ( "github.com/weppos/dnsimple-go/dnsimple" "github.com/xenolf/lego/acme" - "github.com/xenolf/lego/providers/dns" ) // DNSProviderDNSimple is an implementation of the DNSProvider interface. @@ -124,7 +123,7 @@ func (c *DNSProviderDNSimple) newTxtRecord(zone, fqdn, value string, ttl int) *d } func (c *DNSProviderDNSimple) extractRecordName(fqdn, domain string) string { - name := dns.UnFqdn(fqdn) + name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] } diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go index b2832d50..df303001 100644 --- a/providers/dns/rfc2136/rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -60,7 +60,7 @@ func (r *DNSProviderRFC2136) CleanUp(domain, token, keyAuth string) error { func (r *DNSProviderRFC2136) changeRecord(action, fqdn, value string, ttl int) error { // Find the zone for the given fqdn - zone, err := findZoneByFqdn(fqdn, r.nameserver) + zone, err := acme.FindZoneByFqdn(fqdn, r.nameserver) if err != nil { return err } diff --git a/providers/dns/route53/route53.go b/providers/dns/route53/route53.go index fc8cd8cc..ce3bb975 100644 --- a/providers/dns/route53/route53.go +++ b/providers/dns/route53/route53.go @@ -8,7 +8,6 @@ import ( "github.com/mitchellh/goamz/aws" "github.com/mitchellh/goamz/route53" "github.com/xenolf/lego/acme" - "github.com/xenolf/lego/providers/dns" ) // DNSProviderRoute53 is an implementation of the DNSProvider interface @@ -71,7 +70,7 @@ func (r *DNSProviderRoute53) changeRecord(action, fqdn, value string, ttl int) e return err } - return dns.WaitFor(90, 5, func() (bool, error) { + return acme.WaitFor(90, 5, func() (bool, error) { status, err := r.client.GetChange(resp.ChangeInfo.ID) if err != nil { return false, err diff --git a/providers/dns/utils.go b/providers/dns/utils.go deleted file mode 100644 index 9df2a8bb..00000000 --- a/providers/dns/utils.go +++ /dev/null @@ -1,47 +0,0 @@ -package dns - -import ( - "fmt" - "time" -) - -// ToFqdn converts the name into a fqdn appending a trailing dot. -func ToFqdn(name string) string { - n := len(name) - if n == 0 || name[n-1] == '.' { - return name - } - return name + "." -} - -// UnFqdn converts the fqdn into a name removing the trailing dot. -func UnFqdn(name string) string { - n := len(name) - if n != 0 && name[n-1] == '.' { - return name[:n-1] - } - return name -} - -// WaitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds. -func WaitFor(timeout, interval int, f func() (bool, error)) error { - var lastErr string - timeup := time.After(time.Duration(timeout) * time.Second) - for { - select { - case <-timeup: - return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr) - default: - } - - stop, err := f() - if stop { - return nil - } - if err != nil { - lastErr = err.Error() - } - - time.Sleep(time.Duration(interval) * time.Second) - } -} From 2ae35a755d1e2dcede30f5fd965b7f4db5c6dfa4 Mon Sep 17 00:00:00 2001 From: xenolf Date: Fri, 11 Mar 2016 03:46:09 +0100 Subject: [PATCH 3/5] Rename provider types as provider names are already in the package name. Added package level comments and fixed the name of the interface the providers are importing. --- acme/dns_challenge.go | 4 +-- cli_handlers.go | 10 +++---- providers/dns/cloudflare/cloudflare.go | 21 ++++++++------- providers/dns/cloudflare/cloudflare_test.go | 16 +++++------ providers/dns/digitalocean/digitalocean.go | 15 ++++++----- .../dns/digitalocean/digitalocean_test.go | 4 +-- providers/dns/dnsimple/dnsimple.go | 23 ++++++++-------- providers/dns/dnsimple/dnsimple_test.go | 16 +++++------ providers/dns/rfc2136/rfc2136.go | 17 ++++++------ providers/dns/rfc2136/rfc2136_test.go | 27 ++++++++++--------- providers/dns/route53/route53.go | 19 ++++++------- providers/dns/route53/route53_test.go | 20 +++++++------- 12 files changed, 99 insertions(+), 93 deletions(-) diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index 361c76c5..659d7082 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -234,8 +234,8 @@ func FindZoneByFqdn(fqdn, nameserver string) (string, error) { return "", fmt.Errorf("NS %s did not return the expected SOA record in the authority section", nameserver) } -// clearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. -func clearFqdnCache() { +// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. +func ClearFqdnCache() { fqdnToZone = map[string]string{} } diff --git a/cli_handlers.go b/cli_handlers.go index d360a407..28c96d8b 100644 --- a/cli_handlers.go +++ b/cli_handlers.go @@ -72,23 +72,23 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { var provider acme.ChallengeProvider switch c.GlobalString("dns") { case "cloudflare": - provider, err = cloudflare.NewDNSProviderCloudFlare("", "") + provider, err = cloudflare.NewDNSProvider("", "") case "digitalocean": authToken := os.Getenv("DO_AUTH_TOKEN") - provider, err = digitalocean.NewDNSProviderDigitalOcean(authToken) + provider, err = digitalocean.NewDNSProvider(authToken) case "dnsimple": - provider, err = dnsimple.NewDNSProviderDNSimple("", "") + provider, err = dnsimple.NewDNSProvider("", "") case "route53": awsRegion := os.Getenv("AWS_REGION") - provider, err = route53.NewDNSProviderRoute53("", "", awsRegion) + provider, err = route53.NewDNSProvider("", "", awsRegion) case "rfc2136": nameserver := os.Getenv("RFC2136_NAMESERVER") tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") - provider, err = rfc2136.NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret) + provider, err = rfc2136.NewDNSProvider(nameserver, tsigAlgorithm, tsigKey, tsigSecret) case "manual": provider, err = acme.NewDNSProviderManual() } diff --git a/providers/dns/cloudflare/cloudflare.go b/providers/dns/cloudflare/cloudflare.go index d531dfc6..307cc4ef 100644 --- a/providers/dns/cloudflare/cloudflare.go +++ b/providers/dns/cloudflare/cloudflare.go @@ -1,3 +1,4 @@ +// Package cloudflare implements a DNS provider for solving the DNS-01 challenge using cloudflare DNS. package cloudflare import ( @@ -17,15 +18,15 @@ import ( // TODO: Unexport? const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4" -// DNSProviderCloudFlare is an implementation of the DNSProvider interface -type DNSProviderCloudFlare struct { +// DNSProvider is an implementation of the acme.ChallengeProvider interface +type DNSProvider struct { authEmail string authKey string } -// NewDNSProviderCloudFlare returns a DNSProviderCloudFlare instance with a configured cloudflare client. +// NewDNSProvider returns a DNSProvider instance with a configured cloudflare client. // Credentials can either be passed as arguments or through CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY env vars. -func NewDNSProviderCloudFlare(cloudflareEmail, cloudflareKey string) (*DNSProviderCloudFlare, error) { +func NewDNSProvider(cloudflareEmail, cloudflareKey string) (*DNSProvider, error) { if cloudflareEmail == "" || cloudflareKey == "" { cloudflareEmail, cloudflareKey = cloudflareEnvAuth() if cloudflareEmail == "" || cloudflareKey == "" { @@ -33,14 +34,14 @@ func NewDNSProviderCloudFlare(cloudflareEmail, cloudflareKey string) (*DNSProvid } } - return &DNSProviderCloudFlare{ + return &DNSProvider{ authEmail: cloudflareEmail, authKey: cloudflareKey, }, nil } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error { +func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneID, err := c.getHostedZoneID(fqdn) if err != nil { @@ -68,7 +69,7 @@ func (c *DNSProviderCloudFlare) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProviderCloudFlare) CleanUp(domain, token, keyAuth string) error { +func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) record, err := c.findTxtRecord(fqdn) @@ -84,7 +85,7 @@ func (c *DNSProviderCloudFlare) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProviderCloudFlare) getHostedZoneID(fqdn string) (string, error) { +func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { // HostedZone represents a CloudFlare DNS zone type HostedZone struct { ID string `json:"id"` @@ -118,7 +119,7 @@ func (c *DNSProviderCloudFlare) getHostedZoneID(fqdn string) (string, error) { return hostedZone.ID, nil } -func (c *DNSProviderCloudFlare) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { +func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { zoneID, err := c.getHostedZoneID(fqdn) if err != nil { return nil, err @@ -144,7 +145,7 @@ func (c *DNSProviderCloudFlare) findTxtRecord(fqdn string) (*cloudFlareRecord, e return nil, fmt.Errorf("No existing record found for %s", fqdn) } -func (c *DNSProviderCloudFlare) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { +func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { // APIError contains error details for failed requests type APIError struct { Code int `json:"code,omitempty"` diff --git a/providers/dns/cloudflare/cloudflare_test.go b/providers/dns/cloudflare/cloudflare_test.go index 27b5c357..63936ce6 100644 --- a/providers/dns/cloudflare/cloudflare_test.go +++ b/providers/dns/cloudflare/cloudflare_test.go @@ -29,26 +29,26 @@ func restoreCloudFlareEnv() { os.Setenv("CLOUDFLARE_API_KEY", cflareAPIKey) } -func TestNewDNSProviderCloudFlareValid(t *testing.T) { +func TestNewDNSProviderValid(t *testing.T) { os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_API_KEY", "") - _, err := NewDNSProviderCloudFlare("123", "123") + _, err := NewDNSProvider("123", "123") assert.NoError(t, err) restoreCloudFlareEnv() } -func TestNewDNSProviderCloudFlareValidEnv(t *testing.T) { +func TestNewDNSProviderValidEnv(t *testing.T) { os.Setenv("CLOUDFLARE_EMAIL", "test@example.com") os.Setenv("CLOUDFLARE_API_KEY", "123") - _, err := NewDNSProviderCloudFlare("", "") + _, err := NewDNSProvider("", "") assert.NoError(t, err) restoreCloudFlareEnv() } -func TestNewDNSProviderCloudFlareMissingCredErr(t *testing.T) { +func TestNewDNSProviderMissingCredErr(t *testing.T) { os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_API_KEY", "") - _, err := NewDNSProviderCloudFlare("", "") + _, err := NewDNSProvider("", "") assert.EqualError(t, err, "CloudFlare credentials missing") restoreCloudFlareEnv() } @@ -58,7 +58,7 @@ func TestCloudFlarePresent(t *testing.T) { t.Skip("skipping live test") } - provider, err := NewDNSProviderCloudFlare(cflareEmail, cflareAPIKey) + provider, err := NewDNSProvider(cflareEmail, cflareAPIKey) assert.NoError(t, err) err = provider.Present(cflareDomain, "", "123d==") @@ -72,7 +72,7 @@ func TestCloudFlareCleanUp(t *testing.T) { time.Sleep(time.Second * 2) - provider, err := NewDNSProviderCloudFlare(cflareEmail, cflareAPIKey) + provider, err := NewDNSProvider(cflareEmail, cflareAPIKey) assert.NoError(t, err) err = provider.CleanUp(cflareDomain, "", "123d==") diff --git a/providers/dns/digitalocean/digitalocean.go b/providers/dns/digitalocean/digitalocean.go index 34750265..fb3e00de 100644 --- a/providers/dns/digitalocean/digitalocean.go +++ b/providers/dns/digitalocean/digitalocean.go @@ -1,3 +1,4 @@ +// Package digitalocean implements a DNS provider for solving the DNS-01 challenge using digitalocean DNS. package digitalocean import ( @@ -10,26 +11,26 @@ import ( "github.com/xenolf/lego/acme" ) -// DNSProviderDigitalOcean is an implementation of the DNSProvider interface +// DNSProvider is an implementation of the acme.ChallengeProvider interface // that uses DigitalOcean's REST API to manage TXT records for a domain. -type DNSProviderDigitalOcean struct { +type DNSProvider struct { apiAuthToken string recordIDs map[string]int recordIDsMu sync.Mutex } -// NewDNSProviderDigitalOcean returns a new DNSProviderDigitalOcean instance. +// NewDNSProvider returns a new DNSProvider instance. // apiAuthToken is the personal access token created in the DigitalOcean account // control panel, and it will be sent in bearer authorization headers. -func NewDNSProviderDigitalOcean(apiAuthToken string) (*DNSProviderDigitalOcean, error) { - return &DNSProviderDigitalOcean{ +func NewDNSProvider(apiAuthToken string) (*DNSProvider, error) { + return &DNSProvider{ apiAuthToken: apiAuthToken, recordIDs: make(map[string]int), }, nil } // Present creates a TXT record using the specified parameters -func (d *DNSProviderDigitalOcean) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { // txtRecordRequest represents the request body to DO's API to make a TXT record type txtRecordRequest struct { RecordType string `json:"type"` @@ -89,7 +90,7 @@ func (d *DNSProviderDigitalOcean) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters -func (d *DNSProviderDigitalOcean) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // get the record's unique ID from when we created it diff --git a/providers/dns/digitalocean/digitalocean_test.go b/providers/dns/digitalocean/digitalocean_test.go index 52bdedac..8fc383a0 100644 --- a/providers/dns/digitalocean/digitalocean_test.go +++ b/providers/dns/digitalocean/digitalocean_test.go @@ -53,7 +53,7 @@ func TestDigitalOceanPresent(t *testing.T) { defer mock.Close() digitalOceanBaseURL = mock.URL - doprov, err := NewDNSProviderDigitalOcean(fakeDigitalOceanAuth) + doprov, err := NewDNSProvider(fakeDigitalOceanAuth) if doprov == nil { t.Fatal("Expected non-nil DigitalOcean provider, but was nil") } @@ -95,7 +95,7 @@ func TestDigitalOceanCleanUp(t *testing.T) { defer mock.Close() digitalOceanBaseURL = mock.URL - doprov, err := NewDNSProviderDigitalOcean(fakeDigitalOceanAuth) + doprov, err := NewDNSProvider(fakeDigitalOceanAuth) if doprov == nil { t.Fatal("Expected non-nil DigitalOcean provider, but was nil") } diff --git a/providers/dns/dnsimple/dnsimple.go b/providers/dns/dnsimple/dnsimple.go index 0c9f03e2..cde77298 100644 --- a/providers/dns/dnsimple/dnsimple.go +++ b/providers/dns/dnsimple/dnsimple.go @@ -1,3 +1,4 @@ +// Package dnsimple implements a DNS provider for solving the DNS-01 challenge using dnsimple DNS. package dnsimple import ( @@ -9,15 +10,15 @@ import ( "github.com/xenolf/lego/acme" ) -// DNSProviderDNSimple is an implementation of the DNSProvider interface. -type DNSProviderDNSimple struct { +// DNSProvider is an implementation of the acme.ChallengeProvider interface. +type DNSProvider struct { client *dnsimple.Client } -// NewDNSProviderDNSimple returns a DNSProviderDNSimple instance with a configured dnsimple client. +// NewDNSProvider returns a DNSProvider instance with a configured dnsimple client. // Authentication is either done using the passed credentials or - when empty - using the environment // variables DNSIMPLE_EMAIL and DNSIMPLE_API_KEY. -func NewDNSProviderDNSimple(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProviderDNSimple, error) { +func NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProvider, error) { if dnsimpleEmail == "" || dnsimpleAPIKey == "" { dnsimpleEmail, dnsimpleAPIKey = dnsimpleEnvAuth() if dnsimpleEmail == "" || dnsimpleAPIKey == "" { @@ -25,7 +26,7 @@ func NewDNSProviderDNSimple(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProviderD } } - c := &DNSProviderDNSimple{ + c := &DNSProvider{ client: dnsimple.NewClient(dnsimpleAPIKey, dnsimpleEmail), } @@ -33,7 +34,7 @@ func NewDNSProviderDNSimple(dnsimpleEmail, dnsimpleAPIKey string) (*DNSProviderD } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProviderDNSimple) Present(domain, token, keyAuth string) error { +func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zoneID, zoneName, err := c.getHostedZone(domain) @@ -51,7 +52,7 @@ func (c *DNSProviderDNSimple) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProviderDNSimple) CleanUp(domain, token, keyAuth string) error { +func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) records, err := c.findTxtRecords(domain, fqdn) @@ -68,7 +69,7 @@ func (c *DNSProviderDNSimple) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProviderDNSimple) getHostedZone(domain string) (string, string, error) { +func (c *DNSProvider) getHostedZone(domain string) (string, string, error) { domains, _, err := c.client.Domains.List() if err != nil { return "", "", fmt.Errorf("DNSimple API call failed: %v", err) @@ -89,7 +90,7 @@ func (c *DNSProviderDNSimple) getHostedZone(domain string) (string, string, erro return fmt.Sprintf("%v", hostedDomain.Id), hostedDomain.Name, nil } -func (c *DNSProviderDNSimple) findTxtRecords(domain, fqdn string) ([]dnsimple.Record, error) { +func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.Record, error) { zoneID, zoneName, err := c.getHostedZone(domain) if err != nil { return nil, err @@ -111,7 +112,7 @@ func (c *DNSProviderDNSimple) findTxtRecords(domain, fqdn string) ([]dnsimple.Re return records, nil } -func (c *DNSProviderDNSimple) newTxtRecord(zone, fqdn, value string, ttl int) *dnsimple.Record { +func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnsimple.Record { name := c.extractRecordName(fqdn, zone) return &dnsimple.Record{ @@ -122,7 +123,7 @@ func (c *DNSProviderDNSimple) newTxtRecord(zone, fqdn, value string, ttl int) *d } } -func (c *DNSProviderDNSimple) extractRecordName(fqdn, domain string) string { +func (c *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] diff --git a/providers/dns/dnsimple/dnsimple_test.go b/providers/dns/dnsimple/dnsimple_test.go index c5091949..7cb19fea 100644 --- a/providers/dns/dnsimple/dnsimple_test.go +++ b/providers/dns/dnsimple/dnsimple_test.go @@ -29,25 +29,25 @@ func restoreDNSimpleEnv() { os.Setenv("DNSIMPLE_API_KEY", dnsimpleAPIKey) } -func TestNewDNSProviderDNSimpleValid(t *testing.T) { +func TestNewDNSProviderValid(t *testing.T) { os.Setenv("DNSIMPLE_EMAIL", "") os.Setenv("DNSIMPLE_API_KEY", "") - _, err := NewDNSProviderDNSimple("example@example.com", "123") + _, err := NewDNSProvider("example@example.com", "123") assert.NoError(t, err) restoreDNSimpleEnv() } -func TestNewDNSProviderDNSimpleValidEnv(t *testing.T) { +func TestNewDNSProviderValidEnv(t *testing.T) { os.Setenv("DNSIMPLE_EMAIL", "example@example.com") os.Setenv("DNSIMPLE_API_KEY", "123") - _, err := NewDNSProviderDNSimple("", "") + _, err := NewDNSProvider("", "") assert.NoError(t, err) restoreDNSimpleEnv() } -func TestNewDNSProviderDNSimpleMissingCredErr(t *testing.T) { +func TestNewDNSProviderMissingCredErr(t *testing.T) { os.Setenv("DNSIMPLE_EMAIL", "") os.Setenv("DNSIMPLE_API_KEY", "") - _, err := NewDNSProviderDNSimple("", "") + _, err := NewDNSProvider("", "") assert.EqualError(t, err, "DNSimple credentials missing") restoreDNSimpleEnv() } @@ -57,7 +57,7 @@ func TestLiveDNSimplePresent(t *testing.T) { t.Skip("skipping live test") } - provider, err := NewDNSProviderDNSimple(dnsimpleEmail, dnsimpleAPIKey) + provider, err := NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey) assert.NoError(t, err) err = provider.Present(dnsimpleDomain, "", "123d==") @@ -71,7 +71,7 @@ func TestLiveDNSimpleCleanUp(t *testing.T) { time.Sleep(time.Second * 1) - provider, err := NewDNSProviderDNSimple(dnsimpleEmail, dnsimpleAPIKey) + provider, err := NewDNSProvider(dnsimpleEmail, dnsimpleAPIKey) assert.NoError(t, err) err = provider.CleanUp(dnsimpleDomain, "", "123d==") diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go index df303001..3d32409b 100644 --- a/providers/dns/rfc2136/rfc2136.go +++ b/providers/dns/rfc2136/rfc2136.go @@ -1,3 +1,4 @@ +// Package rfc2136 implements a DNS provider for solving the DNS-01 challenge using the rfc2136 dynamic update. package rfc2136 import ( @@ -10,19 +11,19 @@ import ( "github.com/xenolf/lego/acme" ) -// DNSProviderRFC2136 is an implementation of the ChallengeProvider interface that +// DNSProvider is an implementation of the acme.ChallengeProvider interface that // uses dynamic DNS updates (RFC 2136) to create TXT records on a nameserver. -type DNSProviderRFC2136 struct { +type DNSProvider struct { nameserver string tsigAlgorithm string tsigKey string tsigSecret string } -// NewDNSProviderRFC2136 returns a new DNSProviderRFC2136 instance. +// NewDNSProvider returns a new DNSProvider instance. // To disable TSIG authentication 'tsigAlgorithm, 'tsigKey' and 'tsigSecret' must be set to the empty string. // 'nameserver' must be a network address in the the form "host" or "host:port". -func NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret string) (*DNSProviderRFC2136, error) { +func NewDNSProvider(nameserver, tsigAlgorithm, tsigKey, tsigSecret string) (*DNSProvider, error) { // Append the default DNS port if none is specified. if _, _, err := net.SplitHostPort(nameserver); err != nil { if strings.Contains(err.Error(), "missing port") { @@ -31,7 +32,7 @@ func NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret string return nil, err } } - d := &DNSProviderRFC2136{ + d := &DNSProvider{ nameserver: nameserver, } if tsigAlgorithm == "" { @@ -47,18 +48,18 @@ func NewDNSProviderRFC2136(nameserver, tsigAlgorithm, tsigKey, tsigSecret string } // Present creates a TXT record using the specified parameters -func (r *DNSProviderRFC2136) Present(domain, token, keyAuth string) error { +func (r *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) return r.changeRecord("INSERT", fqdn, value, ttl) } // CleanUp removes the TXT record matching the specified parameters -func (r *DNSProviderRFC2136) CleanUp(domain, token, keyAuth string) error { +func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) return r.changeRecord("REMOVE", fqdn, value, ttl) } -func (r *DNSProviderRFC2136) changeRecord(action, fqdn, value string, ttl int) error { +func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { // Find the zone for the given fqdn zone, err := acme.FindZoneByFqdn(fqdn, r.nameserver) if err != nil { diff --git a/providers/dns/rfc2136/rfc2136_test.go b/providers/dns/rfc2136/rfc2136_test.go index 2b8fb9cd..2aa8aa22 100644 --- a/providers/dns/rfc2136/rfc2136_test.go +++ b/providers/dns/rfc2136/rfc2136_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/miekg/dns" + "github.com/xenolf/lego/acme" ) var ( @@ -26,7 +27,7 @@ var ( var reqChan = make(chan *dns.Msg, 10) func TestRFC2136CanaryLocalTestServer(t *testing.T) { - clearFqdnCache() + acme.ClearFqdnCache() dns.HandleFunc("example.com.", serverHandlerHello) defer dns.HandleRemove("example.com.") @@ -50,7 +51,7 @@ func TestRFC2136CanaryLocalTestServer(t *testing.T) { } func TestRFC2136ServerSuccess(t *testing.T) { - clearFqdnCache() + acme.ClearFqdnCache() dns.HandleFunc(rfc2136TestZone, serverHandlerReturnSuccess) defer dns.HandleRemove(rfc2136TestZone) @@ -60,9 +61,9 @@ func TestRFC2136ServerSuccess(t *testing.T) { } defer server.Shutdown() - provider, err := NewDNSProviderRFC2136(addrstr, "", "", "") + provider, err := NewDNSProvider(addrstr, "", "", "") if err != nil { - t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) @@ -70,7 +71,7 @@ func TestRFC2136ServerSuccess(t *testing.T) { } func TestRFC2136ServerError(t *testing.T) { - clearFqdnCache() + acme.ClearFqdnCache() dns.HandleFunc(rfc2136TestZone, serverHandlerReturnErr) defer dns.HandleRemove(rfc2136TestZone) @@ -80,9 +81,9 @@ func TestRFC2136ServerError(t *testing.T) { } defer server.Shutdown() - provider, err := NewDNSProviderRFC2136(addrstr, "", "", "") + provider, err := NewDNSProvider(addrstr, "", "", "") if err != nil { - t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err == nil { t.Errorf("Expected Present() to return an error but it did not.") @@ -92,7 +93,7 @@ func TestRFC2136ServerError(t *testing.T) { } func TestRFC2136TsigClient(t *testing.T) { - clearFqdnCache() + acme.ClearFqdnCache() dns.HandleFunc(rfc2136TestZone, serverHandlerReturnSuccess) defer dns.HandleRemove(rfc2136TestZone) @@ -102,9 +103,9 @@ func TestRFC2136TsigClient(t *testing.T) { } defer server.Shutdown() - provider, err := NewDNSProviderRFC2136(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret) + provider, err := NewDNSProvider(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret) if err != nil { - t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) @@ -112,7 +113,7 @@ func TestRFC2136TsigClient(t *testing.T) { } func TestRFC2136ValidUpdatePacket(t *testing.T) { - clearFqdnCache() + acme.ClearFqdnCache() dns.HandleFunc(rfc2136TestZone, serverHandlerPassBackRequest) defer dns.HandleRemove(rfc2136TestZone) @@ -134,9 +135,9 @@ func TestRFC2136ValidUpdatePacket(t *testing.T) { t.Fatalf("Error packing expect msg: %v", err) } - provider, err := NewDNSProviderRFC2136(addrstr, "", "", "") + provider, err := NewDNSProvider(addrstr, "", "", "") if err != nil { - t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) + t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", "1234d=="); err != nil { diff --git a/providers/dns/route53/route53.go b/providers/dns/route53/route53.go index ce3bb975..bfca45ac 100644 --- a/providers/dns/route53/route53.go +++ b/providers/dns/route53/route53.go @@ -1,3 +1,4 @@ +// Package route53 implements a DNS provider for solving the DNS-01 challenge using route53 DNS. package route53 import ( @@ -10,18 +11,18 @@ import ( "github.com/xenolf/lego/acme" ) -// DNSProviderRoute53 is an implementation of the DNSProvider interface -type DNSProviderRoute53 struct { +// DNSProvider is an implementation of the acme.ChallengeProvider interface +type DNSProvider struct { client *route53.Route53 } -// NewDNSProviderRoute53 returns a DNSProviderRoute53 instance with a configured route53 client. +// NewDNSProvider returns a DNSProvider instance with a configured route53 client. // Authentication is either done using the passed credentials or - when empty - falling back to // the customary AWS credential mechanisms, including the file referenced by $AWS_CREDENTIAL_FILE // (defaulting to $HOME/.aws/credentials) optionally scoped to $AWS_PROFILE, credentials // supplied by the environment variables AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY [ + AWS_SECURITY_TOKEN ], // and finally credentials available via the EC2 instance metadata service. -func NewDNSProviderRoute53(awsAccessKey, awsSecretKey, awsRegionName string) (*DNSProviderRoute53, error) { +func NewDNSProvider(awsAccessKey, awsSecretKey, awsRegionName string) (*DNSProvider, error) { region, ok := aws.Regions[awsRegionName] if !ok { return nil, fmt.Errorf("Invalid AWS region name %s", awsRegionName) @@ -39,24 +40,24 @@ func NewDNSProviderRoute53(awsAccessKey, awsSecretKey, awsRegionName string) (*D } client := route53.New(auth, region) - return &DNSProviderRoute53{client: client}, nil + return &DNSProvider{client: client}, nil } // Present creates a TXT record using the specified parameters -func (r *DNSProviderRoute53) Present(domain, token, keyAuth string) error { +func (r *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` return r.changeRecord("UPSERT", fqdn, value, ttl) } // CleanUp removes the TXT record matching the specified parameters -func (r *DNSProviderRoute53) CleanUp(domain, token, keyAuth string) error { +func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` return r.changeRecord("DELETE", fqdn, value, ttl) } -func (r *DNSProviderRoute53) changeRecord(action, fqdn, value string, ttl int) error { +func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { hostedZoneID, err := r.getHostedZoneID(fqdn) if err != nil { return err @@ -82,7 +83,7 @@ func (r *DNSProviderRoute53) changeRecord(action, fqdn, value string, ttl int) e }) } -func (r *DNSProviderRoute53) getHostedZoneID(fqdn string) (string, error) { +func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) { zones := []route53.HostedZone{} zoneResp, err := r.client.ListHostedZones("", 0) if err != nil { diff --git a/providers/dns/route53/route53_test.go b/providers/dns/route53/route53_test.go index 03f1fa8b..8bb675c3 100644 --- a/providers/dns/route53/route53_test.go +++ b/providers/dns/route53/route53_test.go @@ -91,29 +91,29 @@ func makeRoute53TestServer() *testutil.HTTPServer { return testServer } -func makeRoute53Provider(server *testutil.HTTPServer) *DNSProviderRoute53 { +func makeRoute53Provider(server *testutil.HTTPServer) *DNSProvider { auth := aws.Auth{AccessKey: "abc", SecretKey: "123", Token: ""} client := route53.NewWithClient(auth, aws.Region{Route53Endpoint: server.URL}, testutil.DefaultClient) - return &DNSProviderRoute53{client: client} + return &DNSProvider{client: client} } -func TestNewDNSProviderRoute53Valid(t *testing.T) { +func TestNewDNSProviderValid(t *testing.T) { os.Setenv("AWS_ACCESS_KEY_ID", "") os.Setenv("AWS_SECRET_ACCESS_KEY", "") - _, err := NewDNSProviderRoute53("123", "123", "us-east-1") + _, err := NewDNSProvider("123", "123", "us-east-1") assert.NoError(t, err) restoreRoute53Env() } -func TestNewDNSProviderRoute53ValidEnv(t *testing.T) { +func TestNewDNSProviderValidEnv(t *testing.T) { os.Setenv("AWS_ACCESS_KEY_ID", "123") os.Setenv("AWS_SECRET_ACCESS_KEY", "123") - _, err := NewDNSProviderRoute53("", "", "us-east-1") + _, err := NewDNSProvider("", "", "us-east-1") assert.NoError(t, err) restoreRoute53Env() } -func TestNewDNSProviderRoute53MissingAuthErr(t *testing.T) { +func TestNewDNSProviderMissingAuthErr(t *testing.T) { os.Setenv("AWS_ACCESS_KEY_ID", "") os.Setenv("AWS_SECRET_ACCESS_KEY", "") os.Setenv("AWS_CREDENTIAL_FILE", "") // in case test machine has this variable set @@ -124,7 +124,7 @@ func TestNewDNSProviderRoute53MissingAuthErr(t *testing.T) { awsClient := aws.RetryingClient aws.RetryingClient = &http.Client{Timeout: time.Millisecond} - _, err := NewDNSProviderRoute53("", "", "us-east-1") + _, err := NewDNSProvider("", "", "us-east-1") assert.EqualError(t, err, "No valid AWS authentication found") restoreRoute53Env() @@ -132,8 +132,8 @@ func TestNewDNSProviderRoute53MissingAuthErr(t *testing.T) { aws.RetryingClient = awsClient } -func TestNewDNSProviderRoute53InvalidRegionErr(t *testing.T) { - _, err := NewDNSProviderRoute53("123", "123", "us-east-3") +func TestNewDNSProviderInvalidRegionErr(t *testing.T) { + _, err := NewDNSProvider("123", "123", "us-east-3") assert.EqualError(t, err, "Invalid AWS region name us-east-3") } From c50baa67cb8b557d17842cb333f4affef8908ca7 Mon Sep 17 00:00:00 2001 From: xenolf Date: Fri, 11 Mar 2016 03:52:46 +0100 Subject: [PATCH 4/5] Move WaitFor into new utils.go and switch timeout and interval to time.Duration. --- acme/dns_challenge.go | 24 ------------------------ acme/dns_challenge_test.go | 20 -------------------- acme/utils.go | 29 +++++++++++++++++++++++++++++ acme/utils_test.go | 26 ++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 44 deletions(-) create mode 100644 acme/utils.go create mode 100644 acme/utils_test.go diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index 659d7082..b7be186f 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -8,7 +8,6 @@ import ( "log" "net" "strings" - "time" "github.com/miekg/dns" "golang.org/x/net/publicsuffix" @@ -256,26 +255,3 @@ func UnFqdn(name string) string { } return name } - -// WaitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds. -func WaitFor(timeout, interval int, f func() (bool, error)) error { - var lastErr string - timeup := time.After(time.Duration(timeout) * time.Second) - for { - select { - case <-timeup: - return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr) - default: - } - - stop, err := f() - if stop { - return nil - } - if err != nil { - lastErr = err.Error() - } - - time.Sleep(time.Duration(interval) * time.Second) - } -} diff --git a/acme/dns_challenge_test.go b/acme/dns_challenge_test.go index 760c7991..bfc66561 100644 --- a/acme/dns_challenge_test.go +++ b/acme/dns_challenge_test.go @@ -163,23 +163,3 @@ func TestCheckAuthoritativeNssErr(t *testing.T) { } } } - -func TestWaitForTimeout(t *testing.T) { - c := make(chan error) - go func() { - err := WaitFor(3, 1, func() (bool, error) { - return false, nil - }) - c <- err - }() - - timeout := time.After(4 * time.Second) - select { - case <-timeout: - t.Fatal("timeout exceeded") - case err := <-c: - if err == nil { - t.Errorf("expected timeout error; got %v", err) - } - } -} diff --git a/acme/utils.go b/acme/utils.go new file mode 100644 index 00000000..937a8f2d --- /dev/null +++ b/acme/utils.go @@ -0,0 +1,29 @@ +package acme + +import ( + "fmt" + "time" +) + +// WaitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds. +func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error { + var lastErr string + timeup := time.After(timeout * time.Second) + for { + select { + case <-timeup: + return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr) + default: + } + + stop, err := f() + if stop { + return nil + } + if err != nil { + lastErr = err.Error() + } + + time.Sleep(interval * time.Second) + } +} diff --git a/acme/utils_test.go b/acme/utils_test.go new file mode 100644 index 00000000..cb837cd5 --- /dev/null +++ b/acme/utils_test.go @@ -0,0 +1,26 @@ +package acme + +import ( + "testing" + "time" +) + +func TestWaitForTimeout(t *testing.T) { + c := make(chan error) + go func() { + err := WaitFor(3, 1, func() (bool, error) { + return false, nil + }) + c <- err + }() + + timeout := time.After(4 * time.Second) + select { + case <-timeout: + t.Fatal("timeout exceeded") + case err := <-c: + if err == nil { + t.Errorf("expected timeout error; got %v", err) + } + } +} From 3252b0bcb971f5fa1bf204e6e099642b2f3c9fbc Mon Sep 17 00:00:00 2001 From: xenolf Date: Fri, 11 Mar 2016 04:51:02 +0100 Subject: [PATCH 5/5] Fix WaitFor calls --- acme/dns_challenge.go | 3 ++- acme/utils.go | 6 +++--- acme/utils_test.go | 2 +- providers/dns/route53/route53.go | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/acme/dns_challenge.go b/acme/dns_challenge.go index b7be186f..e5be0105 100644 --- a/acme/dns_challenge.go +++ b/acme/dns_challenge.go @@ -8,6 +8,7 @@ import ( "log" "net" "strings" + "time" "github.com/miekg/dns" "golang.org/x/net/publicsuffix" @@ -68,7 +69,7 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { logf("[INFO][%s] Checking DNS record propagation...", domain) - err = WaitFor(30, 2, func() (bool, error) { + err = WaitFor(60*time.Second, 2*time.Second, func() (bool, error) { return preCheckDNS(fqdn, value) }) if err != nil { diff --git a/acme/utils.go b/acme/utils.go index 937a8f2d..2fa0db30 100644 --- a/acme/utils.go +++ b/acme/utils.go @@ -5,10 +5,10 @@ import ( "time" ) -// WaitFor polls the given function 'f', once every 'interval' seconds, up to 'timeout' seconds. +// WaitFor polls the given function 'f', once every 'interval', up to 'timeout'. func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error { var lastErr string - timeup := time.After(timeout * time.Second) + timeup := time.After(timeout) for { select { case <-timeup: @@ -24,6 +24,6 @@ func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error { lastErr = err.Error() } - time.Sleep(interval * time.Second) + time.Sleep(interval) } } diff --git a/acme/utils_test.go b/acme/utils_test.go index cb837cd5..158af411 100644 --- a/acme/utils_test.go +++ b/acme/utils_test.go @@ -8,7 +8,7 @@ import ( func TestWaitForTimeout(t *testing.T) { c := make(chan error) go func() { - err := WaitFor(3, 1, func() (bool, error) { + err := WaitFor(3*time.Second, 1*time.Second, func() (bool, error) { return false, nil }) c <- err diff --git a/providers/dns/route53/route53.go b/providers/dns/route53/route53.go index bfca45ac..eb1ffdf3 100644 --- a/providers/dns/route53/route53.go +++ b/providers/dns/route53/route53.go @@ -71,7 +71,7 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { return err } - return acme.WaitFor(90, 5, func() (bool, error) { + return acme.WaitFor(90*time.Second, 5*time.Second, func() (bool, error) { status, err := r.client.GetChange(resp.ChangeInfo.ID) if err != nil { return false, err