2019-03-11 16:56:48 +00:00
|
|
|
package env
|
2018-06-11 15:32:50 +00:00
|
|
|
|
|
|
|
import (
|
2018-10-02 20:34:34 +00:00
|
|
|
"errors"
|
2018-06-11 15:32:50 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
2018-09-08 11:49:24 +00:00
|
|
|
"strconv"
|
2018-06-11 15:32:50 +00:00
|
|
|
"strings"
|
2018-09-15 17:07:24 +00:00
|
|
|
"time"
|
2018-10-06 13:33:15 +00:00
|
|
|
|
2020-09-02 01:20:01 +00:00
|
|
|
"github.com/go-acme/lego/v4/log"
|
2018-06-11 15:32:50 +00:00
|
|
|
)
|
|
|
|
|
2020-05-08 17:35:25 +00:00
|
|
|
// Get environment variables.
|
2018-06-11 15:32:50 +00:00
|
|
|
func Get(names ...string) (map[string]string, error) {
|
|
|
|
values := map[string]string{}
|
|
|
|
|
|
|
|
var missingEnvVars []string
|
|
|
|
for _, envVar := range names {
|
2018-10-06 13:33:15 +00:00
|
|
|
value := GetOrFile(envVar)
|
2018-06-11 15:32:50 +00:00
|
|
|
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
|
|
|
|
}
|
2018-09-08 11:49:24 +00:00
|
|
|
|
2020-11-24 08:38:11 +00:00
|
|
|
// GetWithFallback Get environment variable values.
|
|
|
|
// The first name in each group is use as key in the result map.
|
|
|
|
//
|
|
|
|
// case 1:
|
2018-10-02 20:34:34 +00:00
|
|
|
//
|
|
|
|
// // LEGO_ONE="ONE"
|
|
|
|
// // LEGO_TWO="TWO"
|
|
|
|
// env.GetWithFallback([]string{"LEGO_ONE", "LEGO_TWO"})
|
|
|
|
// // => "LEGO_ONE" = "ONE"
|
|
|
|
//
|
2020-11-24 08:38:11 +00:00
|
|
|
// case 2:
|
2018-10-02 20:34:34 +00:00
|
|
|
//
|
|
|
|
// // LEGO_ONE=""
|
|
|
|
// // LEGO_TWO="TWO"
|
|
|
|
// env.GetWithFallback([]string{"LEGO_ONE", "LEGO_TWO"})
|
|
|
|
// // => "LEGO_ONE" = "TWO"
|
|
|
|
//
|
2020-11-24 08:38:11 +00:00
|
|
|
// case 3:
|
2018-10-02 20:34:34 +00:00
|
|
|
//
|
|
|
|
// // LEGO_ONE=""
|
|
|
|
// // LEGO_TWO=""
|
|
|
|
// env.GetWithFallback([]string{"LEGO_ONE", "LEGO_TWO"})
|
|
|
|
// // => error
|
|
|
|
func GetWithFallback(groups ...[]string) (map[string]string, error) {
|
|
|
|
values := map[string]string{}
|
|
|
|
|
|
|
|
var missingEnvVars []string
|
|
|
|
for _, names := range groups {
|
|
|
|
if len(names) == 0 {
|
|
|
|
return nil, errors.New("undefined environment variable names")
|
|
|
|
}
|
|
|
|
|
|
|
|
value, envVar := getOneWithFallback(names[0], names[1:]...)
|
2021-03-04 19:16:59 +00:00
|
|
|
if value == "" {
|
2018-10-02 20:34:34 +00:00
|
|
|
missingEnvVars = append(missingEnvVars, envVar)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
values[envVar] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(missingEnvVars) > 0 {
|
|
|
|
return nil, fmt.Errorf("some credentials information are missing: %s", strings.Join(missingEnvVars, ","))
|
|
|
|
}
|
|
|
|
|
|
|
|
return values, nil
|
|
|
|
}
|
|
|
|
|
2023-10-14 23:12:55 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-10-02 20:34:34 +00:00
|
|
|
func getOneWithFallback(main string, names ...string) (string, string) {
|
2018-10-06 13:33:15 +00:00
|
|
|
value := GetOrFile(main)
|
2023-10-14 23:12:55 +00:00
|
|
|
if value != "" {
|
2018-10-02 20:34:34 +00:00
|
|
|
return value, main
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range names {
|
2018-10-06 13:33:15 +00:00
|
|
|
value := GetOrFile(name)
|
2023-10-14 23:12:55 +00:00
|
|
|
if value != "" {
|
2018-10-02 20:34:34 +00:00
|
|
|
return value, main
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", main
|
|
|
|
}
|
|
|
|
|
2023-10-14 23:12:55 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2018-09-08 11:49:24 +00:00
|
|
|
// GetOrDefaultInt returns the given environment variable value as an integer.
|
2023-07-29 10:59:24 +00:00
|
|
|
// Returns the default if the env var cannot be coopered to an int, or is not found.
|
2018-09-08 11:49:24 +00:00
|
|
|
func GetOrDefaultInt(envVar string, defaultValue int) int {
|
2023-10-14 23:12:55 +00:00
|
|
|
return getOrDefault(envVar, defaultValue, strconv.Atoi)
|
2018-09-08 11:49:24 +00:00
|
|
|
}
|
2018-09-15 17:07:24 +00:00
|
|
|
|
2023-07-29 10:59:24 +00:00
|
|
|
// 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.
|
2018-09-15 17:07:24 +00:00
|
|
|
func GetOrDefaultSecond(envVar string, defaultValue time.Duration) time.Duration {
|
2023-10-14 23:12:55 +00:00
|
|
|
return getOrDefault(envVar, defaultValue, ParseSecond)
|
2018-09-15 17:07:24 +00:00
|
|
|
}
|
|
|
|
|
2023-10-14 23:12:55 +00:00
|
|
|
func getOrDefault[T any](envVar string, defaultValue T, fn func(string) (T, error)) T {
|
|
|
|
v, err := fn(GetOrFile(envVar))
|
2018-09-15 17:07:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return defaultValue
|
|
|
|
}
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
2018-10-06 13:33:15 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2021-08-25 09:44:11 +00:00
|
|
|
fileContents, err := os.ReadFile(fileVarValue)
|
2018-10-06 13:33:15 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Failed to read the file %s (defined by env var %s): %s", fileVarValue, fileVar, err)
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2019-05-09 11:58:24 +00:00
|
|
|
return strings.TrimSuffix(string(fileContents), "\n")
|
2018-10-06 13:33:15 +00:00
|
|
|
}
|
2023-10-14 23:12:55 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|