Skip to content

Commit

Permalink
Route53: Make it possible to configure from the env (go-acme#603)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mongey authored and ldez committed Sep 8, 2018
1 parent 725b6b8 commit ef7cd04
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 41 deletions.
12 changes: 12 additions & 0 deletions platform/config/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package env
import (
"fmt"
"os"
"strconv"
"strings"
)

Expand All @@ -25,3 +26,14 @@ func Get(names ...string) (map[string]string, error) {

return values, nil
}

// GetOrDefaultInt returns the given environment variable value as an integer.
// Returns the default if the envvar cannot be coopered to an int, or is not found.
func GetOrDefaultInt(envVar string, defaultValue int) int {
v, err := strconv.Atoi(os.Getenv(envVar))
if err != nil {
return defaultValue
}

return v
}
56 changes: 56 additions & 0 deletions platform/config/env/env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package env

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_GetOrDefaultInt(t *testing.T) {
testCases := []struct {
desc string
envValue string
defaultValue int
expected int
}{
{
desc: "valid value",
envValue: "100",
defaultValue: 2,
expected: 100,
},
{
desc: "invalid content, use default value",
envValue: "abc123",
defaultValue: 2,
expected: 2,
},
{
desc: "valid negative value",
envValue: "-111",
defaultValue: 2,
expected: -111,
},
{
desc: "float: invalid type, use default value",
envValue: "1.11",
defaultValue: 2,
expected: 2,
},
}

const key = "LEGO_ENV_TC"

for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
defer os.Unsetenv(key)
err := os.Setenv(key, test.envValue)
require.NoError(t, err)

result := GetOrDefaultInt(key, test.defaultValue)
assert.Equal(t, test.expected, result)
})
}
}
11 changes: 7 additions & 4 deletions providers/dns/route53/route53.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
)

