Review DNS providers. (#565)

* refactor: review DNS providers.
This commit is contained in:
Ludovic Fernandez 2018-06-11 17:32:50 +02:00 committed by GitHub
parent 8f9e90b2a0
commit c4bbb4b819
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 671 additions and 560 deletions

27
platform/config/env/env.go vendored Normal file
View file

@ -0,0 +1,27 @@
package env
import (
"fmt"
"os"
"strings"
)
// Get environment variables
func Get(names ...string) (map[string]string, error) {
values := map[string]string{}
var missingEnvVars []string
for _, envVar := range names {
value := os.Getenv(envVar)
if value == "" {
missingEnvVars = append(missingEnvVars, envVar)
}
values[envVar] = value
}
if len(missingEnvVars) > 0 {
return nil, fmt.Errorf("some credentials information are missing: %s", strings.Join(missingEnvVars, ","))
}
return values, nil
}

View file

@ -9,6 +9,7 @@ import (
"github.com/edeckers/auroradnsclient/records" "github.com/edeckers/auroradnsclient/records"
"github.com/edeckers/auroradnsclient/zones" "github.com/edeckers/auroradnsclient/zones"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider describes a provider for AuroraDNS // DNSProvider describes a provider for AuroraDNS
@ -22,20 +23,23 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: AURORA_USER_ID // Credentials must be passed in the environment variables: AURORA_USER_ID
// and AURORA_KEY. // and AURORA_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
userID := os.Getenv("AURORA_USER_ID") values, err := env.Get("AURORA_USER_ID", "AURORA_KEY")
key := os.Getenv("AURORA_KEY") if err != nil {
return nil, fmt.Errorf("AuroraDNS: %v", err)
endpoint := os.Getenv("AURORA_ENDPOINT")
if endpoint == "" {
endpoint = "https://api.auroradns.eu"
} }
return NewDNSProviderCredentials(endpoint, userID, key) endpoint := os.Getenv("AURORA_ENDPOINT")
return NewDNSProviderCredentials(endpoint, values["AURORA_USER_ID"], values["AURORA_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for AuroraDNS. // DNSProvider instance configured for AuroraDNS.
func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSProvider, error) { func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSProvider, error) {
if baseURL == "" {
baseURL = "https://api.auroradns.eu"
}
client, err := auroradnsclient.NewAuroraDNSClient(baseURL, userID, key) client, err := auroradnsclient.NewAuroraDNSClient(baseURL, userID, key)
if err != nil { if err != nil {
return nil, err return nil, err
@ -69,7 +73,7 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil { if err != nil {
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err)
} }
// 1. Aurora will happily create the TXT record when it is provided a fqdn, // 1. Aurora will happily create the TXT record when it is provided a fqdn,

View file

@ -6,6 +6,9 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var fakeAuroraDNSUserID = "asdf1234" var fakeAuroraDNSUserID = "asdf1234"
@ -26,28 +29,13 @@ func TestAuroraDNSPresent(t *testing.T) {
requestReceived = true requestReceived = true
if got, want := r.Method, "POST"; got != want { assert.Equal(t, http.MethodPost, r.Method, "method")
t.Errorf("Expected method to be '%s' but got '%s'", want, got) assert.Equal(t, "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records", r.URL.Path, "Path")
} assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
if got, want := r.URL.Path, "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records"; got != want {
t.Errorf("Expected path to be '%s' but got '%s'", want, got)
}
if got, want := r.Header.Get("Content-Type"), "application/json"; got != want {
t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got)
}
reqBody, err := ioutil.ReadAll(r.Body) reqBody, err := ioutil.ReadAll(r.Body)
if err != nil { require.NoError(t, err, "reading request body")
t.Fatalf("Error reading request body: %v", err) assert.Equal(t, `{"type":"TXT","name":"_acme-challenge","content":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":300}`, string(reqBody))
}
if got, want := string(reqBody),
`{"type":"TXT","name":"_acme-challenge","content":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":300}`; got != want {
t.Errorf("Expected body data to be: `%s` but got `%s`", want, got)
}
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{ fmt.Fprintf(w, `{
@ -61,22 +49,13 @@ func TestAuroraDNSPresent(t *testing.T) {
defer mock.Close() defer mock.Close()
auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserID, fakeAuroraDNSKey) auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserID, fakeAuroraDNSKey)
if auroraProvider == nil { require.NoError(t, err)
t.Fatal("Expected non-nil AuroraDNS provider, but was nil") require.NotNil(t, auroraProvider)
}
if err != nil {
t.Fatalf("Expected no error creating provider, but got: %v", err)
}
err = auroraProvider.Present("example.com", "", "foobar") err = auroraProvider.Present("example.com", "", "foobar")
if err != nil { require.NoError(t, err, "fail to create TXT record")
t.Fatalf("Expected no error creating TXT record, but got: %v", err)
}
if !requestReceived { assert.True(t, requestReceived, "Expected request to be received by mock backend, but it wasn't")
t.Error("Expected request to be received by mock backend, but it wasn't")
}
} }
func TestAuroraDNSCleanUp(t *testing.T) { func TestAuroraDNSCleanUp(t *testing.T) {
@ -105,18 +84,9 @@ func TestAuroraDNSCleanUp(t *testing.T) {
requestReceived = true requestReceived = true
if got, want := r.Method, "DELETE"; got != want { assert.Equal(t, http.MethodDelete, r.Method, "method")
t.Errorf("Expected method to be '%s' but got '%s'", want, got) assert.Equal(t, "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records/ec56a4180-65aa-42ec-a945-5fd21dec0538", r.URL.Path, "Path")
} assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
if got, want := r.URL.Path,
"/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records/ec56a4180-65aa-42ec-a945-5fd21dec0538"; got != want {
t.Errorf("Expected path to be '%s' but got '%s'", want, got)
}
if got, want := r.Header.Get("Content-Type"), "application/json"; got != want {
t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got)
}
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{}`) fmt.Fprintf(w, `{}`)
@ -124,25 +94,14 @@ func TestAuroraDNSCleanUp(t *testing.T) {
defer mock.Close() defer mock.Close()
auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserID, fakeAuroraDNSKey) auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserID, fakeAuroraDNSKey)
if auroraProvider == nil { require.NoError(t, err)
t.Fatal("Expected non-nil AuroraDNS provider, but was nil") require.NotNil(t, auroraProvider)
}
if err != nil {
t.Fatalf("Expected no error creating provider, but got: %v", err)
}
err = auroraProvider.Present("example.com", "", "foobar") err = auroraProvider.Present("example.com", "", "foobar")
if err != nil { require.NoError(t, err, "fail to create TXT record")
t.Fatalf("Expected no error creating TXT record, but got: %v", err)
}
err = auroraProvider.CleanUp("example.com", "", "foobar") err = auroraProvider.CleanUp("example.com", "", "foobar")
if err != nil { require.NoError(t, err, "fail to remove TXT record")
t.Fatalf("Expected no error removing TXT record, but got: %v", err)
}
if !requestReceived { assert.True(t, requestReceived, "Expected request to be received by mock backend, but it wasn't")
t.Error("Expected request to be received by mock backend, but it wasn't")
}
} }

View file

@ -5,8 +5,8 @@ package azure
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
@ -16,6 +16,7 @@ import (
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to" "github.com/Azure/go-autorest/autorest/to"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface // DNSProvider is an implementation of the acme.ChallengeProvider interface
@ -32,25 +33,25 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: AZURE_CLIENT_ID, // Credentials must be passed in the environment variables: AZURE_CLIENT_ID,
// AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP // AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
clientID := os.Getenv("AZURE_CLIENT_ID") values, err := env.Get("AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_SUBSCRIPTION_ID", "AZURE_TENANT_ID", "AZURE_RESOURCE_GROUP")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET") if err != nil {
subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID") return nil, fmt.Errorf("Azure: %v", err)
tenantID := os.Getenv("AZURE_TENANT_ID") }
resourceGroup := os.Getenv("AZURE_RESOURCE_GROUP")
return NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup) return NewDNSProviderCredentials(
values["AZURE_CLIENT_ID"],
values["AZURE_CLIENT_SECRET"],
values["AZURE_SUBSCRIPTION_ID"],
values["AZURE_TENANT_ID"],
values["AZURE_RESOURCE_GROUP"],
)
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for azure. // DNSProvider instance configured for azure.
func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup string) (*DNSProvider, error) { func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup string) (*DNSProvider, error) {
if clientID == "" || clientSecret == "" || subscriptionID == "" || tenantID == "" || resourceGroup == "" { if clientID == "" || clientSecret == "" || subscriptionID == "" || tenantID == "" || resourceGroup == "" {
var missingEnvVars []string return nil, errors.New("Azure: some credentials information are missing")
for _, envVar := range []string{"AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_SUBSCRIPTION_ID", "AZURE_TENANT_ID", "AZURE_RESOURCE_GROUP"} {
if os.Getenv(envVar) == "" {
missingEnvVars = append(missingEnvVars, envVar)
}
}
return nil, fmt.Errorf("Azure configuration missing: %s", strings.Join(missingEnvVars, ","))
} }
return &DNSProvider{ return &DNSProvider{

View file

@ -30,7 +30,7 @@ func init() {
} }
} }
func restoreAzureEnv() { func restoreEnv() {
os.Setenv("AZURE_CLIENT_ID", azureClientID) os.Setenv("AZURE_CLIENT_ID", azureClientID)
os.Setenv("AZURE_SUBSCRIPTION_ID", azureSubscriptionID) os.Setenv("AZURE_SUBSCRIPTION_ID", azureSubscriptionID)
} }
@ -39,27 +39,32 @@ func TestNewDNSProviderValid(t *testing.T) {
if !azureLiveTest { if !azureLiveTest {
t.Skip("skipping live test (requires credentials)") t.Skip("skipping live test (requires credentials)")
} }
defer restoreEnv()
os.Setenv("AZURE_CLIENT_ID", "") os.Setenv("AZURE_CLIENT_ID", "")
_, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup) _, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup)
assert.NoError(t, err) assert.NoError(t, err)
restoreAzureEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
if !azureLiveTest { if !azureLiveTest {
t.Skip("skipping live test (requires credentials)") t.Skip("skipping live test (requires credentials)")
} }
defer restoreEnv()
os.Setenv("AZURE_CLIENT_ID", "other") os.Setenv("AZURE_CLIENT_ID", "other")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreAzureEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("AZURE_SUBSCRIPTION_ID", "") os.Setenv("AZURE_SUBSCRIPTION_ID", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "Azure configuration missing: AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_SUBSCRIPTION_ID,AZURE_TENANT_ID,AZURE_RESOURCE_GROUP") assert.EqualError(t, err, "Azure: some credentials information are missing: AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_SUBSCRIPTION_ID,AZURE_TENANT_ID,AZURE_RESOURCE_GROUP")
restoreAzureEnv()
} }
func TestLiveAzurePresent(t *testing.T) { func TestLiveAzurePresent(t *testing.T) {

View file

@ -6,16 +6,15 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"io/ioutil"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
const bluecatURLTemplate = "%s/Services/REST/v1" const bluecatURLTemplate = "%s/Services/REST/v1"
@ -51,29 +50,42 @@ type DNSProvider struct {
// and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and // and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and
// BLUECAT_DNS_VIEW // BLUECAT_DNS_VIEW
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
server := os.Getenv("BLUECAT_SERVER_URL") values, err := env.Get("BLUECAT_SERVER_URL", "BLUECAT_USER_NAME", "BLUECAT_CONFIG_NAME", "BLUECAT_CONFIG_NAME", "BLUECAT_DNS_VIEW")
userName := os.Getenv("BLUECAT_USER_NAME") if err != nil {
password := os.Getenv("BLUECAT_PASSWORD") return nil, fmt.Errorf("BlueCat: %v", err)
configName := os.Getenv("BLUECAT_CONFIG_NAME") }
dnsView := os.Getenv("BLUECAT_DNS_VIEW")
httpClient := http.Client{Timeout: 30 * time.Second} httpClient := &http.Client{Timeout: 30 * time.Second}
return NewDNSProviderCredentials(server, userName, password, configName, dnsView, httpClient)
return NewDNSProviderCredentials(
values["BLUECAT_SERVER_URL"],
values["BLUECAT_USER_NAME"],
values["BLUECAT_PASSWORD"],
values["BLUECAT_CONFIG_NAME"],
values["BLUECAT_DNS_VIEW"],
httpClient,
)
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for Bluecat DNS. // DNSProvider instance configured for Bluecat DNS.
func NewDNSProviderCredentials(server, userName, password, configName, dnsView string, httpClient http.Client) (*DNSProvider, error) { func NewDNSProviderCredentials(server, userName, password, configName, dnsView string, httpClient *http.Client) (*DNSProvider, error) {
if server == "" || userName == "" || password == "" || configName == "" || dnsView == "" { if server == "" || userName == "" || password == "" || configName == "" || dnsView == "" {
return nil, fmt.Errorf("Bluecat credentials missing") return nil, fmt.Errorf("Bluecat credentials missing")
} }
client := http.DefaultClient
if httpClient != nil {
client = httpClient
}
return &DNSProvider{ return &DNSProvider{
baseURL: fmt.Sprintf(bluecatURLTemplate, server), baseURL: fmt.Sprintf(bluecatURLTemplate, server),
userName: userName, userName: userName,
password: password, password: password,
configName: configName, configName: configName,
dnsView: dnsView, dnsView: dnsView,
httpClient: http.DefaultClient, httpClient: client,
}, nil }, nil
} }

View file

@ -5,15 +5,15 @@ package cloudflare
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os"
"strings"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// CloudFlareAPIURL represents the API endpoint to call. // CloudFlareAPIURL represents the API endpoint to call.
@ -30,23 +30,19 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: CLOUDFLARE_EMAIL // Credentials must be passed in the environment variables: CLOUDFLARE_EMAIL
// and CLOUDFLARE_API_KEY. // and CLOUDFLARE_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
email := os.Getenv("CLOUDFLARE_EMAIL") values, err := env.Get("CLOUDFLARE_EMAIL", "CLOUDFLARE_API_KEY")
key := os.Getenv("CLOUDFLARE_API_KEY") if err != nil {
return NewDNSProviderCredentials(email, key) return nil, fmt.Errorf("CloudFlare: %v", err)
}
return NewDNSProviderCredentials(values["CLOUDFLARE_EMAIL"], values["CLOUDFLARE_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for cloudflare. // DNSProvider instance configured for cloudflare.
func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) {
if email == "" || key == "" { if email == "" || key == "" {
missingEnvVars := []string{} return nil, errors.New("CloudFlare: some credentials information are missing")
if email == "" {
missingEnvVars = append(missingEnvVars, "CLOUDFLARE_EMAIL")
}
if key == "" {
missingEnvVars = append(missingEnvVars, "CLOUDFLARE_API_KEY")
}
return nil, fmt.Errorf("CloudFlare credentials missing: %s", strings.Join(missingEnvVars, ","))
} }
return &DNSProvider{ return &DNSProvider{
@ -63,7 +59,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
// Present creates a TXT record to fulfil the dns-01 challenge // Present creates a TXT record to fulfil the dns-01 challenge
func (c *DNSProvider) Present(domain, token, keyAuth string) error { func (c *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, _ := acme.DNS01Record(domain, keyAuth) fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
zoneID, err := c.getHostedZoneID(fqdn) zoneID, err := c.getHostedZoneID(fqdn)
if err != nil { if err != nil {
return err return err
@ -73,7 +69,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
Type: "TXT", Type: "TXT",
Name: acme.UnFqdn(fqdn), Name: acme.UnFqdn(fqdn),
Content: value, Content: value,
TTL: 120, TTL: ttl,
} }
body, err := json.Marshal(rec) body, err := json.Marshal(rec)
@ -122,7 +118,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
} }
if len(hostedZone) != 1 { if len(hostedZone) != 1 {
return "", fmt.Errorf("Zone %s not found in CloudFlare for domain %s", authZone, fqdn) return "", fmt.Errorf("zone %s not found in CloudFlare for domain %s", authZone, fqdn)
} }
return hostedZone[0].ID, nil return hostedZone[0].ID, nil
@ -184,7 +180,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
client := http.Client{Timeout: 30 * time.Second} client := http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error querying Cloudflare API -> %v", err) return nil, fmt.Errorf("error querying Cloudflare API -> %v", err)
} }
defer resp.Body.Close() defer resp.Body.Close()

View file

@ -24,7 +24,7 @@ func init() {
} }
} }
func restoreCloudFlareEnv() { func restoreEnv() {
os.Setenv("CLOUDFLARE_EMAIL", cflareEmail) os.Setenv("CLOUDFLARE_EMAIL", cflareEmail)
os.Setenv("CLOUDFLARE_API_KEY", cflareAPIKey) os.Setenv("CLOUDFLARE_API_KEY", cflareAPIKey)
} }
@ -32,32 +32,37 @@ func restoreCloudFlareEnv() {
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_EMAIL", "")
os.Setenv("CLOUDFLARE_API_KEY", "") os.Setenv("CLOUDFLARE_API_KEY", "")
defer restoreEnv()
_, err := NewDNSProviderCredentials("123", "123") _, err := NewDNSProviderCredentials("123", "123")
assert.NoError(t, err) assert.NoError(t, err)
restoreCloudFlareEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("CLOUDFLARE_EMAIL", "test@example.com") os.Setenv("CLOUDFLARE_EMAIL", "test@example.com")
os.Setenv("CLOUDFLARE_API_KEY", "123") os.Setenv("CLOUDFLARE_API_KEY", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreCloudFlareEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("CLOUDFLARE_EMAIL", "") os.Setenv("CLOUDFLARE_EMAIL", "")
os.Setenv("CLOUDFLARE_API_KEY", "") os.Setenv("CLOUDFLARE_API_KEY", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "CloudFlare credentials missing: CLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY") assert.EqualError(t, err, "CloudFlare: some credentials information are missing: CLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY")
restoreCloudFlareEnv()
} }
func TestNewDNSProviderMissingCredErrSingle(t *testing.T){ func TestNewDNSProviderMissingCredErrSingle(t *testing.T) {
os.Setenv("CLOUDFLARE_EMAIL", "awesome@possum.com") defer restoreEnv()
_, err:= NewDNSProvider() os.Setenv("CLOUDFLARE_EMAIL", "awesome@possum.com")
assert.EqualError(t, err, "CloudFlare credentials missing: CLOUDFLARE_API_KEY")
restoreCloudFlareEnv() _, err := NewDNSProvider()
assert.EqualError(t, err, "CloudFlare: some credentials information are missing: CLOUDFLARE_API_KEY")
} }
func TestCloudFlarePresent(t *testing.T) { func TestCloudFlarePresent(t *testing.T) {

View file

@ -9,11 +9,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strconv" "strconv"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
const cloudXNSBaseURL = "https://www.cloudxns.net/api2/" const cloudXNSBaseURL = "https://www.cloudxns.net/api2/"
@ -28,9 +28,12 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: CLOUDXNS_API_KEY // Credentials must be passed in the environment variables: CLOUDXNS_API_KEY
// and CLOUDXNS_SECRET_KEY. // and CLOUDXNS_SECRET_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiKey := os.Getenv("CLOUDXNS_API_KEY") values, err := env.Get("CLOUDXNS_API_KEY", "CLOUDXNS_SECRET_KEY")
secretKey := os.Getenv("CLOUDXNS_SECRET_KEY") if err != nil {
return NewDNSProviderCredentials(apiKey, secretKey) return nil, fmt.Errorf("CloudXNS: %v", err)
}
return NewDNSProviderCredentials(values["CLOUDXNS_API_KEY"], values["CLOUDXNS_SECRET_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -24,33 +24,36 @@ func init() {
} }
} }
func restoreCloudXNSEnv() { func restoreEnv() {
os.Setenv("CLOUDXNS_API_KEY", cxAPIKey) os.Setenv("CLOUDXNS_API_KEY", cxAPIKey)
os.Setenv("CLOUDXNS_SECRET_KEY", cxSecretKey) os.Setenv("CLOUDXNS_SECRET_KEY", cxSecretKey)
} }
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
defer restoreEnv()
os.Setenv("CLOUDXNS_API_KEY", "") os.Setenv("CLOUDXNS_API_KEY", "")
os.Setenv("CLOUDXNS_SECRET_KEY", "") os.Setenv("CLOUDXNS_SECRET_KEY", "")
_, err := NewDNSProviderCredentials("123", "123") _, err := NewDNSProviderCredentials("123", "123")
assert.NoError(t, err) assert.NoError(t, err)
restoreCloudXNSEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("CLOUDXNS_API_KEY", "123") os.Setenv("CLOUDXNS_API_KEY", "123")
os.Setenv("CLOUDXNS_SECRET_KEY", "123") os.Setenv("CLOUDXNS_SECRET_KEY", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreCloudXNSEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("CLOUDXNS_API_KEY", "") os.Setenv("CLOUDXNS_API_KEY", "")
os.Setenv("CLOUDXNS_SECRET_KEY", "") os.Setenv("CLOUDXNS_SECRET_KEY", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "CloudXNS credentials missing") assert.EqualError(t, err, "CloudXNS: some credentials information are missing: CLOUDXNS_API_KEY,CLOUDXNS_SECRET_KEY")
restoreCloudXNSEnv()
} }
func TestCloudXNSPresent(t *testing.T) { func TestCloudXNSPresent(t *testing.T) {

View file

@ -7,11 +7,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"sync" "sync"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface // DNSProvider is an implementation of the acme.ChallengeProvider interface
@ -32,8 +32,12 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
// Ocean. Credentials must be passed in the environment variable: // Ocean. Credentials must be passed in the environment variable:
// DO_AUTH_TOKEN. // DO_AUTH_TOKEN.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiAuthToken := os.Getenv("DO_AUTH_TOKEN") values, err := env.Get("DO_AUTH_TOKEN")
return NewDNSProviderCredentials(apiAuthToken) if err != nil {
return nil, fmt.Errorf("DigitalOcean: %v", err)
}
return NewDNSProviderCredentials(values["DO_AUTH_TOKEN"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -6,6 +6,9 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var fakeDigitalOceanAuth = "asdf1234" var fakeDigitalOceanAuth = "asdf1234"
@ -16,26 +19,14 @@ func TestDigitalOceanPresent(t *testing.T) {
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestReceived = true requestReceived = true
if got, want := r.Method, "POST"; got != want { assert.Equal(t, http.MethodPost, r.Method, "method")
t.Errorf("Expected method to be '%s' but got '%s'", want, got) assert.Equal(t, "/v2/domains/example.com/records", r.URL.Path, "Path")
} assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
if got, want := r.URL.Path, "/v2/domains/example.com/records"; got != want { assert.Equal(t, "Bearer asdf1234", r.Header.Get("Authorization"), "Authorization")
t.Errorf("Expected path to be '%s' but got '%s'", want, got)
}
if got, want := r.Header.Get("Content-Type"), "application/json"; got != want {
t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got)
}
if got, want := r.Header.Get("Authorization"), "Bearer asdf1234"; got != want {
t.Errorf("Expected Authorization to be '%s' but got '%s'", want, got)
}
reqBody, err := ioutil.ReadAll(r.Body) reqBody, err := ioutil.ReadAll(r.Body)
if err != nil { require.NoError(t, err, "reading request body")
t.Fatalf("Error reading request body: %v", err) assert.Equal(t, `{"type":"TXT","name":"_acme-challenge.example.com.","data":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":30}`, string(reqBody))
}
if got, want := string(reqBody), `{"type":"TXT","name":"_acme-challenge.example.com.","data":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":30}`; got != want {
t.Errorf("Expected body data to be: `%s` but got `%s`", want, got)
}
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{ fmt.Fprintf(w, `{
@ -54,20 +45,13 @@ func TestDigitalOceanPresent(t *testing.T) {
digitalOceanBaseURL = mock.URL digitalOceanBaseURL = mock.URL
doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth) doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth)
if doprov == nil { require.NoError(t, err)
t.Fatal("Expected non-nil DigitalOcean provider, but was nil") require.NotNil(t, doprov)
}
if err != nil {
t.Fatalf("Expected no error creating provider, but got: %v", err)
}
err = doprov.Present("example.com", "", "foobar") err = doprov.Present("example.com", "", "foobar")
if err != nil { require.NoError(t, err, "fail to create TXT record")
t.Fatalf("Expected no error creating TXT record, but got: %v", err)
} assert.True(t, requestReceived, "Expected request to be received by mock backend, but it wasn't")
if !requestReceived {
t.Error("Expected request to be received by mock backend, but it wasn't")
}
} }
func TestDigitalOceanCleanUp(t *testing.T) { func TestDigitalOceanCleanUp(t *testing.T) {
@ -76,19 +60,11 @@ func TestDigitalOceanCleanUp(t *testing.T) {
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestReceived = true requestReceived = true
if got, want := r.Method, "DELETE"; got != want { assert.Equal(t, http.MethodDelete, r.Method, "method")
t.Errorf("Expected method to be '%s' but got '%s'", want, got) assert.Equal(t, "/v2/domains/example.com/records/1234567", r.URL.Path, "Path")
}
if got, want := r.URL.Path, "/v2/domains/example.com/records/1234567"; got != want {
t.Errorf("Expected path to be '%s' but got '%s'", want, got)
}
// NOTE: Even though the body is empty, DigitalOcean API docs still show setting this Content-Type... // NOTE: Even though the body is empty, DigitalOcean API docs still show setting this Content-Type...
if got, want := r.Header.Get("Content-Type"), "application/json"; got != want { assert.Equal(t, "application/json", r.Header.Get("Content-Type"), "Content-Type")
t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got) assert.Equal(t, "Bearer asdf1234", r.Header.Get("Authorization"), "Authorization")
}
if got, want := r.Header.Get("Authorization"), "Bearer asdf1234"; got != want {
t.Errorf("Expected Authorization to be '%s' but got '%s'", want, got)
}
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
})) }))
@ -96,22 +72,15 @@ func TestDigitalOceanCleanUp(t *testing.T) {
digitalOceanBaseURL = mock.URL digitalOceanBaseURL = mock.URL
doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth) doprov, err := NewDNSProviderCredentials(fakeDigitalOceanAuth)
if doprov == nil { require.NoError(t, err)
t.Fatal("Expected non-nil DigitalOcean provider, but was nil") require.NotNil(t, doprov)
}
if err != nil {
t.Fatalf("Expected no error creating provider, but got: %v", err)
}
doprov.recordIDsMu.Lock() doprov.recordIDsMu.Lock()
doprov.recordIDs["_acme-challenge.example.com."] = 1234567 doprov.recordIDs["_acme-challenge.example.com."] = 1234567
doprov.recordIDsMu.Unlock() doprov.recordIDsMu.Unlock()
err = doprov.CleanUp("example.com", "", "") err = doprov.CleanUp("example.com", "", "")
if err != nil { require.NoError(t, err, "fail to remove TXT record")
t.Fatalf("Expected no error removing TXT record, but got: %v", err)
} assert.True(t, requestReceived, "Expected request to be received by mock backend, but it wasn't")
if !requestReceived {
t.Error("Expected request to be received by mock backend, but it wasn't")
}
} }

View file

@ -2,7 +2,6 @@ package dns
import ( import (
"os" "os"
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -25,23 +24,24 @@ func restoreExoscaleEnv() {
} }
func TestKnownDNSProviderSuccess(t *testing.T) { func TestKnownDNSProviderSuccess(t *testing.T) {
defer restoreExoscaleEnv()
os.Setenv("EXOSCALE_API_KEY", "abc") os.Setenv("EXOSCALE_API_KEY", "abc")
os.Setenv("EXOSCALE_API_SECRET", "123") os.Setenv("EXOSCALE_API_SECRET", "123")
provider, err := NewDNSChallengeProviderByName("exoscale") provider, err := NewDNSChallengeProviderByName("exoscale")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, provider) assert.NotNil(t, provider)
if reflect.TypeOf(provider) != reflect.TypeOf(&exoscale.DNSProvider{}) {
t.Errorf("Not loaded correct DNS proviver: %v is not *exoscale.DNSProvider", reflect.TypeOf(provider)) assert.IsType(t, &exoscale.DNSProvider{}, provider, "Not loaded correct DNS provider")
}
restoreExoscaleEnv()
} }
func TestKnownDNSProviderError(t *testing.T) { func TestKnownDNSProviderError(t *testing.T) {
defer restoreExoscaleEnv()
os.Setenv("EXOSCALE_API_KEY", "") os.Setenv("EXOSCALE_API_KEY", "")
os.Setenv("EXOSCALE_API_SECRET", "") os.Setenv("EXOSCALE_API_SECRET", "")
_, err := NewDNSChallengeProviderByName("exoscale") _, err := NewDNSChallengeProviderByName("exoscale")
assert.Error(t, err) assert.Error(t, err)
restoreExoscaleEnv()
} }
func TestUnknownDNSProvider(t *testing.T) { func TestUnknownDNSProvider(t *testing.T) {

View file

@ -31,7 +31,7 @@ func init() {
} }
} }
func restoreDNSimpleEnv() { func restoreEnv() {
os.Setenv("DNSIMPLE_OAUTH_TOKEN", dnsimpleOauthToken) os.Setenv("DNSIMPLE_OAUTH_TOKEN", dnsimpleOauthToken)
os.Setenv("DNSIMPLE_BASE_URL", dnsimpleBaseURL) os.Setenv("DNSIMPLE_BASE_URL", dnsimpleBaseURL)
} }
@ -41,9 +41,9 @@ func restoreDNSimpleEnv() {
// //
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
defer restoreDNSimpleEnv() defer restoreEnv()
os.Setenv("DNSIMPLE_OAUTH_TOKEN", "123") os.Setenv("DNSIMPLE_OAUTH_TOKEN", "123")
provider, err := NewDNSProvider() provider, err := NewDNSProvider()
assert.NotNil(t, provider) assert.NotNil(t, provider)
@ -52,10 +52,10 @@ func TestNewDNSProviderValid(t *testing.T) {
} }
func TestNewDNSProviderValidWithBaseUrl(t *testing.T) { func TestNewDNSProviderValidWithBaseUrl(t *testing.T) {
defer restoreDNSimpleEnv() defer restoreEnv()
os.Setenv("DNSIMPLE_OAUTH_TOKEN", "123") os.Setenv("DNSIMPLE_OAUTH_TOKEN", "123")
os.Setenv("DNSIMPLE_BASE_URL", "https://api.dnsimple.test") os.Setenv("DNSIMPLE_BASE_URL", "https://api.dnsimple.test")
provider, err := NewDNSProvider() provider, err := NewDNSProvider()
assert.NotNil(t, provider) assert.NotNil(t, provider)
@ -65,11 +65,8 @@ func TestNewDNSProviderValidWithBaseUrl(t *testing.T) {
} }
func TestNewDNSProviderInvalidWithMissingOauthToken(t *testing.T) { func TestNewDNSProviderInvalidWithMissingOauthToken(t *testing.T) {
if dnsimpleLiveTest { defer restoreEnv()
t.Skip("skipping test in live mode") os.Setenv("DNSIMPLE_OAUTH_TOKEN", "")
}
defer restoreDNSimpleEnv()
provider, err := NewDNSProvider() provider, err := NewDNSProvider()

View file

@ -15,6 +15,7 @@ import (
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
@ -45,20 +46,19 @@ type Record struct {
// Credentials must be passed in the environment variables: DNSMADEEASY_API_KEY // Credentials must be passed in the environment variables: DNSMADEEASY_API_KEY
// and DNSMADEEASY_API_SECRET. // and DNSMADEEASY_API_SECRET.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
dnsmadeeasyAPIKey := os.Getenv("DNSMADEEASY_API_KEY") values, err := env.Get("DNSMADEEASY_API_KEY", "DNSMADEEASY_API_SECRET")
dnsmadeeasyAPISecret := os.Getenv("DNSMADEEASY_API_SECRET") if err != nil {
dnsmadeeasySandbox := os.Getenv("DNSMADEEASY_SANDBOX") return nil, fmt.Errorf("DNSMadeEasy: %v", err)
}
var baseURL string var baseURL string
if sandbox, _ := strconv.ParseBool(os.Getenv("DNSMADEEASY_SANDBOX")); sandbox {
sandbox, _ := strconv.ParseBool(dnsmadeeasySandbox)
if sandbox {
baseURL = "https://api.sandbox.dnsmadeeasy.com/V2.0" baseURL = "https://api.sandbox.dnsmadeeasy.com/V2.0"
} else { } else {
baseURL = "https://api.dnsmadeeasy.com/V2.0" baseURL = "https://api.dnsmadeeasy.com/V2.0"
} }
return NewDNSProviderCredentials(baseURL, dnsmadeeasyAPIKey, dnsmadeeasyAPISecret) return NewDNSProviderCredentials(baseURL, values["DNSMADEEASY_API_KEY"], values["DNSMADEEASY_API_SECRET"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -4,11 +4,11 @@ package dnspod
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/decker502/dnspod-go" "github.com/decker502/dnspod-go"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface. // DNSProvider is an implementation of the acme.ChallengeProvider interface.
@ -19,8 +19,12 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance configured for dnspod. // NewDNSProvider returns a DNSProvider instance configured for dnspod.
// Credentials must be passed in the environment variables: DNSPOD_API_KEY. // Credentials must be passed in the environment variables: DNSPOD_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("DNSPOD_API_KEY") values, err := env.Get("DNSPOD_API_KEY")
return NewDNSProviderCredentials(key) if err != nil {
return nil, fmt.Errorf("DNSPod: %v", err)
}
return NewDNSProviderCredentials(values["DNSPOD_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
@ -95,7 +99,7 @@ func (c *DNSProvider) getHostedZone(domain string) (string, string, error) {
} }
if hostedZone.ID == 0 { if hostedZone.ID == 0 {
return "", "", fmt.Errorf("Zone %s not found in dnspod for domain %s", authZone, domain) return "", "", fmt.Errorf("zone %s not found in dnspod for domain %s", authZone, domain)
} }

View file

@ -1,10 +1,11 @@
package dnspod package dnspod
import ( import (
"github.com/stretchr/testify/assert"
"os" "os"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
) )
var ( var (
@ -21,28 +22,31 @@ func init() {
} }
} }
func restorednspodEnv() { func restoreEnv() {
os.Setenv("DNSPOD_API_KEY", dnspodAPIKey) os.Setenv("DNSPOD_API_KEY", dnspodAPIKey)
} }
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
defer restoreEnv()
os.Setenv("DNSPOD_API_KEY", "") os.Setenv("DNSPOD_API_KEY", "")
_, err := NewDNSProviderCredentials("123") _, err := NewDNSProviderCredentials("123")
assert.NoError(t, err) assert.NoError(t, err)
restorednspodEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("DNSPOD_API_KEY", "123") os.Setenv("DNSPOD_API_KEY", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restorednspodEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("DNSPOD_API_KEY", "") os.Setenv("DNSPOD_API_KEY", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "dnspod credentials missing") assert.EqualError(t, err, "DNSPod: some credentials information are missing: DNSPOD_API_KEY")
restorednspodEnv()
} }
func TestLivednspodPresent(t *testing.T) { func TestLivednspodPresent(t *testing.T) {

View file

@ -1,4 +1,4 @@
// Package duckdns Adds lego support for http://duckdns.org . // Package duckdns Adds lego support for http://duckdns.org.
// See http://www.duckdns.org/spec.jsp for more info on updating TXT records. // See http://www.duckdns.org/spec.jsp for more info on updating TXT records.
package duckdns package duckdns
@ -6,30 +6,33 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider adds and removes the record for the DNS challenge // DNSProvider adds and removes the record for the DNS challenge
type DNSProvider struct { type DNSProvider struct {
// The duckdns api token // The api token
token string token string
} }
// NewDNSProvider returns a new DNS provider using // NewDNSProvider returns a new DNS provider using
// environment variable DUCKDNS_TOKEN for adding and removing the DNS record. // environment variable DUCKDNS_TOKEN for adding and removing the DNS record.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
duckdnsToken := os.Getenv("DUCKDNS_TOKEN") values, err := env.Get("DUCKDNS_TOKEN")
if err != nil {
return nil, fmt.Errorf("DuckDNS: %v", err)
}
return NewDNSProviderCredentials(duckdnsToken) return NewDNSProviderCredentials(values["DUCKDNS_TOKEN"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for http://duckdns.org . // DNSProvider instance configured for http://duckdns.org .
func NewDNSProviderCredentials(duckdnsToken string) (*DNSProvider, error) { func NewDNSProviderCredentials(duckdnsToken string) (*DNSProvider, error) {
if duckdnsToken == "" { if duckdnsToken == "" {
return nil, errors.New("environment variable DUCKDNS_TOKEN not set") return nil, errors.New("DuckDNS: credentials missing")
} }
return &DNSProvider{token: duckdnsToken}, nil return &DNSProvider{token: duckdnsToken}, nil

View file

@ -22,22 +22,26 @@ func init() {
} }
} }
func restoreDuckdnsEnv() { func restoreEnv() {
os.Setenv("DUCKDNS_TOKEN", duckdnsToken) os.Setenv("DUCKDNS_TOKEN", duckdnsToken)
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("DUCKDNS_TOKEN", "123") os.Setenv("DUCKDNS_TOKEN", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreDuckdnsEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("DUCKDNS_TOKEN", "") os.Setenv("DUCKDNS_TOKEN", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "environment variable DUCKDNS_TOKEN not set") assert.EqualError(t, err, "DuckDNS: some credentials information are missing: DUCKDNS_TOKEN")
restoreDuckdnsEnv()
} }
func TestLiveDuckdnsPresent(t *testing.T) { func TestLiveDuckdnsPresent(t *testing.T) {
if !duckdnsLiveTest { if !duckdnsLiveTest {
t.Skip("skipping live test") t.Skip("skipping live test")

View file

@ -7,11 +7,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strconv" "strconv"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
var dynBaseURL = "https://api.dynect.net/REST" var dynBaseURL = "https://api.dynect.net/REST"
@ -43,10 +43,12 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: DYN_CUSTOMER_NAME, // Credentials must be passed in the environment variables: DYN_CUSTOMER_NAME,
// DYN_USER_NAME and DYN_PASSWORD. // DYN_USER_NAME and DYN_PASSWORD.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
customerName := os.Getenv("DYN_CUSTOMER_NAME") values, err := env.Get("DYN_CUSTOMER_NAME", "DYN_USER_NAME", "DYN_PASSWORD")
userName := os.Getenv("DYN_USER_NAME") if err != nil {
password := os.Getenv("DYN_PASSWORD") return nil, fmt.Errorf("DynDNS: %v", err)
return NewDNSProviderCredentials(customerName, userName, password) }
return NewDNSProviderCredentials(values["DYN_CUSTOMER_NAME"], values["DYN_USER_NAME"], values["DYN_PASSWORD"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -9,6 +9,7 @@ import (
"github.com/exoscale/egoscale" "github.com/exoscale/egoscale"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface. // DNSProvider is an implementation of the acme.ChallengeProvider interface.
@ -19,10 +20,13 @@ type DNSProvider struct {
// NewDNSProvider Credentials must be passed in the environment variables: // NewDNSProvider Credentials must be passed in the environment variables:
// EXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT. // EXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("EXOSCALE_API_KEY") values, err := env.Get("EXOSCALE_API_KEY", "EXOSCALE_API_SECRET")
secret := os.Getenv("EXOSCALE_API_SECRET") if err != nil {
return nil, fmt.Errorf("Exoscale: %v", err)
}
endpoint := os.Getenv("EXOSCALE_ENDPOINT") endpoint := os.Getenv("EXOSCALE_ENDPOINT")
return NewDNSProviderClient(key, secret, endpoint) return NewDNSProviderClient(values["EXOSCALE_API_KEY"], values["EXOSCALE_API_SECRET"], endpoint)
} }
// NewDNSProviderClient Uses the supplied parameters to return a DNSProvider instance // NewDNSProviderClient Uses the supplied parameters to return a DNSProvider instance
@ -31,6 +35,7 @@ func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) {
if key == "" || secret == "" { if key == "" || secret == "" {
return nil, fmt.Errorf("Exoscale credentials missing") return nil, fmt.Errorf("Exoscale credentials missing")
} }
if endpoint == "" { if endpoint == "" {
endpoint = "https://api.exoscale.ch/dns" endpoint = "https://api.exoscale.ch/dns"
} }

View file

@ -24,32 +24,36 @@ func init() {
} }
} }
func restoreExoscaleEnv() { func restoreEnv() {
os.Setenv("EXOSCALE_API_KEY", exoscaleAPIKey) os.Setenv("EXOSCALE_API_KEY", exoscaleAPIKey)
os.Setenv("EXOSCALE_API_SECRET", exoscaleAPISecret) os.Setenv("EXOSCALE_API_SECRET", exoscaleAPISecret)
} }
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
defer restoreEnv()
os.Setenv("EXOSCALE_API_KEY", "") os.Setenv("EXOSCALE_API_KEY", "")
os.Setenv("EXOSCALE_API_SECRET", "") os.Setenv("EXOSCALE_API_SECRET", "")
_, err := NewDNSProviderClient("example@example.com", "123", "") _, err := NewDNSProviderClient("example@example.com", "123", "")
assert.NoError(t, err) assert.NoError(t, err)
restoreExoscaleEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("EXOSCALE_API_KEY", "example@example.com") os.Setenv("EXOSCALE_API_KEY", "example@example.com")
os.Setenv("EXOSCALE_API_SECRET", "123") os.Setenv("EXOSCALE_API_SECRET", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreExoscaleEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("EXOSCALE_API_KEY", "") os.Setenv("EXOSCALE_API_KEY", "")
os.Setenv("EXOSCALE_API_SECRET", "") os.Setenv("EXOSCALE_API_SECRET", "")
defer restoreEnv()
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "Exoscale credentials missing") assert.EqualError(t, err, "Exoscale: some credentials information are missing: EXOSCALE_API_KEY,EXOSCALE_API_SECRET")
restoreExoscaleEnv()
} }
func TestExtractRootRecordName(t *testing.T) { func TestExtractRootRecordName(t *testing.T) {

View file

@ -2,12 +2,12 @@ package fastdns
import ( import (
"fmt" "fmt"
"os"
"reflect" "reflect"
configdns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1" configdns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid" "github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface. // DNSProvider is an implementation of the acme.ChallengeProvider interface.
@ -18,19 +18,24 @@ type DNSProvider struct {
// NewDNSProvider uses the supplied environment variables to return a DNSProvider instance: // NewDNSProvider uses the supplied environment variables to return a DNSProvider instance:
// AKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET, AKAMAI_ACCESS_TOKEN // AKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET, AKAMAI_ACCESS_TOKEN
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
host := os.Getenv("AKAMAI_HOST") values, err := env.Get("AKAMAI_HOST", "AKAMAI_CLIENT_TOKEN", "AKAMAI_CLIENT_SECRET", "AKAMAI_ACCESS_TOKEN")
clientToken := os.Getenv("AKAMAI_CLIENT_TOKEN") if err != nil {
clientSecret := os.Getenv("AKAMAI_CLIENT_SECRET") return nil, fmt.Errorf("FastDNS: %v", err)
accessToken := os.Getenv("AKAMAI_ACCESS_TOKEN") }
return NewDNSProviderClient(host, clientToken, clientSecret, accessToken) return NewDNSProviderClient(
values["AKAMAI_HOST"],
values["AKAMAI_CLIENT_TOKEN"],
values["AKAMAI_CLIENT_SECRET"],
values["AKAMAI_ACCESS_TOKEN"],
)
} }
// NewDNSProviderClient uses the supplied parameters to return a DNSProvider instance // NewDNSProviderClient uses the supplied parameters to return a DNSProvider instance
// configured for FastDNS. // configured for FastDNS.
func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (*DNSProvider, error) { func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (*DNSProvider, error) {
if clientToken == "" || clientSecret == "" || accessToken == "" || host == "" { if clientToken == "" || clientSecret == "" || accessToken == "" || host == "" {
return nil, fmt.Errorf("Akamai FastDNS credentials missing") return nil, fmt.Errorf("FastDNS credentials are missing")
} }
config := edgegrid.Config{ config := edgegrid.Config{
Host: host, Host: host,

View file

@ -29,7 +29,7 @@ func init() {
} }
} }
func restoreFastdnsEnv() { func restoreEnv() {
os.Setenv("AKAMAI_HOST", host) os.Setenv("AKAMAI_HOST", host)
os.Setenv("AKAMAI_CLIENT_TOKEN", clientToken) os.Setenv("AKAMAI_CLIENT_TOKEN", clientToken)
os.Setenv("AKAMAI_CLIENT_SECRET", clientSecret) os.Setenv("AKAMAI_CLIENT_SECRET", clientSecret)
@ -37,33 +37,36 @@ func restoreFastdnsEnv() {
} }
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
defer restoreEnv()
os.Setenv("AKAMAI_HOST", "") os.Setenv("AKAMAI_HOST", "")
os.Setenv("AKAMAI_CLIENT_TOKEN", "") os.Setenv("AKAMAI_CLIENT_TOKEN", "")
os.Setenv("AKAMAI_CLIENT_SECRET", "") os.Setenv("AKAMAI_CLIENT_SECRET", "")
os.Setenv("AKAMAI_ACCESS_TOKEN", "") os.Setenv("AKAMAI_ACCESS_TOKEN", "")
_, err := NewDNSProviderClient("somehost", "someclienttoken", "someclientsecret", "someaccesstoken") _, err := NewDNSProviderClient("somehost", "someclienttoken", "someclientsecret", "someaccesstoken")
assert.NoError(t, err) assert.NoError(t, err)
restoreFastdnsEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("AKAMAI_HOST", "somehost") os.Setenv("AKAMAI_HOST", "somehost")
os.Setenv("AKAMAI_CLIENT_TOKEN", "someclienttoken") os.Setenv("AKAMAI_CLIENT_TOKEN", "someclienttoken")
os.Setenv("AKAMAI_CLIENT_SECRET", "someclientsecret") os.Setenv("AKAMAI_CLIENT_SECRET", "someclientsecret")
os.Setenv("AKAMAI_ACCESS_TOKEN", "someaccesstoken") os.Setenv("AKAMAI_ACCESS_TOKEN", "someaccesstoken")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreFastdnsEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("AKAMAI_HOST", "") os.Setenv("AKAMAI_HOST", "")
os.Setenv("AKAMAI_CLIENT_TOKEN", "") os.Setenv("AKAMAI_CLIENT_TOKEN", "")
os.Setenv("AKAMAI_CLIENT_SECRET", "") os.Setenv("AKAMAI_CLIENT_SECRET", "")
os.Setenv("AKAMAI_ACCESS_TOKEN", "") os.Setenv("AKAMAI_ACCESS_TOKEN", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "Akamai FastDNS credentials missing") assert.EqualError(t, err, "FastDNS: some credentials information are missing: AKAMAI_HOST,AKAMAI_CLIENT_TOKEN,AKAMAI_CLIENT_SECRET,AKAMAI_ACCESS_TOKEN")
restoreFastdnsEnv()
} }
func TestLiveFastdnsPresent(t *testing.T) { func TestLiveFastdnsPresent(t *testing.T) {

View file

@ -9,12 +9,12 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// Gandi API reference: http://doc.rpc.gandi.net/index.html // Gandi API reference: http://doc.rpc.gandi.net/index.html
@ -49,8 +49,12 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance configured for Gandi. // NewDNSProvider returns a DNSProvider instance configured for Gandi.
// Credentials must be passed in the environment variable: GANDI_API_KEY. // Credentials must be passed in the environment variable: GANDI_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiKey := os.Getenv("GANDI_API_KEY") values, err := env.Get("GANDI_API_KEY")
return NewDNSProviderCredentials(apiKey) if err != nil {
return nil, fmt.Errorf("GandiDNS: %v", err)
}
return NewDNSProviderCredentials(values["GANDI_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -8,6 +8,8 @@ import (
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/require"
) )
// TestDNSProvider runs Present and CleanUp against a fake Gandi RPC // TestDNSProvider runs Present and CleanUp against a fake Gandi RPC
@ -15,55 +17,49 @@ import (
func TestDNSProvider(t *testing.T) { func TestDNSProvider(t *testing.T) {
fakeAPIKey := "123412341234123412341234" fakeAPIKey := "123412341234123412341234"
fakeKeyAuth := "XXXX" fakeKeyAuth := "XXXX"
provider, err := NewDNSProviderCredentials(fakeAPIKey) provider, err := NewDNSProviderCredentials(fakeAPIKey)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
regexpDate, err := regexp.Compile(`\[ACME Challenge [^\]:]*:[^\]]*\]`) regexpDate, err := regexp.Compile(`\[ACME Challenge [^\]:]*:[^\]]*\]`)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
// start fake RPC server // start fake RPC server
fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-Type") != "text/xml" { require.Equal(t, "text/xml", r.Header.Get("Content-Type"), "invalid content type")
t.Fatalf("Content-Type: text/xml header not found")
}
req, err := ioutil.ReadAll(r.Body) req, err := ioutil.ReadAll(r.Body)
if err != nil { require.NoError(t, err)
t.Fatal(err)
} req = regexpDate.ReplaceAllLiteral(req, []byte(`[ACME Challenge 01 Jan 16 00:00 +0000]`))
req = regexpDate.ReplaceAllLiteral(
req, []byte(`[ACME Challenge 01 Jan 16 00:00 +0000]`))
resp, ok := serverResponses[string(req)] resp, ok := serverResponses[string(req)]
if !ok { require.True(t, ok, "Server response for request not found")
t.Fatalf("Server response for request not found")
}
_, err = io.Copy(w, strings.NewReader(resp)) _, err = io.Copy(w, strings.NewReader(resp))
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
})) }))
defer fakeServer.Close() defer fakeServer.Close()
// define function to override findZoneByFqdn with // define function to override findZoneByFqdn with
fakeFindZoneByFqdn := func(fqdn string, nameserver []string) (string, error) { fakeFindZoneByFqdn := func(fqdn string, nameserver []string) (string, error) {
return "example.com.", nil return "example.com.", nil
} }
// override gandi endpoint and findZoneByFqdn function // override gandi endpoint and findZoneByFqdn function
savedEndpoint, savedFindZoneByFqdn := endpoint, findZoneByFqdn savedEndpoint, savedFindZoneByFqdn := endpoint, findZoneByFqdn
defer func() { defer func() {
endpoint, findZoneByFqdn = savedEndpoint, savedFindZoneByFqdn endpoint, findZoneByFqdn = savedEndpoint, savedFindZoneByFqdn
}() }()
endpoint, findZoneByFqdn = fakeServer.URL+"/", fakeFindZoneByFqdn endpoint, findZoneByFqdn = fakeServer.URL+"/", fakeFindZoneByFqdn
// run Present // run Present
err = provider.Present("abc.def.example.com", "", fakeKeyAuth) err = provider.Present("abc.def.example.com", "", fakeKeyAuth)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
// run CleanUp // run CleanUp
err = provider.CleanUp("abc.def.example.com", "", fakeKeyAuth) err = provider.CleanUp("abc.def.example.com", "", fakeKeyAuth)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
} }
// serverResponses is the XML-RPC Request->Response map used by the // serverResponses is the XML-RPC Request->Response map used by the

View file

@ -7,12 +7,12 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// Gandi API reference: http://doc.livedns.gandi.net/ // Gandi API reference: http://doc.livedns.gandi.net/
@ -45,8 +45,12 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance configured for Gandi. // NewDNSProvider returns a DNSProvider instance configured for Gandi.
// Credentials must be passed in the environment variable: GANDIV5_API_KEY. // Credentials must be passed in the environment variable: GANDIV5_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiKey := os.Getenv("GANDIV5_API_KEY") values, err := env.Get("GANDIV5_API_KEY")
return NewDNSProviderCredentials(apiKey) if err != nil {
return nil, fmt.Errorf("GandiDNS: %v", err)
}
return NewDNSProviderCredentials(values["GANDIV5_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -8,6 +8,8 @@ import (
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/require"
) )
// TestDNSProvider runs Present and CleanUp against a fake Gandi RPC // TestDNSProvider runs Present and CleanUp against a fake Gandi RPC
@ -15,55 +17,50 @@ import (
func TestDNSProvider(t *testing.T) { func TestDNSProvider(t *testing.T) {
fakeAPIKey := "123412341234123412341234" fakeAPIKey := "123412341234123412341234"
fakeKeyAuth := "XXXX" fakeKeyAuth := "XXXX"
provider, err := NewDNSProviderCredentials(fakeAPIKey) provider, err := NewDNSProviderCredentials(fakeAPIKey)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
regexpToken, err := regexp.Compile(`"rrset_values":\[".+"\]`) regexpToken, err := regexp.Compile(`"rrset_values":\[".+"\]`)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
// start fake RPC server // start fake RPC server
fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-Type") != "application/json" { require.Equal(t, "application/json", r.Header.Get("Content-Type"), "invalid content type")
t.Fatalf("Content-Type: application/json header not found")
}
req, err := ioutil.ReadAll(r.Body) req, err := ioutil.ReadAll(r.Body)
if err != nil { require.NoError(t, err)
t.Fatal(err)
} req = regexpToken.ReplaceAllLiteral(req, []byte(`"rrset_values":["TOKEN"]`))
req = regexpToken.ReplaceAllLiteral(
req, []byte(`"rrset_values":["TOKEN"]`))
resp, ok := serverResponses[string(req)] resp, ok := serverResponses[string(req)]
if !ok { require.True(t, ok, "Server response for request not found")
t.Fatalf("Server response for request not found")
}
_, err = io.Copy(w, strings.NewReader(resp)) _, err = io.Copy(w, strings.NewReader(resp))
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
})) }))
defer fakeServer.Close() defer fakeServer.Close()
// define function to override findZoneByFqdn with // define function to override findZoneByFqdn with
fakeFindZoneByFqdn := func(fqdn string, nameserver []string) (string, error) { fakeFindZoneByFqdn := func(fqdn string, nameserver []string) (string, error) {
return "example.com.", nil return "example.com.", nil
} }
// override gandi endpoint and findZoneByFqdn function // override gandi endpoint and findZoneByFqdn function
savedEndpoint, savedFindZoneByFqdn := endpoint, findZoneByFqdn savedEndpoint, savedFindZoneByFqdn := endpoint, findZoneByFqdn
defer func() { defer func() {
endpoint, findZoneByFqdn = savedEndpoint, savedFindZoneByFqdn endpoint, findZoneByFqdn = savedEndpoint, savedFindZoneByFqdn
}() }()
endpoint, findZoneByFqdn = fakeServer.URL, fakeFindZoneByFqdn endpoint, findZoneByFqdn = fakeServer.URL, fakeFindZoneByFqdn
// run Present // run Present
err = provider.Present("abc.def.example.com", "", fakeKeyAuth) err = provider.Present("abc.def.example.com", "", fakeKeyAuth)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
// run CleanUp // run CleanUp
err = provider.CleanUp("abc.def.example.com", "", fakeKeyAuth) err = provider.CleanUp("abc.def.example.com", "", fakeKeyAuth)
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
} }
// serverResponses is the JSON Request->Response map used by the // serverResponses is the JSON Request->Response map used by the

View file

@ -31,6 +31,7 @@ func NewDNSProvider() (*DNSProvider, error) {
if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok { if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok {
return NewDNSProviderServiceAccount(saFile) return NewDNSProviderServiceAccount(saFile)
} }
project := os.Getenv("GCE_PROJECT") project := os.Getenv("GCE_PROJECT")
return NewDNSProviderCredentials(project) return NewDNSProviderCredentials(project)
} }
@ -44,11 +45,11 @@ func NewDNSProviderCredentials(project string) (*DNSProvider, error) {
client, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope) client, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope)
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to get Google Cloud client: %v", err) return nil, fmt.Errorf("unable to get Google Cloud client: %v", err)
} }
svc, err := dns.New(client) svc, err := dns.New(client)
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err) return nil, fmt.Errorf("unable to create Google Cloud DNS service: %v", err)
} }
return &DNSProvider{ return &DNSProvider{
project: project, project: project,
@ -65,7 +66,7 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) {
dat, err := ioutil.ReadFile(saFile) dat, err := ioutil.ReadFile(saFile)
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to read Service Account file: %v", err) return nil, fmt.Errorf("unable to read Service Account file: %v", err)
} }
// read project id from service account file // read project id from service account file
@ -74,19 +75,19 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) {
} }
err = json.Unmarshal(dat, &datJSON) err = json.Unmarshal(dat, &datJSON)
if err != nil || datJSON.ProjectID == "" { if err != nil || datJSON.ProjectID == "" {
return nil, fmt.Errorf("Project ID not found in Google Cloud Service Account file") return nil, fmt.Errorf("project ID not found in Google Cloud Service Account file")
} }
project := datJSON.ProjectID project := datJSON.ProjectID
conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope) conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope)
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to acquire config: %v", err) return nil, fmt.Errorf("unable to acquire config: %v", err)
} }
client := conf.Client(context.Background()) client := conf.Client(context.Background())
svc, err := dns.New(client) svc, err := dns.New(client)
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to create Google Cloud DNS service: %v", err) return nil, fmt.Errorf("unable to create Google Cloud DNS service: %v", err)
} }
return &DNSProvider{ return &DNSProvider{
project: project, project: project,
@ -189,7 +190,7 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) {
} }
if len(zones.ManagedZones) == 0 { if len(zones.ManagedZones) == 0 {
return "", fmt.Errorf("No matching GoogleCloud domain found for domain %s", authZone) return "", fmt.Errorf("no matching GoogleCloud domain found for domain %s", authZone)
} }
return zones.ManagedZones[0].Name, nil return zones.ManagedZones[0].Name, nil
@ -202,7 +203,7 @@ func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSe
return nil, err return nil, err
} }
found := []*dns.ResourceRecordSet{} var found []*dns.ResourceRecordSet
for _, r := range recs.Rrsets { for _, r := range recs.Rrsets {
if r.Type == "TXT" && r.Name == fqdn { if r.Type == "TXT" && r.Name == fqdn {
found = append(found, r) found = append(found, r)

View file

@ -27,7 +27,7 @@ func init() {
} }
} }
func restoreGCloudEnv() { func restoreEnv() {
os.Setenv("GCE_PROJECT", gcloudProject) os.Setenv("GCE_PROJECT", gcloudProject)
} }
@ -35,27 +35,32 @@ func TestNewDNSProviderValid(t *testing.T) {
if !gcloudLiveTest { if !gcloudLiveTest {
t.Skip("skipping live test (requires credentials)") t.Skip("skipping live test (requires credentials)")
} }
defer restoreEnv()
os.Setenv("GCE_PROJECT", "") os.Setenv("GCE_PROJECT", "")
_, err := NewDNSProviderCredentials("my-project") _, err := NewDNSProviderCredentials("my-project")
assert.NoError(t, err) assert.NoError(t, err)
restoreGCloudEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
if !gcloudLiveTest { if !gcloudLiveTest {
t.Skip("skipping live test (requires credentials)") t.Skip("skipping live test (requires credentials)")
} }
defer restoreEnv()
os.Setenv("GCE_PROJECT", "my-project") os.Setenv("GCE_PROJECT", "my-project")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restoreGCloudEnv()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("GCE_PROJECT", "") os.Setenv("GCE_PROJECT", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "Google Cloud project name missing") assert.EqualError(t, err, "Google Cloud project name missing")
restoreGCloudEnv()
} }
func TestLiveGoogleCloudPresent(t *testing.T) { func TestLiveGoogleCloudPresent(t *testing.T) {

View file

@ -7,13 +7,13 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/log" "github.com/xenolf/lego/log"
"github.com/xenolf/lego/platform/config/env"
) )
// GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation // GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation
@ -35,9 +35,12 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: GLESYS_API_USER // Credentials must be passed in the environment variables: GLESYS_API_USER
// and GLESYS_API_KEY. // and GLESYS_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiUser := os.Getenv("GLESYS_API_USER") values, err := env.Get("GLESYS_API_USER", "GLESYS_API_KEY")
apiKey := os.Getenv("GLESYS_API_KEY") if err != nil {
return NewDNSProviderCredentials(apiUser, apiKey) return nil, fmt.Errorf("GleSYS DNS: %v", err)
}
return NewDNSProviderCredentials(values["GLESYS_API_USER"], values["GLESYS_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -2,18 +2,17 @@
package godaddy package godaddy
import ( import (
"fmt"
"io"
"net/http"
"os"
"time"
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http"
"strings" "strings"
"time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// GoDaddyAPIURL represents the API endpoint to call. // GoDaddyAPIURL represents the API endpoint to call.
@ -29,9 +28,12 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: GODADDY_API_KEY // Credentials must be passed in the environment variables: GODADDY_API_KEY
// and GODADDY_API_SECRET. // and GODADDY_API_SECRET.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apikey := os.Getenv("GODADDY_API_KEY") values, err := env.Get("GODADDY_API_KEY", "GODADDY_API_SECRET")
secret := os.Getenv("GODADDY_API_SECRET") if err != nil {
return NewDNSProviderCredentials(apikey, secret) return nil, fmt.Errorf("GoDaddy: %v", err)
}
return NewDNSProviderCredentials(values["GODADDY_API_KEY"], values["GODADDY_API_SECRET"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -8,40 +8,40 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/lightsail" "github.com/aws/aws-sdk-go/service/lightsail"
"github.com/stretchr/testify/require"
) )
func TestLightsailTTL(t *testing.T) { func TestLightsailTTL(t *testing.T) {
m, err := testGetAndPreCheck() m, err := testGetAndPreCheck()
if err != nil { if err != nil {
t.Skip(err.Error()) t.Skip(err.Error())
} }
provider, err := NewDNSProvider() provider, err := NewDNSProvider()
if err != nil { require.NoError(t, err)
t.Fatalf("Fatal: %s", err.Error())
}
err = provider.Present(m["lightsailDomain"], "foo", "bar") err = provider.Present(m["lightsailDomain"], "foo", "bar")
if err != nil { require.NoError(t, err)
t.Fatalf("Fatal: %s", err.Error())
}
// we need a separate Lightshail client here as the one in the DNS provider is // we need a separate Lightshail client here as the one in the DNS provider is
// unexported. // unexported.
fqdn := "_acme-challenge." + m["lightsailDomain"] fqdn := "_acme-challenge." + m["lightsailDomain"]
svc := lightsail.New(session.New()) svc := lightsail.New(session.New())
if err != nil { if err != nil {
provider.CleanUp(m["lightsailDomain"], "foo", "bar") provider.CleanUp(m["lightsailDomain"], "foo", "bar")
t.Fatalf("Fatal: %s", err.Error()) t.Fatal(err)
} }
params := &lightsail.GetDomainInput{ params := &lightsail.GetDomainInput{
DomainName: aws.String(m["lightsailDomain"]), DomainName: aws.String(m["lightsailDomain"]),
} }
resp, err := svc.GetDomain(params) resp, err := svc.GetDomain(params)
if err != nil { if err != nil {
provider.CleanUp(m["lightsailDomain"], "foo", "bar") provider.CleanUp(m["lightsailDomain"], "foo", "bar")
t.Fatalf("Fatal: %s", err.Error()) t.Fatal(err)
} }
entries := resp.Domain.DomainEntries entries := resp.Domain.DomainEntries
for _, entry := range entries { for _, entry := range entries {
if *entry.Type == "TXT" && *entry.Name == fqdn { if *entry.Type == "TXT" && *entry.Name == fqdn {
@ -49,6 +49,7 @@ func TestLightsailTTL(t *testing.T) {
return return
} }
} }
provider.CleanUp(m["lightsailDomain"], "foo", "bar") provider.CleanUp(m["lightsailDomain"], "foo", "bar")
t.Fatalf("Could not find a TXT record for _acme-challenge.%s", m["lightsailDomain"]) t.Fatalf("Could not find a TXT record for _acme-challenge.%s", m["lightsailDomain"])
} }

View file

@ -23,7 +23,7 @@ func init() {
lightsailSecret = os.Getenv("AWS_SECRET_ACCESS_KEY") lightsailSecret = os.Getenv("AWS_SECRET_ACCESS_KEY")
} }
func restoreLightsailEnv() { func restoreEnv() {
os.Setenv("AWS_ACCESS_KEY_ID", lightsailKey) os.Setenv("AWS_ACCESS_KEY_ID", lightsailKey)
os.Setenv("AWS_SECRET_ACCESS_KEY", lightsailSecret) os.Setenv("AWS_SECRET_ACCESS_KEY", lightsailSecret)
os.Setenv("AWS_REGION", "us-east-1") os.Setenv("AWS_REGION", "us-east-1")
@ -43,6 +43,7 @@ func makeLightsailProvider(ts *httptest.Server) *DNSProvider {
} }
func TestCredentialsFromEnv(t *testing.T) { func TestCredentialsFromEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("AWS_ACCESS_KEY_ID", "123") os.Setenv("AWS_ACCESS_KEY_ID", "123")
os.Setenv("AWS_SECRET_ACCESS_KEY", "123") os.Setenv("AWS_SECRET_ACCESS_KEY", "123")
os.Setenv("AWS_REGION", "us-east-1") os.Setenv("AWS_REGION", "us-east-1")
@ -54,8 +55,6 @@ func TestCredentialsFromEnv(t *testing.T) {
sess := session.New(config) sess := session.New(config)
_, err := sess.Config.Credentials.Get() _, err := sess.Config.Credentials.Get()
assert.NoError(t, err, "Expected credentials to be set from environment") assert.NoError(t, err, "Expected credentials to be set from environment")
restoreLightsailEnv()
} }
func TestLightsailPresent(t *testing.T) { func TestLightsailPresent(t *testing.T) {

View file

@ -4,12 +4,13 @@ package linode
import ( import (
"errors" "errors"
"os" "fmt"
"strings" "strings"
"time" "time"
"github.com/timewasted/linode/dns" "github.com/timewasted/linode/dns"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
const ( const (
@ -31,8 +32,12 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance configured for Linode. // NewDNSProvider returns a DNSProvider instance configured for Linode.
// Credentials must be passed in the environment variable: LINODE_API_KEY. // Credentials must be passed in the environment variable: LINODE_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiKey := os.Getenv("LINODE_API_KEY") values, err := env.Get("LINODE_API_KEY")
return NewDNSProviderCredentials(apiKey) if err != nil {
return nil, fmt.Errorf("Linode: %v", err)
}
return NewDNSProviderCredentials(values["LINODE_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -48,8 +48,7 @@ func newMockServer(t *testing.T, responses MockResponseMap) *httptest.Server {
action := r.URL.Query().Get("api_action") action := r.URL.Query().Get("api_action")
resp, ok := responses[action] resp, ok := responses[action]
if !ok { if !ok {
msg := fmt.Sprintf("Unsupported mock action: %s", action) require.FailNowf(t, "Unsupported mock action: %s", action)
require.FailNow(t, msg)
} }
// Build the response that the server will return. // Build the response that the server will return.
@ -75,17 +74,19 @@ func newMockServer(t *testing.T, responses MockResponseMap) *httptest.Server {
} }
func TestNewDNSProviderWithEnv(t *testing.T) { func TestNewDNSProviderWithEnv(t *testing.T) {
os.Setenv("LINODE_API_KEY", "testing")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "testing")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestNewDNSProviderWithoutEnv(t *testing.T) { func TestNewDNSProviderWithoutEnv(t *testing.T) {
os.Setenv("LINODE_API_KEY", "")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "Linode credentials missing") assert.EqualError(t, err, "Linode: some credentials information are missing: LINODE_API_KEY")
} }
func TestNewDNSProviderCredentialsWithKey(t *testing.T) { func TestNewDNSProviderCredentialsWithKey(t *testing.T) {
@ -99,8 +100,9 @@ func TestNewDNSProviderCredentialsWithoutKey(t *testing.T) {
} }
func TestDNSProvider_Present(t *testing.T) { func TestDNSProvider_Present(t *testing.T) {
os.Setenv("LINODE_API_KEY", "testing")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "testing")
p, err := NewDNSProvider() p, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
@ -109,7 +111,7 @@ func TestDNSProvider_Present(t *testing.T) {
mockResponses := MockResponseMap{ mockResponses := MockResponseMap{
"domain.list": MockResponse{ "domain.list": MockResponse{
Response: []dns.Domain{ Response: []dns.Domain{
dns.Domain{ {
Domain: domain, Domain: domain,
DomainID: 1234, DomainID: 1234,
}, },
@ -121,8 +123,10 @@ func TestDNSProvider_Present(t *testing.T) {
}, },
}, },
} }
mockSrv := newMockServer(t, mockResponses) mockSrv := newMockServer(t, mockResponses)
defer mockSrv.Close() defer mockSrv.Close()
p.linode.ToLinode().SetEndpoint(mockSrv.URL) p.linode.ToLinode().SetEndpoint(mockSrv.URL)
err = p.Present(domain, "", keyAuth) err = p.Present(domain, "", keyAuth)
@ -130,8 +134,9 @@ func TestDNSProvider_Present(t *testing.T) {
} }
func TestDNSProvider_PresentNoDomain(t *testing.T) { func TestDNSProvider_PresentNoDomain(t *testing.T) {
os.Setenv("LINODE_API_KEY", "testing")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "testing")
p, err := NewDNSProvider() p, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
@ -140,15 +145,17 @@ func TestDNSProvider_PresentNoDomain(t *testing.T) {
mockResponses := MockResponseMap{ mockResponses := MockResponseMap{
"domain.list": MockResponse{ "domain.list": MockResponse{
Response: []dns.Domain{ Response: []dns.Domain{
dns.Domain{ {
Domain: "foobar.com", Domain: "foobar.com",
DomainID: 1234, DomainID: 1234,
}, },
}, },
}, },
} }
mockSrv := newMockServer(t, mockResponses) mockSrv := newMockServer(t, mockResponses)
defer mockSrv.Close() defer mockSrv.Close()
p.linode.ToLinode().SetEndpoint(mockSrv.URL) p.linode.ToLinode().SetEndpoint(mockSrv.URL)
err = p.Present(domain, "", keyAuth) err = p.Present(domain, "", keyAuth)
@ -156,8 +163,9 @@ func TestDNSProvider_PresentNoDomain(t *testing.T) {
} }
func TestDNSProvider_PresentCreateFailed(t *testing.T) { func TestDNSProvider_PresentCreateFailed(t *testing.T) {
os.Setenv("LINODE_API_KEY", "testing")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "testing")
p, err := NewDNSProvider() p, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
@ -166,7 +174,7 @@ func TestDNSProvider_PresentCreateFailed(t *testing.T) {
mockResponses := MockResponseMap{ mockResponses := MockResponseMap{
"domain.list": MockResponse{ "domain.list": MockResponse{
Response: []dns.Domain{ Response: []dns.Domain{
dns.Domain{ {
Domain: domain, Domain: domain,
DomainID: 1234, DomainID: 1234,
}, },
@ -175,7 +183,7 @@ func TestDNSProvider_PresentCreateFailed(t *testing.T) {
"domain.resource.create": MockResponse{ "domain.resource.create": MockResponse{
Response: nil, Response: nil,
Errors: []linode.ResponseError{ Errors: []linode.ResponseError{
linode.ResponseError{ {
Code: 1234, Code: 1234,
Message: "Failed to create domain resource", Message: "Failed to create domain resource",
}, },
@ -184,6 +192,7 @@ func TestDNSProvider_PresentCreateFailed(t *testing.T) {
} }
mockSrv := newMockServer(t, mockResponses) mockSrv := newMockServer(t, mockResponses)
defer mockSrv.Close() defer mockSrv.Close()
p.linode.ToLinode().SetEndpoint(mockSrv.URL) p.linode.ToLinode().SetEndpoint(mockSrv.URL)
err = p.Present(domain, "", keyAuth) err = p.Present(domain, "", keyAuth)
@ -197,8 +206,9 @@ func TestDNSProvider_PresentLive(t *testing.T) {
} }
func TestDNSProvider_CleanUp(t *testing.T) { func TestDNSProvider_CleanUp(t *testing.T) {
os.Setenv("LINODE_API_KEY", "testing")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "testing")
p, err := NewDNSProvider() p, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
@ -207,7 +217,7 @@ func TestDNSProvider_CleanUp(t *testing.T) {
mockResponses := MockResponseMap{ mockResponses := MockResponseMap{
"domain.list": MockResponse{ "domain.list": MockResponse{
Response: []dns.Domain{ Response: []dns.Domain{
dns.Domain{ {
Domain: domain, Domain: domain,
DomainID: 1234, DomainID: 1234,
}, },
@ -215,7 +225,7 @@ func TestDNSProvider_CleanUp(t *testing.T) {
}, },
"domain.resource.list": MockResponse{ "domain.resource.list": MockResponse{
Response: []dns.Resource{ Response: []dns.Resource{
dns.Resource{ {
DomainID: 1234, DomainID: 1234,
Name: "_acme-challenge", Name: "_acme-challenge",
ResourceID: 1234, ResourceID: 1234,
@ -230,8 +240,10 @@ func TestDNSProvider_CleanUp(t *testing.T) {
}, },
}, },
} }
mockSrv := newMockServer(t, mockResponses) mockSrv := newMockServer(t, mockResponses)
defer mockSrv.Close() defer mockSrv.Close()
p.linode.ToLinode().SetEndpoint(mockSrv.URL) p.linode.ToLinode().SetEndpoint(mockSrv.URL)
err = p.CleanUp(domain, "", keyAuth) err = p.CleanUp(domain, "", keyAuth)
@ -239,8 +251,9 @@ func TestDNSProvider_CleanUp(t *testing.T) {
} }
func TestDNSProvider_CleanUpNoDomain(t *testing.T) { func TestDNSProvider_CleanUpNoDomain(t *testing.T) {
os.Setenv("LINODE_API_KEY", "testing")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "testing")
p, err := NewDNSProvider() p, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
@ -249,15 +262,17 @@ func TestDNSProvider_CleanUpNoDomain(t *testing.T) {
mockResponses := MockResponseMap{ mockResponses := MockResponseMap{
"domain.list": MockResponse{ "domain.list": MockResponse{
Response: []dns.Domain{ Response: []dns.Domain{
dns.Domain{ {
Domain: "foobar.com", Domain: "foobar.com",
DomainID: 1234, DomainID: 1234,
}, },
}, },
}, },
} }
mockSrv := newMockServer(t, mockResponses) mockSrv := newMockServer(t, mockResponses)
defer mockSrv.Close() defer mockSrv.Close()
p.linode.ToLinode().SetEndpoint(mockSrv.URL) p.linode.ToLinode().SetEndpoint(mockSrv.URL)
err = p.CleanUp(domain, "", keyAuth) err = p.CleanUp(domain, "", keyAuth)
@ -265,8 +280,9 @@ func TestDNSProvider_CleanUpNoDomain(t *testing.T) {
} }
func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) { func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) {
os.Setenv("LINODE_API_KEY", "testing")
defer restoreEnv() defer restoreEnv()
os.Setenv("LINODE_API_KEY", "testing")
p, err := NewDNSProvider() p, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
@ -275,7 +291,7 @@ func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) {
mockResponses := MockResponseMap{ mockResponses := MockResponseMap{
"domain.list": MockResponse{ "domain.list": MockResponse{
Response: []dns.Domain{ Response: []dns.Domain{
dns.Domain{ {
Domain: domain, Domain: domain,
DomainID: 1234, DomainID: 1234,
}, },
@ -283,7 +299,7 @@ func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) {
}, },
"domain.resource.list": MockResponse{ "domain.resource.list": MockResponse{
Response: []dns.Resource{ Response: []dns.Resource{
dns.Resource{ {
DomainID: 1234, DomainID: 1234,
Name: "_acme-challenge", Name: "_acme-challenge",
ResourceID: 1234, ResourceID: 1234,
@ -295,23 +311,19 @@ func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) {
"domain.resource.delete": MockResponse{ "domain.resource.delete": MockResponse{
Response: nil, Response: nil,
Errors: []linode.ResponseError{ Errors: []linode.ResponseError{
linode.ResponseError{ {
Code: 1234, Code: 1234,
Message: "Failed to delete domain resource", Message: "Failed to delete domain resource",
}, },
}, },
}, },
} }
mockSrv := newMockServer(t, mockResponses) mockSrv := newMockServer(t, mockResponses)
defer mockSrv.Close() defer mockSrv.Close()
p.linode.ToLinode().SetEndpoint(mockSrv.URL) p.linode.ToLinode().SetEndpoint(mockSrv.URL)
err = p.CleanUp(domain, "", keyAuth) err = p.CleanUp(domain, "", keyAuth)
assert.EqualError(t, err, "Failed to delete domain resource") assert.EqualError(t, err, "Failed to delete domain resource")
} }
func TestDNSProvider_CleanUpLive(t *testing.T) {
if !isTestLive {
t.Skip("Skipping live test")
}
}

View file

@ -8,11 +8,11 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"os"
"strings" "strings"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// Notes about namecheap's tool API: // Notes about namecheap's tool API:
@ -48,9 +48,12 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: NAMECHEAP_API_USER // Credentials must be passed in the environment variables: NAMECHEAP_API_USER
// and NAMECHEAP_API_KEY. // and NAMECHEAP_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiUser := os.Getenv("NAMECHEAP_API_USER") values, err := env.Get("NAMECHEAP_API_USER", "NAMECHEAP_API_KEY")
apiKey := os.Getenv("NAMECHEAP_API_KEY") if err != nil {
return NewDNSProviderCredentials(apiUser, apiKey) return nil, fmt.Errorf("NameCheap: %v", err)
}
return NewDNSProviderCredentials(values["NAMECHEAP_API_USER"], values["NAMECHEAP_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
@ -117,7 +120,7 @@ func getClientIP() (addr string, err error) {
return string(clientIP), nil return string(clientIP), nil
} }
// A challenge repesents all the data needed to specify a dns-01 challenge // A challenge represents all the data needed to specify a dns-01 challenge
// to lets-encrypt. // to lets-encrypt.
type challenge struct { type challenge struct {
domain string domain string

View file

@ -241,24 +241,27 @@ func TestNamecheapDomainSplit(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
valid := true test := test
ch, err := newChallenge(test.domain, "", tlds) t.Run(test.domain, func(t *testing.T) {
if err != nil { valid := true
valid = false ch, err := newChallenge(test.domain, "", tlds)
} if err != nil {
valid = false
}
if test.valid && !valid { if test.valid && !valid {
t.Errorf("Expected '%s' to split", test.domain) t.Errorf("Expected '%s' to split", test.domain)
} else if !test.valid && valid { } else if !test.valid && valid {
t.Errorf("Expected '%s' to produce error", test.domain) t.Errorf("Expected '%s' to produce error", test.domain)
} }
if test.valid && valid { if test.valid && valid {
assertEq(t, "domain", ch.domain, test.domain) assertEq(t, "domain", ch.domain, test.domain)
assertEq(t, "tld", ch.tld, test.tld) assertEq(t, "tld", ch.tld, test.tld)
assertEq(t, "sld", ch.sld, test.sld) assertEq(t, "sld", ch.sld, test.sld)
assertEq(t, "host", ch.host, test.host) assertEq(t, "host", ch.host, test.host)
} }
})
} }
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/namedotcom/go/namecom" "github.com/namedotcom/go/namecom"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface. // DNSProvider is an implementation of the acme.ChallengeProvider interface.
@ -19,11 +20,13 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance configured for namedotcom. // NewDNSProvider returns a DNSProvider instance configured for namedotcom.
// Credentials must be passed in the environment variables: NAMECOM_USERNAME and NAMECOM_API_TOKEN // Credentials must be passed in the environment variables: NAMECOM_USERNAME and NAMECOM_API_TOKEN
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
username := os.Getenv("NAMECOM_USERNAME") values, err := env.Get("NAMECOM_USERNAME", "NAMECOM_API_TOKEN")
apiToken := os.Getenv("NAMECOM_API_TOKEN") if err != nil {
server := os.Getenv("NAMECOM_SERVER") return nil, fmt.Errorf("Name.com: %v", err)
}
return NewDNSProviderCredentials(username, apiToken, server) server := os.Getenv("NAMECOM_SERVER")
return NewDNSProviderCredentials(values["NAMECOM_USERNAME"], values["NAMECOM_API_TOKEN"], server)
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a
@ -59,7 +62,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
_, err := c.client.CreateRecord(request) _, err := c.client.CreateRecord(request)
if err != nil { if err != nil {
return fmt.Errorf("namedotcom API call failed: %v", err) return fmt.Errorf("Name.com API call failed: %v", err)
} }
return nil return nil

View file

@ -27,7 +27,7 @@ func init() {
} }
} }
func TestLivenamedotcomPresent(t *testing.T) { func TestLiveNamedotcomPresent(t *testing.T) {
if !namedotcomLiveTest { if !namedotcomLiveTest {
t.Skip("skipping live test") t.Skip("skipping live test")
} }
@ -43,7 +43,7 @@ func TestLivenamedotcomPresent(t *testing.T) {
// Cleanup // Cleanup
// //
func TestLivenamedotcomCleanUp(t *testing.T) { func TestLiveNamedotcomCleanUp(t *testing.T) {
if !namedotcomLiveTest { if !namedotcomLiveTest {
t.Skip("skipping live test") t.Skip("skipping live test")
} }

View file

@ -5,10 +5,10 @@ package ns1
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"os"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
"gopkg.in/ns1/ns1-go.v2/rest" "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns" "gopkg.in/ns1/ns1-go.v2/rest/model/dns"
) )
@ -21,11 +21,12 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance configured for NS1. // NewDNSProvider returns a DNSProvider instance configured for NS1.
// Credentials must be passed in the environment variables: NS1_API_KEY. // Credentials must be passed in the environment variables: NS1_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("NS1_API_KEY") values, err := env.Get("NS1_API_KEY")
if key == "" { if err != nil {
return nil, fmt.Errorf("NS1 credentials missing") return nil, fmt.Errorf("NS1: %v", err)
} }
return NewDNSProviderCredentials(key)
return NewDNSProviderCredentials(values["NS1_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -22,22 +22,24 @@ func init() {
} }
} }
func restoreNS1Env() { func restoreEnv() {
os.Setenv("NS1_API_KEY", apiKey) os.Setenv("NS1_API_KEY", apiKey)
} }
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
defer restoreEnv()
os.Setenv("NS1_API_KEY", "") os.Setenv("NS1_API_KEY", "")
_, err := NewDNSProviderCredentials("123") _, err := NewDNSProviderCredentials("123")
assert.NoError(t, err) assert.NoError(t, err)
restoreNS1Env()
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
defer restoreEnv()
os.Setenv("NS1_API_KEY", "") os.Setenv("NS1_API_KEY", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "NS1 credentials missing") assert.EqualError(t, err, "NS1: some credentials information are missing: NS1_API_KEY")
restoreNS1Env()
} }
func TestLivePresent(t *testing.T) { func TestLivePresent(t *testing.T) {

View file

@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
@ -31,12 +32,18 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: OTC_USER_NAME, // Credentials must be passed in the environment variables: OTC_USER_NAME,
// OTC_DOMAIN_NAME, OTC_PASSWORD OTC_PROJECT_NAME and OTC_IDENTITY_ENDPOINT. // OTC_DOMAIN_NAME, OTC_PASSWORD OTC_PROJECT_NAME and OTC_IDENTITY_ENDPOINT.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
domainName := os.Getenv("OTC_DOMAIN_NAME") values, err := env.Get("OTC_DOMAIN_NAME", "OTC_USER_NAME", "OTC_PASSWORD", "OTC_PROJECT_NAME")
userName := os.Getenv("OTC_USER_NAME") if err != nil {
password := os.Getenv("OTC_PASSWORD") return nil, fmt.Errorf("OTC: %v", err)
projectName := os.Getenv("OTC_PROJECT_NAME") }
identityEndpoint := os.Getenv("OTC_IDENTITY_ENDPOINT")
return NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint) return NewDNSProviderCredentials(
values["OTC_DOMAIN_NAME"],
values["OTC_USER_NAME"],
values["OTC_PASSWORD"],
values["OTC_PROJECT_NAME"],
os.Getenv("OTC_IDENTITY_ENDPOINT"),
)
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -2,10 +2,11 @@ package otc
import ( import (
"fmt" "fmt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"os" "os"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
) )
type OTCDNSTestSuite struct { type OTCDNSTestSuite struct {
@ -34,6 +35,8 @@ func (s *OTCDNSTestSuite) createDNSProvider() (*DNSProvider, error) {
} }
func (s *OTCDNSTestSuite) TestOTCDNSLoginEnv() { func (s *OTCDNSTestSuite) TestOTCDNSLoginEnv() {
defer os.Clearenv()
os.Setenv("OTC_DOMAIN_NAME", "unittest1") os.Setenv("OTC_DOMAIN_NAME", "unittest1")
os.Setenv("OTC_USER_NAME", "unittest2") os.Setenv("OTC_USER_NAME", "unittest2")
os.Setenv("OTC_PASSWORD", "unittest3") os.Setenv("OTC_PASSWORD", "unittest3")
@ -53,15 +56,13 @@ func (s *OTCDNSTestSuite) TestOTCDNSLoginEnv() {
provider, err = NewDNSProvider() provider, err = NewDNSProvider()
assert.Nil(s.T(), err) assert.Nil(s.T(), err)
assert.Equal(s.T(), provider.identityEndpoint, "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens") assert.Equal(s.T(), provider.identityEndpoint, "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens")
os.Clearenv()
} }
func (s *OTCDNSTestSuite) TestOTCDNSLoginEnvEmpty() { func (s *OTCDNSTestSuite) TestOTCDNSLoginEnvEmpty() {
_, err := NewDNSProvider() defer os.Clearenv()
assert.Equal(s.T(), "OTC credentials missing", err.Error())
os.Clearenv() _, err := NewDNSProvider()
assert.EqualError(s.T(), err, "OTC: some credentials information are missing: OTC_DOMAIN_NAME,OTC_USER_NAME,OTC_PASSWORD,OTC_PROJECT_NAME")
} }
func (s *OTCDNSTestSuite) TestOTCDNSLogin() { func (s *OTCDNSTestSuite) TestOTCDNSLogin() {

View file

@ -4,12 +4,12 @@ package ovh
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"sync" "sync"
"github.com/ovh/go-ovh/ovh" "github.com/ovh/go-ovh/ovh"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// OVH API reference: https://eu.api.ovh.com/ // OVH API reference: https://eu.api.ovh.com/
@ -30,11 +30,17 @@ type DNSProvider struct {
// OVH_APPLICATION_SECRET // OVH_APPLICATION_SECRET
// OVH_CONSUMER_KEY // OVH_CONSUMER_KEY
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiEndpoint := os.Getenv("OVH_ENDPOINT") values, err := env.Get("OVH_ENDPOINT", "OVH_APPLICATION_KEY", "OVH_APPLICATION_SECRET", "OVH_CONSUMER_KEY")
applicationKey := os.Getenv("OVH_APPLICATION_KEY") if err != nil {
applicationSecret := os.Getenv("OVH_APPLICATION_SECRET") return nil, fmt.Errorf("OVH: %v", err)
consumerKey := os.Getenv("OVH_CONSUMER_KEY") }
return NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, consumerKey)
return NewDNSProviderCredentials(
values["OVH_ENDPOINT"],
values["OVH_APPLICATION_KEY"],
values["OVH_APPLICATION_SECRET"],
values["OVH_CONSUMER_KEY"],
)
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -33,47 +33,88 @@ func restoreEnv() {
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("OVH_ENDPOINT", "ovh-eu") os.Setenv("OVH_ENDPOINT", "ovh-eu")
os.Setenv("OVH_APPLICATION_KEY", "1234") os.Setenv("OVH_APPLICATION_KEY", "1234")
os.Setenv("OVH_APPLICATION_SECRET", "5678") os.Setenv("OVH_APPLICATION_SECRET", "5678")
os.Setenv("OVH_CONSUMER_KEY", "abcde") os.Setenv("OVH_CONSUMER_KEY", "abcde")
defer restoreEnv()
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("OVH_ENDPOINT", "")
os.Setenv("OVH_APPLICATION_KEY", "1234")
os.Setenv("OVH_APPLICATION_SECRET", "5678")
os.Setenv("OVH_CONSUMER_KEY", "abcde")
defer restoreEnv() defer restoreEnv()
_, err := NewDNSProvider()
assert.EqualError(t, err, "OVH credentials missing")
os.Setenv("OVH_ENDPOINT", "ovh-eu") testCases := []struct {
os.Setenv("OVH_APPLICATION_KEY", "") desc string
os.Setenv("OVH_APPLICATION_SECRET", "5678") envVars map[string]string
os.Setenv("OVH_CONSUMER_KEY", "abcde") expected string
defer restoreEnv() }{
_, err = NewDNSProvider() {
assert.EqualError(t, err, "OVH credentials missing") desc: "missing OVH_ENDPOINT",
envVars: map[string]string{
"OVH_ENDPOINT": "",
"OVH_APPLICATION_KEY": "1234",
"OVH_APPLICATION_SECRET": "5678",
"OVH_CONSUMER_KEY": "abcde",
},
expected: "OVH: some credentials information are missing: OVH_ENDPOINT",
},
{
desc: "missing OVH_APPLICATION_KEY",
envVars: map[string]string{
"OVH_ENDPOINT": "ovh-eu",
"OVH_APPLICATION_KEY": "",
"OVH_APPLICATION_SECRET": "5678",
"OVH_CONSUMER_KEY": "abcde",
},
expected: "OVH: some credentials information are missing: OVH_APPLICATION_KEY",
},
{
desc: "missing OVH_APPLICATION_SECRET",
envVars: map[string]string{
"OVH_ENDPOINT": "ovh-eu",
"OVH_APPLICATION_KEY": "1234",
"OVH_APPLICATION_SECRET": "",
"OVH_CONSUMER_KEY": "abcde",
},
expected: "OVH: some credentials information are missing: OVH_APPLICATION_SECRET",
},
{
desc: "missing OVH_CONSUMER_KEY",
envVars: map[string]string{
"OVH_ENDPOINT": "ovh-eu",
"OVH_APPLICATION_KEY": "1234",
"OVH_APPLICATION_SECRET": "5678",
"OVH_CONSUMER_KEY": "",
},
expected: "OVH: some credentials information are missing: OVH_CONSUMER_KEY",
},
{
desc: "all missing",
envVars: map[string]string{
"OVH_ENDPOINT": "",
"OVH_APPLICATION_KEY": "",
"OVH_APPLICATION_SECRET": "",
"OVH_CONSUMER_KEY": "",
},
expected: "OVH: some credentials information are missing: OVH_ENDPOINT,OVH_APPLICATION_KEY,OVH_APPLICATION_SECRET,OVH_CONSUMER_KEY",
},
}
os.Setenv("OVH_ENDPOINT", "ovh-eu") for _, test := range testCases {
os.Setenv("OVH_APPLICATION_KEY", "1234") test := test
os.Setenv("OVH_APPLICATION_SECRET", "") t.Run(test.desc, func(t *testing.T) {
os.Setenv("OVH_CONSUMER_KEY", "abcde")
defer restoreEnv()
_, err = NewDNSProvider()
assert.EqualError(t, err, "OVH credentials missing")
os.Setenv("OVH_ENDPOINT", "ovh-eu") for key, value := range test.envVars {
os.Setenv("OVH_APPLICATION_KEY", "1234") os.Setenv(key, value)
os.Setenv("OVH_APPLICATION_SECRET", "5678") }
os.Setenv("OVH_CONSUMER_KEY", "")
defer restoreEnv() _, err := NewDNSProvider()
_, err = NewDNSProvider() assert.EqualError(t, err, test.expected)
assert.EqualError(t, err, "OVH credentials missing") })
}
} }
func TestLivePresent(t *testing.T) { func TestLivePresent(t *testing.T) {

View file

@ -9,12 +9,12 @@ import (
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface // DNSProvider is an implementation of the acme.ChallengeProvider interface
@ -28,13 +28,17 @@ type DNSProvider struct {
// Credentials must be passed in the environment variable: // Credentials must be passed in the environment variable:
// PDNS_API_URL and PDNS_API_KEY. // PDNS_API_URL and PDNS_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("PDNS_API_KEY") values, err := env.Get("PDNS_API_KEY", "PDNS_API_URL")
hostURL, err := url.Parse(os.Getenv("PDNS_API_URL"))
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("PDNS: %v", err)
} }
return NewDNSProviderCredentials(hostURL, key) hostURL, err := url.Parse(values["PDNS_API_URL"])
if err != nil {
return nil, fmt.Errorf("PDNS: %v", err)
}
return NewDNSProviderCredentials(hostURL, values["PDNS_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -26,42 +26,46 @@ func init() {
} }
} }
func restorePdnsEnv() { func restoreEnv() {
os.Setenv("PDNS_API_URL", pdnsURLStr) os.Setenv("PDNS_API_URL", pdnsURLStr)
os.Setenv("PDNS_API_KEY", pdnsAPIKey) os.Setenv("PDNS_API_KEY", pdnsAPIKey)
} }
func TestNewDNSProviderValid(t *testing.T) { func TestNewDNSProviderValid(t *testing.T) {
defer restoreEnv()
os.Setenv("PDNS_API_URL", "") os.Setenv("PDNS_API_URL", "")
os.Setenv("PDNS_API_KEY", "") os.Setenv("PDNS_API_KEY", "")
tmpURL, _ := url.Parse("http://localhost:8081") tmpURL, _ := url.Parse("http://localhost:8081")
_, err := NewDNSProviderCredentials(tmpURL, "123") _, err := NewDNSProviderCredentials(tmpURL, "123")
assert.NoError(t, err) assert.NoError(t, err)
restorePdnsEnv()
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("PDNS_API_URL", "http://localhost:8081") os.Setenv("PDNS_API_URL", "http://localhost:8081")
os.Setenv("PDNS_API_KEY", "123") os.Setenv("PDNS_API_KEY", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
restorePdnsEnv()
} }
func TestNewDNSProviderMissingHostErr(t *testing.T) { func TestNewDNSProviderMissingHostErr(t *testing.T) {
defer restoreEnv()
os.Setenv("PDNS_API_URL", "") os.Setenv("PDNS_API_URL", "")
os.Setenv("PDNS_API_KEY", "123") os.Setenv("PDNS_API_KEY", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "PDNS API URL missing") assert.EqualError(t, err, "PDNS: some credentials information are missing: PDNS_API_URL")
restorePdnsEnv()
} }
func TestNewDNSProviderMissingKeyErr(t *testing.T) { func TestNewDNSProviderMissingKeyErr(t *testing.T) {
defer restoreEnv()
os.Setenv("PDNS_API_URL", pdnsURLStr) os.Setenv("PDNS_API_URL", pdnsURLStr)
os.Setenv("PDNS_API_KEY", "") os.Setenv("PDNS_API_KEY", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "PDNS API key missing") assert.EqualError(t, err, "PDNS: some credentials information are missing: PDNS_API_KEY,PDNS_API_URL")
restorePdnsEnv()
} }
func TestPdnsPresentAndCleanup(t *testing.T) { func TestPdnsPresentAndCleanup(t *testing.T) {

View file

@ -8,10 +8,10 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// rackspaceAPIURL represents the Identity API endpoint to call // rackspaceAPIURL represents the Identity API endpoint to call
@ -28,9 +28,12 @@ type DNSProvider struct {
// Credentials must be passed in the environment variables: RACKSPACE_USER // Credentials must be passed in the environment variables: RACKSPACE_USER
// and RACKSPACE_API_KEY. // and RACKSPACE_API_KEY.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
user := os.Getenv("RACKSPACE_USER") values, err := env.Get("RACKSPACE_USER", "RACKSPACE_API_KEY")
key := os.Getenv("RACKSPACE_API_KEY") if err != nil {
return NewDNSProviderCredentials(user, key) return nil, fmt.Errorf("Rackspace: %v", err)
}
return NewDNSProviderCredentials(values["RACKSPACE_USER"], values["RACKSPACE_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a // NewDNSProviderCredentials uses the supplied credentials to return a

View file

@ -38,6 +38,7 @@ func NewDNSProvider() (*DNSProvider, error) {
tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigKey := os.Getenv("RFC2136_TSIG_KEY")
tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET")
timeout := os.Getenv("RFC2136_TIMEOUT") timeout := os.Getenv("RFC2136_TIMEOUT")
return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout) return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout)
} }
@ -58,13 +59,14 @@ func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, t
return nil, err return nil, err
} }
} }
d := &DNSProvider{
nameserver: nameserver, d := &DNSProvider{nameserver: nameserver}
}
if tsigAlgorithm == "" { if tsigAlgorithm == "" {
tsigAlgorithm = dns.HmacMD5 tsigAlgorithm = dns.HmacMD5
} }
d.tsigAlgorithm = tsigAlgorithm d.tsigAlgorithm = tsigAlgorithm
if len(tsigKey) > 0 && len(tsigSecret) > 0 { if len(tsigKey) > 0 && len(tsigSecret) > 0 {
d.tsigKey = tsigKey d.tsigKey = tsigKey
d.tsigSecret = tsigSecret d.tsigSecret = tsigSecret

View file

@ -10,6 +10,8 @@ import (
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
) )
@ -32,22 +34,20 @@ func TestRFC2136CanaryLocalTestServer(t *testing.T) {
defer dns.HandleRemove("example.com.") defer dns.HandleRemove("example.com.")
server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false)
if err != nil { require.NoError(t, err, "Failed to start test server")
t.Fatalf("Failed to start test server: %v", err)
}
defer server.Shutdown() defer server.Shutdown()
c := new(dns.Client) c := new(dns.Client)
m := new(dns.Msg) m := new(dns.Msg)
m.SetQuestion("example.com.", dns.TypeTXT) m.SetQuestion("example.com.", dns.TypeTXT)
r, _, err := c.Exchange(m, addrstr) r, _, err := c.Exchange(m, addrstr)
if err != nil || len(r.Extra) == 0 { require.NoError(t, err, "Failed to communicate with test server")
t.Fatalf("Failed to communicate with test server: %v", err) assert.Len(t, r.Extra, 1, "Failed to communicate with test server")
}
txt := r.Extra[0].(*dns.TXT).Txt[0] txt := r.Extra[0].(*dns.TXT).Txt[0]
if txt != "Hello world" { assert.Equal(t, "Hello world", txt)
t.Error("Expected test server to return 'Hello world' but got: ", txt)
}
} }
func TestRFC2136ServerSuccess(t *testing.T) { func TestRFC2136ServerSuccess(t *testing.T) {
@ -56,18 +56,14 @@ func TestRFC2136ServerSuccess(t *testing.T) {
defer dns.HandleRemove(rfc2136TestZone) defer dns.HandleRemove(rfc2136TestZone)
server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false)
if err != nil { require.NoError(t, err, "Failed to start test server")
t.Fatalf("Failed to start test server: %v", err)
}
defer server.Shutdown() defer server.Shutdown()
provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "") provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "")
if err != nil { require.NoError(t, err)
t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err)
} err = provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth)
if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { require.NoError(t, err)
t.Errorf("Expected Present() to return no error but the error was -> %v", err)
}
} }
func TestRFC2136ServerError(t *testing.T) { func TestRFC2136ServerError(t *testing.T) {
@ -76,19 +72,16 @@ func TestRFC2136ServerError(t *testing.T) {
defer dns.HandleRemove(rfc2136TestZone) defer dns.HandleRemove(rfc2136TestZone)
server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false)
if err != nil { require.NoError(t, err, "Failed to start test server")
t.Fatalf("Failed to start test server: %v", err)
}
defer server.Shutdown() defer server.Shutdown()
provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "") provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "")
if err != nil { require.NoError(t, err)
t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err)
} err = provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth)
if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err == nil { require.Error(t, err)
t.Errorf("Expected Present() to return an error but it did not.") if !strings.Contains(err.Error(), "NOTZONE") {
} else if !strings.Contains(err.Error(), "NOTZONE") { t.Errorf("Expected Present() to return an error with the 'NOTZONE' rcode string but it did not: %v", err)
t.Errorf("Expected Present() to return an error with the 'NOTZONE' rcode string but it did not.")
} }
} }
@ -98,18 +91,14 @@ func TestRFC2136TsigClient(t *testing.T) {
defer dns.HandleRemove(rfc2136TestZone) defer dns.HandleRemove(rfc2136TestZone)
server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", true) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", true)
if err != nil { require.NoError(t, err, "Failed to start test server")
t.Fatalf("Failed to start test server: %v", err)
}
defer server.Shutdown() defer server.Shutdown()
provider, err := NewDNSProviderCredentials(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret, "") provider, err := NewDNSProviderCredentials(addrstr, "", rfc2136TestTsigKey, rfc2136TestTsigSecret, "")
if err != nil { require.NoError(t, err)
t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err)
} err = provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth)
if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { require.NoError(t, err)
t.Errorf("Expected Present() to return no error but the error was -> %v", err)
}
} }
func TestRFC2136ValidUpdatePacket(t *testing.T) { func TestRFC2136ValidUpdatePacket(t *testing.T) {
@ -118,9 +107,7 @@ func TestRFC2136ValidUpdatePacket(t *testing.T) {
defer dns.HandleRemove(rfc2136TestZone) defer dns.HandleRemove(rfc2136TestZone)
server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false)
if err != nil { require.NoError(t, err, "Failed to start test server")
t.Fatalf("Failed to start test server: %v", err)
}
defer server.Shutdown() defer server.Shutdown()
txtRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN TXT %s", rfc2136TestFqdn, rfc2136TestTTL, rfc2136TestValue)) txtRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN TXT %s", rfc2136TestFqdn, rfc2136TestTTL, rfc2136TestValue))
@ -130,26 +117,21 @@ func TestRFC2136ValidUpdatePacket(t *testing.T) {
m.RemoveRRset(rrs) m.RemoveRRset(rrs)
m.Insert(rrs) m.Insert(rrs)
expectstr := m.String() expectstr := m.String()
expect, err := m.Pack() expect, err := m.Pack()
if err != nil { require.NoError(t, err, "error packing")
t.Fatalf("Error packing expect msg: %v", err)
}
provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "") provider, err := NewDNSProviderCredentials(addrstr, "", "", "", "")
if err != nil { require.NoError(t, err)
t.Fatalf("Expected NewDNSProviderCredentials() to return no error but the error was -> %v", err)
}
if err := provider.Present(rfc2136TestDomain, "", "1234d=="); err != nil { err = provider.Present(rfc2136TestDomain, "", "1234d==")
t.Errorf("Expected Present() to return no error but the error was -> %v", err) require.NoError(t, err)
}
rcvMsg := <-reqChan rcvMsg := <-reqChan
rcvMsg.Id = m.Id rcvMsg.Id = m.Id
actual, err := rcvMsg.Pack() actual, err := rcvMsg.Pack()
if err != nil { require.NoError(t, err, "error packing")
t.Fatalf("Error packing actual msg: %v", err)
}
if !bytes.Equal(actual, expect) { if !bytes.Equal(actual, expect) {
tmp := new(dns.Msg) tmp := new(dns.Msg)

View file

@ -8,6 +8,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53" "github.com/aws/aws-sdk-go/service/route53"
"github.com/stretchr/testify/require"
) )
func TestRoute53TTL(t *testing.T) { func TestRoute53TTL(t *testing.T) {
@ -17,14 +18,10 @@ func TestRoute53TTL(t *testing.T) {
} }
provider, err := NewDNSProvider() provider, err := NewDNSProvider()
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
err = provider.Present(m["route53Domain"], "foo", "bar") err = provider.Present(m["route53Domain"], "foo", "bar")
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
// we need a separate R53 client here as the one in the DNS provider is // we need a separate R53 client here as the one in the DNS provider is
// unexported. // unexported.

View file

@ -26,7 +26,7 @@ func init() {
route53Zone = os.Getenv("AWS_HOSTED_ZONE_ID") route53Zone = os.Getenv("AWS_HOSTED_ZONE_ID")
} }
func restoreRoute53Env() { func restoreEnv() {
os.Setenv("AWS_ACCESS_KEY_ID", route53Key) os.Setenv("AWS_ACCESS_KEY_ID", route53Key)
os.Setenv("AWS_SECRET_ACCESS_KEY", route53Secret) os.Setenv("AWS_SECRET_ACCESS_KEY", route53Secret)
os.Setenv("AWS_REGION", route53Region) os.Setenv("AWS_REGION", route53Region)
@ -46,6 +46,7 @@ func makeRoute53Provider(ts *httptest.Server) *DNSProvider {
} }
func TestCredentialsFromEnv(t *testing.T) { func TestCredentialsFromEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("AWS_ACCESS_KEY_ID", "123") os.Setenv("AWS_ACCESS_KEY_ID", "123")
os.Setenv("AWS_SECRET_ACCESS_KEY", "123") os.Setenv("AWS_SECRET_ACCESS_KEY", "123")
os.Setenv("AWS_REGION", "us-east-1") os.Setenv("AWS_REGION", "us-east-1")
@ -57,23 +58,20 @@ func TestCredentialsFromEnv(t *testing.T) {
sess := session.New(config) sess := session.New(config)
_, err := sess.Config.Credentials.Get() _, err := sess.Config.Credentials.Get()
assert.NoError(t, err, "Expected credentials to be set from environment") assert.NoError(t, err, "Expected credentials to be set from environment")
restoreRoute53Env()
} }
func TestRegionFromEnv(t *testing.T) { func TestRegionFromEnv(t *testing.T) {
defer restoreEnv()
os.Setenv("AWS_REGION", "us-east-1") os.Setenv("AWS_REGION", "us-east-1")
sess := session.New(aws.NewConfig()) sess := session.New(aws.NewConfig())
assert.Equal(t, "us-east-1", aws.StringValue(sess.Config.Region), "Expected Region to be set from environment") assert.Equal(t, "us-east-1", aws.StringValue(sess.Config.Region), "Expected Region to be set from environment")
restoreRoute53Env()
} }
func TestHostedZoneIDFromEnv(t *testing.T) { func TestHostedZoneIDFromEnv(t *testing.T) {
const testZoneID = "testzoneid" defer restoreEnv()
defer restoreRoute53Env() const testZoneID = "testzoneid"
os.Setenv("AWS_HOSTED_ZONE_ID", testZoneID) os.Setenv("AWS_HOSTED_ZONE_ID", testZoneID)
provider, err := NewDNSProvider() provider, err := NewDNSProvider()

View file

@ -5,11 +5,11 @@ package vultr
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
vultr "github.com/JamesClonk/vultr/lib" vultr "github.com/JamesClonk/vultr/lib"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
"github.com/xenolf/lego/platform/config/env"
) )
// DNSProvider is an implementation of the acme.ChallengeProvider interface. // DNSProvider is an implementation of the acme.ChallengeProvider interface.
@ -20,8 +20,12 @@ type DNSProvider struct {
// NewDNSProvider returns a DNSProvider instance with a configured Vultr client. // NewDNSProvider returns a DNSProvider instance with a configured Vultr client.
// Authentication uses the VULTR_API_KEY environment variable. // Authentication uses the VULTR_API_KEY environment variable.
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
apiKey := os.Getenv("VULTR_API_KEY") values, err := env.Get("VULTR_API_KEY")
return NewDNSProviderCredentials(apiKey) if err != nil {
return nil, fmt.Errorf("Vultr: %v", err)
}
return NewDNSProviderCredentials(values["VULTR_API_KEY"])
} }
// NewDNSProviderCredentials uses the supplied credentials to return a DNSProvider // NewDNSProviderCredentials uses the supplied credentials to return a DNSProvider

View file

@ -25,17 +25,19 @@ func restoreEnv() {
} }
func TestNewDNSProviderValidEnv(t *testing.T) { func TestNewDNSProviderValidEnv(t *testing.T) {
os.Setenv("VULTR_API_KEY", "123")
defer restoreEnv() defer restoreEnv()
os.Setenv("VULTR_API_KEY", "123")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestNewDNSProviderMissingCredErr(t *testing.T) { func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("VULTR_API_KEY", "")
defer restoreEnv() defer restoreEnv()
os.Setenv("VULTR_API_KEY", "")
_, err := NewDNSProvider() _, err := NewDNSProvider()
assert.EqualError(t, err, "Vultr credentials missing") assert.EqualError(t, err, "Vultr: some credentials information are missing: VULTR_API_KEY")
} }
func TestLivePresent(t *testing.T) { func TestLivePresent(t *testing.T) {