Add support for reading DNS provider setup from files (#535)

This commit is contained in:
Matt Farmer 2018-10-06 09:33:15 -04:00 committed by Ludovic Fernandez
parent 37ef38c4fc
commit fa455bc037
13 changed files with 112 additions and 31 deletions

View file

@ -3,10 +3,13 @@ package env
import ( import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/xenolf/lego/log"
) )
// Get environment variables // Get environment variables
@ -15,7 +18,7 @@ func Get(names ...string) (map[string]string, error) {
var missingEnvVars []string var missingEnvVars []string
for _, envVar := range names { for _, envVar := range names {
value := os.Getenv(envVar) value := GetOrFile(envVar)
if value == "" { if value == "" {
missingEnvVars = append(missingEnvVars, envVar) missingEnvVars = append(missingEnvVars, envVar)
} }
@ -76,13 +79,13 @@ func GetWithFallback(groups ...[]string) (map[string]string, error) {
} }
func getOneWithFallback(main string, names ...string) (string, string) { func getOneWithFallback(main string, names ...string) (string, string) {
value := os.Getenv(main) value := GetOrFile(main)
if len(value) > 0 { if len(value) > 0 {
return value, main return value, main
} }
for _, name := range names { for _, name := range names {
value := os.Getenv(name) value := GetOrFile(name)
if len(value) > 0 { if len(value) > 0 {
return value, main return value, main
} }
@ -94,7 +97,7 @@ func getOneWithFallback(main string, names ...string) (string, string) {
// GetOrDefaultInt returns the given environment variable value as an integer. // GetOrDefaultInt returns the given environment variable value as an integer.
// Returns the default if the envvar cannot be coopered to an int, or is not found. // Returns the default if the envvar cannot be coopered to an int, or is not found.
func GetOrDefaultInt(envVar string, defaultValue int) int { func GetOrDefaultInt(envVar string, defaultValue int) int {
v, err := strconv.Atoi(os.Getenv(envVar)) v, err := strconv.Atoi(GetOrFile(envVar))
if err != nil { if err != nil {
return defaultValue return defaultValue
} }
@ -116,7 +119,7 @@ func GetOrDefaultSecond(envVar string, defaultValue time.Duration) time.Duration
// GetOrDefaultString returns the given environment variable value as a string. // GetOrDefaultString returns the given environment variable value as a string.
// Returns the default if the envvar cannot be find. // Returns the default if the envvar cannot be find.
func GetOrDefaultString(envVar string, defaultValue string) string { func GetOrDefaultString(envVar string, defaultValue string) string {
v := os.Getenv(envVar) v := GetOrFile(envVar)
if len(v) == 0 { if len(v) == 0 {
return defaultValue return defaultValue
} }
@ -127,10 +130,34 @@ func GetOrDefaultString(envVar string, defaultValue string) string {
// GetOrDefaultBool returns the given environment variable value as a boolean. // GetOrDefaultBool returns the given environment variable value as a boolean.
// Returns the default if the envvar cannot be coopered to a boolean, or is not found. // Returns the default if the envvar cannot be coopered to a boolean, or is not found.
func GetOrDefaultBool(envVar string, defaultValue bool) bool { func GetOrDefaultBool(envVar string, defaultValue bool) bool {
v, err := strconv.ParseBool(os.Getenv(envVar)) v, err := strconv.ParseBool(GetOrFile(envVar))
if err != nil { if err != nil {
return defaultValue return defaultValue
} }
return v return v
} }
// GetOrFile Attempts to resolve 'key' as an environment variable.
// Failing that, it will check to see if '<key>_FILE' exists.
// If so, it will attempt to read from the referenced file to populate a value.
func GetOrFile(envVar string) string {
envVarValue := os.Getenv(envVar)
if envVarValue != "" {
return envVarValue
}
fileVar := envVar + "_FILE"
fileVarValue := os.Getenv(fileVar)
if fileVarValue == "" {
return envVarValue
}
fileContents, err := ioutil.ReadFile(fileVarValue)
if err != nil {
log.Printf("Failed to read the file %s (defined by env var %s): %s", fileVarValue, fileVar, err)
return ""
}
return string(fileContents)
}

View file

@ -1,6 +1,7 @@
package env package env
import ( import (
"io/ioutil"
"os" "os"
"testing" "testing"
"time" "time"
@ -273,3 +274,67 @@ func TestGetOrDefaultBool(t *testing.T) {
}) })
} }
} }
func TestGetOrFile_ReadsEnvVars(t *testing.T) {
err := os.Setenv("TEST_LEGO_ENV_VAR", "lego_env")
require.NoError(t, err)
defer os.Unsetenv("TEST_LEGO_ENV_VAR")
value := GetOrFile("TEST_LEGO_ENV_VAR")
assert.Equal(t, "lego_env", value)
}
func TestGetOrFile_ReadsFiles(t *testing.T) {
varEnvFileName := "TEST_LEGO_ENV_VAR_FILE"
varEnvName := "TEST_LEGO_ENV_VAR"
err := os.Unsetenv(varEnvFileName)
require.NoError(t, err)
err = os.Unsetenv(varEnvName)
require.NoError(t, err)
file, err := ioutil.TempFile("", "lego")
require.NoError(t, err)
defer os.Remove(file.Name())
err = ioutil.WriteFile(file.Name(), []byte("lego_file"), 0644)
require.NoError(t, err)
err = os.Setenv(varEnvFileName, file.Name())
require.NoError(t, err)
defer os.Unsetenv(varEnvFileName)
value := GetOrFile(varEnvName)
assert.Equal(t, "lego_file", value)
}
func TestGetOrFile_PrefersEnvVars(t *testing.T) {
varEnvFileName := "TEST_LEGO_ENV_VAR_FILE"
varEnvName := "TEST_LEGO_ENV_VAR"
err := os.Unsetenv(varEnvFileName)
require.NoError(t, err)
err = os.Unsetenv(varEnvName)
require.NoError(t, err)
file, err := ioutil.TempFile("", "lego")
require.NoError(t, err)
defer os.Remove(file.Name())
err = ioutil.WriteFile(file.Name(), []byte("lego_file"), 0644)
require.NoError(t, err)
err = os.Setenv(varEnvFileName, file.Name())
require.NoError(t, err)
defer os.Unsetenv(varEnvFileName)
err = os.Setenv(varEnvName, "lego_env")
require.NoError(t, err)
defer os.Unsetenv(varEnvName)
value := GetOrFile(varEnvName)
assert.Equal(t, "lego_env", value)
}

View file

@ -5,7 +5,6 @@ package alidns
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
@ -57,7 +56,7 @@ func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.APIKey = values["ALICLOUD_ACCESS_KEY"] config.APIKey = values["ALICLOUD_ACCESS_KEY"]
config.SecretKey = values["ALICLOUD_SECRET_KEY"] config.SecretKey = values["ALICLOUD_SECRET_KEY"]
config.RegionID = os.Getenv("ALICLOUD_REGION_ID") config.RegionID = env.GetOrFile("ALICLOUD_REGION_ID")
return NewDNSProviderConfig(config) return NewDNSProviderConfig(config)
} }

View file

@ -3,7 +3,6 @@ package auroradns
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"sync" "sync"
"time" "time"
@ -53,7 +52,7 @@ func NewDNSProvider() (*DNSProvider, error) {
} }
config := NewDefaultConfig() config := NewDefaultConfig()
config.BaseURL = os.Getenv("AURORA_ENDPOINT") config.BaseURL = env.GetOrFile("AURORA_ENDPOINT")
config.UserID = values["AURORA_USER_ID"] config.UserID = values["AURORA_USER_ID"]
config.Key = values["AURORA_KEY"] config.Key = values["AURORA_KEY"]

View file

@ -5,7 +5,6 @@ package dnsimple
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -45,8 +44,8 @@ type DNSProvider struct {
// See: https://developer.dnsimple.com/v2/#authentication // See: https://developer.dnsimple.com/v2/#authentication
func NewDNSProvider() (*DNSProvider, error) { func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.AccessToken = os.Getenv("DNSIMPLE_OAUTH_TOKEN") config.AccessToken = env.GetOrFile("DNSIMPLE_OAUTH_TOKEN")
config.BaseURL = os.Getenv("DNSIMPLE_BASE_URL") config.BaseURL = env.GetOrFile("DNSIMPLE_BASE_URL")
return NewDNSProviderConfig(config) return NewDNSProviderConfig(config)
} }

View file

@ -5,7 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -57,7 +56,7 @@ func NewDNSProvider() (*DNSProvider, error) {
} }
var baseURL string var baseURL string
if sandbox, _ := strconv.ParseBool(os.Getenv("DNSMADEEASY_SANDBOX")); sandbox { if sandbox, _ := strconv.ParseBool(env.GetOrFile("DNSMADEEASY_SANDBOX")); 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"

View file

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"os"
"time" "time"
"github.com/exoscale/egoscale" "github.com/exoscale/egoscale"
@ -56,7 +55,7 @@ func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.APIKey = values["EXOSCALE_API_KEY"] config.APIKey = values["EXOSCALE_API_KEY"]
config.APISecret = values["EXOSCALE_API_SECRET"] config.APISecret = values["EXOSCALE_API_SECRET"]
config.Endpoint = os.Getenv("EXOSCALE_ENDPOINT") config.Endpoint = env.GetOrFile("EXOSCALE_ENDPOINT")
return NewDNSProviderConfig(config) return NewDNSProviderConfig(config)
} }

View file

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/rand" "math/rand"
"os"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
@ -54,7 +53,7 @@ type Config struct {
// NewDefaultConfig returns a default configuration for the DNSProvider // NewDefaultConfig returns a default configuration for the DNSProvider
func NewDefaultConfig() *Config { func NewDefaultConfig() *Config {
return &Config{ return &Config{
DNSZone: os.Getenv("DNS_ZONE"), DNSZone: env.GetOrFile("DNS_ZONE"),
PropagationTimeout: env.GetOrDefaultSecond("LIGHTSAIL_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), PropagationTimeout: env.GetOrDefaultSecond("LIGHTSAIL_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
PollingInterval: env.GetOrDefaultSecond("LIGHTSAIL_POLLING_INTERVAL", acme.DefaultPollingInterval), PollingInterval: env.GetOrDefaultSecond("LIGHTSAIL_POLLING_INTERVAL", acme.DefaultPollingInterval),
Region: env.GetOrDefaultString("LIGHTSAIL_REGION", "us-east-1"), Region: env.GetOrDefaultString("LIGHTSAIL_REGION", "us-east-1"),

View file

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"os"
"strings" "strings"
"time" "time"
@ -56,7 +55,7 @@ func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.Username = values["NAMECOM_USERNAME"] config.Username = values["NAMECOM_USERNAME"]
config.APIToken = values["NAMECOM_API_TOKEN"] config.APIToken = values["NAMECOM_API_TOKEN"]
config.Server = os.Getenv("NAMECOM_SERVER") config.Server = env.GetOrFile("NAMECOM_SERVER")
return NewDNSProviderConfig(config) return NewDNSProviderConfig(config)
} }

View file

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"os"
"time" "time"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme"
@ -52,7 +51,7 @@ func NewDNSProvider() (*DNSProvider, error) {
} }
config := NewDefaultConfig() config := NewDefaultConfig()
config.BaseURL = os.Getenv("NIFCLOUD_DNS_ENDPOINT") config.BaseURL = env.GetOrFile("NIFCLOUD_DNS_ENDPOINT")
config.AccessKey = values["NIFCLOUD_ACCESS_KEY_ID"] config.AccessKey = values["NIFCLOUD_ACCESS_KEY_ID"]
config.SecretKey = values["NIFCLOUD_SECRET_ACCESS_KEY"] config.SecretKey = values["NIFCLOUD_SECRET_ACCESS_KEY"]

View file

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"os"
"strings" "strings"
"time" "time"
@ -62,8 +61,8 @@ func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.Nameserver = values["RFC2136_NAMESERVER"] config.Nameserver = values["RFC2136_NAMESERVER"]
config.TSIGKey = os.Getenv("RFC2136_TSIG_KEY") config.TSIGKey = env.GetOrFile("RFC2136_TSIG_KEY")
config.TSIGSecret = os.Getenv("RFC2136_TSIG_SECRET") config.TSIGSecret = env.GetOrFile("RFC2136_TSIG_SECRET")
return NewDNSProviderConfig(config) return NewDNSProviderConfig(config)
} }

View file

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/rand" "math/rand"
"os"
"strings" "strings"
"time" "time"
@ -35,7 +34,7 @@ func NewDefaultConfig() *Config {
TTL: env.GetOrDefaultInt("AWS_TTL", 10), TTL: env.GetOrDefaultInt("AWS_TTL", 10),
PropagationTimeout: env.GetOrDefaultSecond("AWS_PROPAGATION_TIMEOUT", 2*time.Minute), PropagationTimeout: env.GetOrDefaultSecond("AWS_PROPAGATION_TIMEOUT", 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond("AWS_POLLING_INTERVAL", 4*time.Second), PollingInterval: env.GetOrDefaultSecond("AWS_POLLING_INTERVAL", 4*time.Second),
HostedZoneID: os.Getenv("AWS_HOSTED_ZONE_ID"), HostedZoneID: env.GetOrFile("AWS_HOSTED_ZONE_ID"),
} }
} }

View file

@ -5,7 +5,6 @@ package vegadns
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
@ -50,8 +49,8 @@ func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig() config := NewDefaultConfig()
config.BaseURL = values["VEGADNS_URL"] config.BaseURL = values["VEGADNS_URL"]
config.APIKey = os.Getenv("SECRET_VEGADNS_KEY") config.APIKey = env.GetOrFile("SECRET_VEGADNS_KEY")
config.APISecret = os.Getenv("SECRET_VEGADNS_SECRET") config.APISecret = env.GetOrFile("SECRET_VEGADNS_SECRET")
return NewDNSProviderConfig(config) return NewDNSProviderConfig(config)
} }