github.com/avenga/couper@v1.12.2/config/env/env.go (about)

     1  package env
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/avenga/couper/config"
    13  )
    14  
    15  const PREFIX = "COUPER_"
    16  
    17  var OsEnvironMu = sync.Mutex{}
    18  var OsEnviron = os.Environ
    19  
    20  func SetTestOsEnviron(f func() []string) {
    21  	OsEnvironMu.Lock()
    22  	defer OsEnvironMu.Unlock()
    23  
    24  	OsEnviron = f
    25  }
    26  
    27  func Decode(conf interface{}) {
    28  	DecodeWithPrefix(conf, "")
    29  }
    30  
    31  func DecodeWithPrefix(conf interface{}, prefix string) {
    32  	ctxPrefix := PREFIX + prefix
    33  	envMap := make(map[string]string)
    34  
    35  	OsEnvironMu.Lock()
    36  	envVars := OsEnviron()
    37  	OsEnvironMu.Unlock()
    38  
    39  	for _, v := range envVars {
    40  		key := strings.Split(v, "=")
    41  		if !strings.HasPrefix(key[0], ctxPrefix) {
    42  			continue
    43  		}
    44  		envMap[strings.ToLower(key[0][len(ctxPrefix):])] = key[1]
    45  	}
    46  
    47  	if len(envMap) == 0 {
    48  		return
    49  	}
    50  
    51  	val := reflect.ValueOf(conf)
    52  	if val.Kind() == reflect.Ptr {
    53  		val = val.Elem()
    54  	}
    55  	for i := 0; i < val.NumField(); i++ {
    56  		field := val.Type().Field(i)
    57  
    58  		switch val.Field(i).Kind() {
    59  		case reflect.Ptr:
    60  			continue
    61  		case reflect.Struct:
    62  			DecodeWithPrefix(val.Field(i).Interface(), prefix)
    63  		default:
    64  		}
    65  
    66  		envVal, ok := field.Tag.Lookup("env")
    67  		if !ok { // fallback to hcl struct tag
    68  			envVal, ok = field.Tag.Lookup("hcl")
    69  			if !ok {
    70  				continue
    71  			}
    72  		}
    73  		envVal = strings.Split(envVal, ",")[0]
    74  
    75  		mapVal, exist := envMap[envVal]
    76  		if !exist || mapVal == "" {
    77  			continue
    78  		}
    79  
    80  		variableName := strings.ToUpper(PREFIX + envVal)
    81  		switch val.Field(i).Interface().(type) {
    82  		case bool:
    83  			val.Field(i).SetBool(mapVal == "true")
    84  		case int:
    85  			intVal, err := strconv.Atoi(mapVal)
    86  			if err != nil {
    87  				fmt.Fprintf(os.Stderr, "Invalid integer value for %q: %s\n", variableName, mapVal)
    88  				os.Exit(1)
    89  			}
    90  			val.Field(i).SetInt(int64(intVal))
    91  		case string:
    92  			val.Field(i).SetString(mapVal)
    93  		case []string, config.List:
    94  			slice := strings.Split(mapVal, ",")
    95  			for idx, v := range slice {
    96  				slice[idx] = strings.TrimSpace(v)
    97  			}
    98  			val.Field(i).Set(reflect.ValueOf(slice))
    99  		case time.Duration:
   100  			parsedDuration, err := time.ParseDuration(mapVal)
   101  			if err != nil {
   102  				fmt.Fprintf(os.Stderr, "Invalid duration value for %q: %s\n", variableName, mapVal)
   103  				os.Exit(1)
   104  			}
   105  			val.Field(i).Set(reflect.ValueOf(parsedDuration))
   106  		default:
   107  			panic(fmt.Sprintf("env decode: type mapping not implemented: %v", reflect.TypeOf(val.Field(i).Interface())))
   108  		}
   109  	}
   110  }