// Config is used to configure the creation of the DNSProvider
Expand All @@ -29,11 +30,13 @@ type Config struct {

// NewDefaultConfig returns a default configuration for the DNSProvider
func NewDefaultConfig() *Config {
propagationMins := env.GetOrDefaultInt("AWS_PROPAGATION_TIMEOUT", 2)
intervalSecs := env.GetOrDefaultInt("AWS_POLLING_INTERVAL", 4)
return &Config{
MaxRetries: 5,
TTL: 10,
PropagationTimeout: time.Minute * 2,
PollingInterval: time.Second * 4,
MaxRetries: env.GetOrDefaultInt("AWS_MAX_RETRIES", 5),
TTL: env.GetOrDefaultInt("AWS_TTL", 10),
PropagationTimeout: time.Minute * time.Duration(propagationMins),
PollingInterval: time.Second * time.Duration(intervalSecs),
HostedZoneID: os.Getenv("AWS_HOSTED_ZONE_ID"),
}
}
Expand Down
34 changes: 9 additions & 25 deletions providers/dns/route53/route53_integration_test.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
package route53

import (
"fmt"
"os"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/stretchr/testify/require"
"github.com/xenolf/lego/platform/config/env"
)

func TestRoute53TTL(t *testing.T) {
m, err := testGetAndPreCheck()
config, err := env.Get("AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "R53_DOMAIN")
if err != nil {
t.Skip(err.Error())
}

provider, err := NewDNSProvider()
require.NoError(t, err)

err = provider.Present(m["route53Domain"], "foo", "bar")
err = provider.Present(config["R53_DOMAIN"], "foo", "bar")
require.NoError(t, err)

// we need a separate R53 client here as the one in the DNS provider is
// unexported.
fqdn := "_acme-challenge." + m["route53Domain"] + "."
fqdn := "_acme-challenge." + config["R53_DOMAIN"] + "."
svc := route53.New(session.New())
zoneID, err := provider.getHostedZoneID(fqdn)
if err != nil {
provider.CleanUp(m["route53Domain"], "foo", "bar")
provider.CleanUp(config["R53_DOMAIN"], "foo", "bar")
t.Fatal(err)
}

Expand All @@ -38,32 +37,17 @@ func TestRoute53TTL(t *testing.T) {
}
resp, err := svc.ListResourceRecordSets(params)
if err != nil {
provider.CleanUp(m["route53Domain"], "foo", "bar")
provider.CleanUp(config["R53_DOMAIN"], "foo", "bar")
t.Fatal(err)
}

for _, v := range resp.ResourceRecordSets {
if aws.StringValue(v.Name) == fqdn && aws.StringValue(v.Type) == "TXT" && aws.Int64Value(v.TTL) == 10 {
provider.CleanUp(m["route53Domain"], "foo", "bar")
provider.CleanUp(config["R53_DOMAIN"], "foo", "bar")
return
}
}

provider.CleanUp(m["route53Domain"], "foo", "bar")
t.Fatalf("Could not find a TXT record for _acme-challenge.%s with a TTL of 10", m["route53Domain"])
}

func testGetAndPreCheck() (map[string]string, error) {
m := map[string]string{
"route53Key": os.Getenv("AWS_ACCESS_KEY_ID"),
"route53Secret": os.Getenv("AWS_SECRET_ACCESS_KEY"),
"route53Region": os.Getenv("AWS_REGION"),
"route53Domain": os.Getenv("R53_DOMAIN"),
}
for _, v := range m {
if v == "" {
return nil, fmt.Errorf("AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, and R53_DOMAIN are needed to run this test")
}
}
return m, nil
provider.CleanUp(config["R53_DOMAIN"], "foo", "bar")
t.Fatalf("Could not find a TXT record for _acme-challenge.%s with a TTL of 10", config["R53_DOMAIN"])
}
62 changes: 50 additions & 12 deletions providers/dns/route53/route53_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/http/httptest"
"os"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
Expand All @@ -13,24 +14,40 @@ import (
)

var (
route53Secret string
route53Key string
route53Region string
route53Zone string
r53AwsSecretAccessKey string
r53AwsAccessKeyID string
r53AwsRegion string
r53AwsHostedZoneID string

r53AwsMaxRetries string
r53AwsTTL string
r53AwsPropagationTimeout string
r53AwsPollingInterval string
)

func init() {
route53Key = os.Getenv("AWS_ACCESS_KEY_ID")
route53Secret = os.Getenv("AWS_SECRET_ACCESS_KEY")
route53Region = os.Getenv("AWS_REGION")
route53Zone = os.Getenv("AWS_HOSTED_ZONE_ID")
r53AwsAccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID")
r53AwsSecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
r53AwsRegion = os.Getenv("AWS_REGION")
r53AwsHostedZoneID = os.Getenv("AWS_HOSTED_ZONE_ID")

r53AwsMaxRetries = os.Getenv("AWS_MAX_RETRIES")
r53AwsTTL = os.Getenv("AWS_TTL")
r53AwsPropagationTimeout = os.Getenv("AWS_PROPAGATION_TIMEOUT")
r53AwsPollingInterval = os.Getenv("AWS_POLLING_INTERVAL")
}

func restoreEnv() {
os.Setenv("AWS_ACCESS_KEY_ID", route53Key)
os.Setenv("AWS_SECRET_ACCESS_KEY", route53Secret)
os.Setenv("AWS_REGION", route53Region)
os.Setenv("AWS_HOSTED_ZONE_ID", route53Zone)
os.Setenv("AWS_ACCESS_KEY_ID", r53AwsAccessKeyID)
os.Setenv("AWS_SECRET_ACCESS_KEY", r53AwsSecretAccessKey)
os.Setenv("AWS_REGION", r53AwsRegion)
os.Setenv("AWS_HOSTED_ZONE_ID", r53AwsHostedZoneID)

os.Setenv("AWS_MAX_RETRIES", r53AwsMaxRetries)
os.Setenv("AWS_TTL", r53AwsTTL)
os.Setenv("AWS_PROPAGATION_TIMEOUT", r53AwsPropagationTimeout)
os.Setenv("AWS_POLLING_INTERVAL", r53AwsPollingInterval)

}

func makeRoute53Provider(ts *httptest.Server) *DNSProvider {
Expand Down Expand Up @@ -84,6 +101,27 @@ func TestHostedZoneIDFromEnv(t *testing.T) {
assert.Equal(t, testZoneID, fqdn)
}

func TestConfigFromEnv(t *testing.T) {
defer restoreEnv()

config := NewDefaultConfig()
assert.Equal(t, config.TTL, 10, "Expected TTL to be use the default")

os.Setenv("AWS_MAX_RETRIES", "10")
os.Setenv("AWS_TTL", "99")
os.Setenv("AWS_PROPAGATION_TIMEOUT", "60")
os.Setenv("AWS_POLLING_INTERVAL", "60")
const zoneID = "abc123"
os.Setenv("AWS_HOSTED_ZONE_ID", zoneID)

config = NewDefaultConfig()
assert.Equal(t, config.MaxRetries, 10, "Expected PropagationTimeout to be configured from the environment")
assert.Equal(t, config.TTL, 99, "Expected TTL to be configured from the environment")
assert.Equal(t, config.PropagationTimeout, time.Minute*60, "Expected PropagationTimeout to be configured from the environment")
assert.Equal(t, config.PollingInterval, time.Second*60, "Expected PollingInterval to be configured from the environment")
assert.Equal(t, config.HostedZoneID, zoneID, "Expected HostedZoneID to be configured from the environment")
}

func TestRoute53Present(t *testing.T) {
mockResponses := MockResponseMap{
"/2013-04-01/hostedzonesbyname": MockResponse{StatusCode: 200, Body: ListHostedZonesByNameResponse},
Expand Down

0 comments on commit ef7cd04

Please sign in to comment.