forked from TrueCloudLab/lego
liquidweb: add LWAPI_ prefix for env vars (#2034)
This commit is contained in:
parent
8afdc9d01c
commit
52990b3c9e
4 changed files with 154 additions and 52 deletions
87
platform/config/env/env.go
vendored
87
platform/config/env/env.go
vendored
|
@ -78,15 +78,26 @@ 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
|
||||
}
|
||||
}
|
||||
|
@ -94,43 +105,32 @@ func getOneWithFallback(main string, names ...string) (string, string) {
|
|||
return "", main
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
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 {
|
||||
v, err := strconv.ParseBool(GetOrFile(envVar))
|
||||
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 {
|
||||
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 {
|
||||
return getOrDefault(envVar, defaultValue, ParseSecond)
|
||||
}
|
||||
|
||||
func getOrDefault[T any](envVar string, defaultValue T, fn func(string) (T, error)) T {
|
||||
v, err := fn(GetOrFile(envVar))
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
|
@ -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
platform/config/env/env_test.go
vendored
75
platform/config/env/env_test.go
vendored
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
|
|
@ -21,6 +21,7 @@ const defaultBaseURL = "https://api.liquidweb.com"
|
|||
// Environment variables names.
|
||||
const (
|
||||
envNamespace = "LIQUID_WEB_"
|
||||
altEnvNamespace = "LWAPI_"
|
||||
|
||||
EnvURL = envNamespace + "URL"
|
||||
EnvUsername = envNamespace + "USERNAME"
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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/"
|
||||
|
|
Loading…
Reference in a new issue