Skip to content

Commit

Permalink
liquidweb: add LWAPI_ prefix for env vars (#2034)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez authored Oct 14, 2023
1 parent 8afdc9d commit 52990b3
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 49 deletions.
81 changes: 52 additions & 29 deletions platform/config/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,59 +78,59 @@ func GetWithFallback(groups ...[]string) (map[string]string, error) {
return values, nil
}

func GetOneWithFallback[T any](main string, defaultValue T, fn func(string) (T, error), names ...string) T {
v, _ := getOneWithFallback(main, names...)

value, err := fn(v)
if err != nil {
return defaultValue
}

return value
}

func getOneWithFallback(main string, names ...string) (string, string) {
value := GetOrFile(main)
if len(value) > 0 {
if value != "" {
return value, main
}

for _, name := range names {
value := GetOrFile(name)
if len(value) > 0 {
if value != "" {
return value, main
}
}

return "", main
}

// GetOrDefaultString returns the given environment variable value as a string.
// Returns the default if the env var cannot be found.
func GetOrDefaultString(envVar string, defaultValue string) string {
return getOrDefault(envVar, defaultValue, ParseString)
}

// GetOrDefaultBool returns the given environment variable value as a boolean.
// Returns the default if the env var cannot be coopered to a boolean, or is not found.
func GetOrDefaultBool(envVar string, defaultValue bool) bool {
return getOrDefault(envVar, defaultValue, strconv.ParseBool)
}

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

return v
return getOrDefault(envVar, defaultValue, strconv.Atoi)
}

// GetOrDefaultSecond returns the given environment variable value as a time.Duration (second).
// Returns the default if the env var cannot be coopered to an int, or is not found.
func GetOrDefaultSecond(envVar string, defaultValue time.Duration) time.Duration {
v := GetOrDefaultInt(envVar, -1)
if v < 0 {
return defaultValue
}

return time.Duration(v) * time.Second
}

// GetOrDefaultString returns the given environment variable value as a string.
// Returns the default if the env var cannot be found.
func GetOrDefaultString(envVar, defaultValue string) string {
v := GetOrFile(envVar)
if v == "" {
return defaultValue
}

return v
return getOrDefault(envVar, defaultValue, ParseSecond)
}

// GetOrDefaultBool returns the given environment variable value as a boolean.
// Returns the default if the env var cannot be coopered to a boolean, or is not found.
func GetOrDefaultBool(envVar string, defaultValue bool) bool {
v, err := strconv.ParseBool(GetOrFile(envVar))
func getOrDefault[T any](envVar string, defaultValue T, fn func(string) (T, error)) T {
v, err := fn(GetOrFile(envVar))
if err != nil {
return defaultValue
}
Expand Down Expand Up @@ -161,3 +161,26 @@ func GetOrFile(envVar string) string {

return strings.TrimSuffix(string(fileContents), "\n")
}

// ParseSecond parses env var value (string) to a second (time.Duration).
func ParseSecond(s string) (time.Duration, error) {
v, err := strconv.Atoi(s)
if err != nil {
return 0, err
}

if v < 0 {
return 0, fmt.Errorf("unsupported value: %d", v)
}

return time.Duration(v) * time.Second, nil
}

// ParseString parses env var value (string) to a string but throws an error when the string is empty.
func ParseString(s string) (string, error) {
if s == "" {
return "", errors.New("empty string")
}

return s, nil
}
75 changes: 73 additions & 2 deletions platform/config/env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ func TestGetWithFallback(t *testing.T) {
var1Missing := os.Getenv("TEST_LEGO_VAR_MISSING_1")
var2Missing := os.Getenv("TEST_LEGO_VAR_MISSING_2")

defer func() {
t.Cleanup(func() {
_ = os.Setenv("TEST_LEGO_VAR_EXIST_1", var1Exist)
_ = os.Setenv("TEST_LEGO_VAR_EXIST_2", var2Exist)
_ = os.Setenv("TEST_LEGO_VAR_MISSING_1", var1Missing)
_ = os.Setenv("TEST_LEGO_VAR_MISSING_2", var2Missing)
}()
})

err := os.Setenv("TEST_LEGO_VAR_EXIST_1", "VAR1")
require.NoError(t, err)
Expand Down Expand Up @@ -93,7 +93,10 @@ func TestGetWithFallback(t *testing.T) {
}

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

value, err := GetWithFallback(test.groups...)
if len(test.expected.error) > 0 {
assert.EqualError(t, err, test.expected.error)
Expand All @@ -105,6 +108,74 @@ func TestGetWithFallback(t *testing.T) {
}
}

func TestGetOneWithFallback(t *testing.T) {
var1Exist := os.Getenv("TEST_LEGO_VAR_EXIST_1")
var2Exist := os.Getenv("TEST_LEGO_VAR_EXIST_2")
var1Missing := os.Getenv("TEST_LEGO_VAR_MISSING_1")
var2Missing := os.Getenv("TEST_LEGO_VAR_MISSING_2")

t.Cleanup(func() {
_ = os.Setenv("TEST_LEGO_VAR_EXIST_1", var1Exist)
_ = os.Setenv("TEST_LEGO_VAR_EXIST_2", var2Exist)
_ = os.Setenv("TEST_LEGO_VAR_MISSING_1", var1Missing)
_ = os.Setenv("TEST_LEGO_VAR_MISSING_2", var2Missing)
})

err := os.Setenv("TEST_LEGO_VAR_EXIST_1", "VAR1")
require.NoError(t, err)
err = os.Setenv("TEST_LEGO_VAR_EXIST_2", "VAR2")
require.NoError(t, err)
err = os.Unsetenv("TEST_LEGO_VAR_MISSING_1")
require.NoError(t, err)
err = os.Unsetenv("TEST_LEGO_VAR_MISSING_2")
require.NoError(t, err)

testCases := []struct {
desc string
main string
defaultValue string
alts []string
expected string
}{
{
desc: "with value and no alternative",
main: "TEST_LEGO_VAR_EXIST_1",
defaultValue: "oops",
expected: "VAR1",
},
{
desc: "with value and alternatives",
main: "TEST_LEGO_VAR_EXIST_1",
defaultValue: "oops",
alts: []string{"TEST_LEGO_VAR_MISSING_1"},
expected: "VAR1",
},
{
desc: "without value and no alternatives",
main: "TEST_LEGO_VAR_MISSING_1",
defaultValue: "oops",
expected: "oops",
},
{
desc: "without value and alternatives",
main: "TEST_LEGO_VAR_MISSING_1",
defaultValue: "oops",
alts: []string{"TEST_LEGO_VAR_EXIST_1"},
expected: "VAR1",
},
}

for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

value := GetOneWithFallback(test.main, test.defaultValue, ParseString, test.alts...)
assert.Equal(t, test.expected, value)
})
}
}

func TestGetOrDefaultInt(t *testing.T) {
testCases := []struct {
desc string
Expand Down
24 changes: 16 additions & 8 deletions providers/dns/liquidweb/liquidweb.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const defaultBaseURL = "https://api.liquidweb.com"

// Environment variables names.
const (
envNamespace = "LIQUID_WEB_"
envNamespace = "LIQUID_WEB_"
altEnvNamespace = "LWAPI_"

EnvURL = envNamespace + "URL"
EnvUsername = envNamespace + "USERNAME"
Expand Down Expand Up @@ -49,10 +50,10 @@ type Config struct {
func NewDefaultConfig() *Config {
return &Config{
BaseURL: defaultBaseURL,
TTL: env.GetOrDefaultInt(EnvTTL, 300),
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 2*time.Second),
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 1*time.Minute),
TTL: env.GetOneWithFallback(EnvTTL, 300, strconv.Atoi, altEnvName(EnvTTL)),
PropagationTimeout: env.GetOneWithFallback(EnvPropagationTimeout, 2*time.Minute, env.ParseSecond, altEnvName(EnvPropagationTimeout)),
PollingInterval: env.GetOneWithFallback(EnvPollingInterval, 2*time.Second, env.ParseSecond, altEnvName(EnvPollingInterval)),
HTTPTimeout: env.GetOneWithFallback(EnvHTTPTimeout, 1*time.Minute, env.ParseSecond, altEnvName(EnvHTTPTimeout)),
}
}

Expand All @@ -66,16 +67,19 @@ type DNSProvider struct {

// NewDNSProvider returns a DNSProvider instance configured for Liquid Web.
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get(EnvUsername, EnvPassword)
values, err := env.GetWithFallback(
[]string{EnvUsername, altEnvName(EnvUsername)},
[]string{EnvPassword, altEnvName(EnvPassword)},
)
if err != nil {
return nil, fmt.Errorf("liquidweb: %w", err)
}

config := NewDefaultConfig()
config.BaseURL = env.GetOrFile(EnvURL)
config.BaseURL = env.GetOneWithFallback(EnvURL, defaultBaseURL, env.ParseString, altEnvName(EnvURL))
config.Username = values[EnvUsername]
config.Password = values[EnvPassword]
config.Zone = env.GetOrDefaultString(EnvZone, "")
config.Zone = env.GetOneWithFallback(EnvZone, "", env.ParseString, altEnvName(EnvZone))

return NewDNSProviderConfig(config)
}
Expand Down Expand Up @@ -191,3 +195,7 @@ func (d *DNSProvider) findZone(domain string) (string, error) {

return zs[0].Name, nil
}

func altEnvName(v string) string {
return strings.ReplaceAll(v, envNamespace, altEnvNamespace)
}
20 changes: 10 additions & 10 deletions providers/dns/liquidweb/liquidweb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ Code = "liquidweb"
Since = "v3.1.0"

Example = '''
LIQUID_WEB_USERNAME=someuser \
LIQUID_WEB_PASSWORD=somepass \
LWAPI_USERNAME=someuser \
LWAPI_PASSWORD=somepass \
lego --email you@example.com --dns liquidweb --domains my.example.org run
'''

[Configuration]
[Configuration.Credentials]
LIQUID_WEB_USERNAME = "Liquid Web API Username"
LIQUID_WEB_PASSWORD = "Liquid Web API Password"
LWAPI_USERNAME = "Liquid Web API Username"
LWAPI_PASSWORD = "Liquid Web API Password"
[Configuration.Additional]
LIQUID_WEB_ZONE = "DNS Zone"
LIQUID_WEB_URL = "Liquid Web API endpoint"
LIQUID_WEB_TTL = "The TTL of the TXT record used for the DNS challenge"
LIQUID_WEB_POLLING_INTERVAL = "Time between DNS propagation check"
LIQUID_WEB_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
LIQUID_WEB_HTTP_TIMEOUT = "Maximum waiting time for the DNS records to be created (not verified)"
LWAPI_ZONE = "DNS Zone"
LWAPI_URL = "Liquid Web API endpoint"
LWAPI_TTL = "The TTL of the TXT record used for the DNS challenge"
LWAPI_POLLING_INTERVAL = "Time between DNS propagation check"
LWAPI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
LWAPI_HTTP_TIMEOUT = "Maximum waiting time for the DNS records to be created (not verified)"

[Links]
API = "https://api.liquidweb.com/docs/"
Expand Down

0 comments on commit 52990b3

Please sign in to comment.