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
|
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) {
|
func getOneWithFallback(main string, names ...string) (string, string) {
|
||||||
value := GetOrFile(main)
|
value := GetOrFile(main)
|
||||||
if len(value) > 0 {
|
if value != "" {
|
||||||
return value, main
|
return value, main
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
value := GetOrFile(name)
|
value := GetOrFile(name)
|
||||||
if len(value) > 0 {
|
if value != "" {
|
||||||
return value, main
|
return value, main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,43 +105,32 @@ func getOneWithFallback(main string, names ...string) (string, string) {
|
||||||
return "", main
|
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.
|
// GetOrDefaultString returns the given environment variable value as a string.
|
||||||
// Returns the default if the env var cannot be found.
|
// Returns the default if the env var cannot be found.
|
||||||
func GetOrDefaultString(envVar, defaultValue string) string {
|
func GetOrDefaultString(envVar string, defaultValue string) string {
|
||||||
v := GetOrFile(envVar)
|
return getOrDefault(envVar, defaultValue, ParseString)
|
||||||
if v == "" {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 env var cannot be coopered to a boolean, or is not found.
|
// Returns the default if the env var 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(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 {
|
if err != nil {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
@ -161,3 +161,26 @@ func GetOrFile(envVar string) string {
|
||||||
|
|
||||||
return strings.TrimSuffix(string(fileContents), "\n")
|
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")
|
var1Missing := os.Getenv("TEST_LEGO_VAR_MISSING_1")
|
||||||
var2Missing := os.Getenv("TEST_LEGO_VAR_MISSING_2")
|
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_1", var1Exist)
|
||||||
_ = os.Setenv("TEST_LEGO_VAR_EXIST_2", var2Exist)
|
_ = os.Setenv("TEST_LEGO_VAR_EXIST_2", var2Exist)
|
||||||
_ = os.Setenv("TEST_LEGO_VAR_MISSING_1", var1Missing)
|
_ = os.Setenv("TEST_LEGO_VAR_MISSING_1", var1Missing)
|
||||||
_ = os.Setenv("TEST_LEGO_VAR_MISSING_2", var2Missing)
|
_ = os.Setenv("TEST_LEGO_VAR_MISSING_2", var2Missing)
|
||||||
}()
|
})
|
||||||
|
|
||||||
err := os.Setenv("TEST_LEGO_VAR_EXIST_1", "VAR1")
|
err := os.Setenv("TEST_LEGO_VAR_EXIST_1", "VAR1")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -93,7 +93,10 @@ func TestGetWithFallback(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
value, err := GetWithFallback(test.groups...)
|
value, err := GetWithFallback(test.groups...)
|
||||||
if len(test.expected.error) > 0 {
|
if len(test.expected.error) > 0 {
|
||||||
assert.EqualError(t, err, test.expected.error)
|
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) {
|
func TestGetOrDefaultInt(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
|
@ -21,6 +21,7 @@ const defaultBaseURL = "https://api.liquidweb.com"
|
||||||
// Environment variables names.
|
// Environment variables names.
|
||||||
const (
|
const (
|
||||||
envNamespace = "LIQUID_WEB_"
|
envNamespace = "LIQUID_WEB_"
|
||||||
|
altEnvNamespace = "LWAPI_"
|
||||||
|
|
||||||
EnvURL = envNamespace + "URL"
|
EnvURL = envNamespace + "URL"
|
||||||
EnvUsername = envNamespace + "USERNAME"
|
EnvUsername = envNamespace + "USERNAME"
|
||||||
|
@ -49,10 +50,10 @@ type Config struct {
|
||||||
func NewDefaultConfig() *Config {
|
func NewDefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
BaseURL: defaultBaseURL,
|
BaseURL: defaultBaseURL,
|
||||||
TTL: env.GetOrDefaultInt(EnvTTL, 300),
|
TTL: env.GetOneWithFallback(EnvTTL, 300, strconv.Atoi, altEnvName(EnvTTL)),
|
||||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
PropagationTimeout: env.GetOneWithFallback(EnvPropagationTimeout, 2*time.Minute, env.ParseSecond, altEnvName(EnvPropagationTimeout)),
|
||||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 2*time.Second),
|
PollingInterval: env.GetOneWithFallback(EnvPollingInterval, 2*time.Second, env.ParseSecond, altEnvName(EnvPollingInterval)),
|
||||||
HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 1*time.Minute),
|
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.
|
// NewDNSProvider returns a DNSProvider instance configured for Liquid Web.
|
||||||
func NewDNSProvider() (*DNSProvider, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("liquidweb: %w", err)
|
return nil, fmt.Errorf("liquidweb: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := NewDefaultConfig()
|
config := NewDefaultConfig()
|
||||||
config.BaseURL = env.GetOrFile(EnvURL)
|
config.BaseURL = env.GetOneWithFallback(EnvURL, defaultBaseURL, env.ParseString, altEnvName(EnvURL))
|
||||||
config.Username = values[EnvUsername]
|
config.Username = values[EnvUsername]
|
||||||
config.Password = values[EnvPassword]
|
config.Password = values[EnvPassword]
|
||||||
config.Zone = env.GetOrDefaultString(EnvZone, "")
|
config.Zone = env.GetOneWithFallback(EnvZone, "", env.ParseString, altEnvName(EnvZone))
|
||||||
|
|
||||||
return NewDNSProviderConfig(config)
|
return NewDNSProviderConfig(config)
|
||||||
}
|
}
|
||||||
|
@ -191,3 +195,7 @@ func (d *DNSProvider) findZone(domain string) (string, error) {
|
||||||
|
|
||||||
return zs[0].Name, nil
|
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"
|
Since = "v3.1.0"
|
||||||
|
|
||||||
Example = '''
|
Example = '''
|
||||||
LIQUID_WEB_USERNAME=someuser \
|
LWAPI_USERNAME=someuser \
|
||||||
LIQUID_WEB_PASSWORD=somepass \
|
LWAPI_PASSWORD=somepass \
|
||||||
lego --email you@example.com --dns liquidweb --domains my.example.org run
|
lego --email you@example.com --dns liquidweb --domains my.example.org run
|
||||||
'''
|
'''
|
||||||
|
|
||||||
[Configuration]
|
[Configuration]
|
||||||
[Configuration.Credentials]
|
[Configuration.Credentials]
|
||||||
LIQUID_WEB_USERNAME = "Liquid Web API Username"
|
LWAPI_USERNAME = "Liquid Web API Username"
|
||||||
LIQUID_WEB_PASSWORD = "Liquid Web API Password"
|
LWAPI_PASSWORD = "Liquid Web API Password"
|
||||||
[Configuration.Additional]
|
[Configuration.Additional]
|
||||||
LIQUID_WEB_ZONE = "DNS Zone"
|
LWAPI_ZONE = "DNS Zone"
|
||||||
LIQUID_WEB_URL = "Liquid Web API endpoint"
|
LWAPI_URL = "Liquid Web API endpoint"
|
||||||
LIQUID_WEB_TTL = "The TTL of the TXT record used for the DNS challenge"
|
LWAPI_TTL = "The TTL of the TXT record used for the DNS challenge"
|
||||||
LIQUID_WEB_POLLING_INTERVAL = "Time between DNS propagation check"
|
LWAPI_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||||
LIQUID_WEB_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
LWAPI_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_HTTP_TIMEOUT = "Maximum waiting time for the DNS records to be created (not verified)"
|
||||||
|
|
||||||
[Links]
|
[Links]
|
||||||
API = "https://api.liquidweb.com/docs/"
|
API = "https://api.liquidweb.com/docs/"
|
||||||
|
|
Loading…
Reference in a new issue