162 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package config
 | |
| 
 | |
| import (
 | |
| 	"flag"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"reflect"
 | |
| 	"strconv"
 | |
| 	"time"
 | |
| 
 | |
| 	"crypto/rand"
 | |
| 	"encoding/hex"
 | |
| 	"io/ioutil"
 | |
| 
 | |
| 	"gopkg.in/yaml.v2"
 | |
| )
 | |
| 
 | |
| // getEnvString returns string from environment variable
 | |
| func getEnvString(env, def string) (val string) { //nolint:deadcode
 | |
| 	val = os.Getenv(env)
 | |
| 
 | |
| 	if val == "" {
 | |
| 		return def
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // getEnvInt returns int from environment variable
 | |
| func getEnvInt(env string, def int) (ret int) {
 | |
| 	val := os.Getenv(env)
 | |
| 
 | |
| 	if val == "" {
 | |
| 		return def
 | |
| 	}
 | |
| 
 | |
| 	ret, err := strconv.Atoi(val)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("[ERROR] Environment variable is not numeric: %v\n", env)
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // getEnvBool returns boolean from environment variable
 | |
| func getEnvBool(env string, def bool) bool {
 | |
| 	var (
 | |
| 		err    error
 | |
| 		retVal bool
 | |
| 		val    = os.Getenv(env)
 | |
| 	)
 | |
| 
 | |
| 	if len(val) == 0 {
 | |
| 		return def
 | |
| 	} else {
 | |
| 		retVal, err = strconv.ParseBool(val)
 | |
| 		if err != nil {
 | |
| 			log.Fatalf("[ERROR] Environment variable is not boolean: %v\n", env)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return retVal
 | |
| }
 | |
| 
 | |
| // Init initializes the application configuration by reading default values from the struct's tags
 | |
| // and environment variables. Tags processed by this process are as follows:
 | |
| // `ignored:"true" env:"ENVIRONMENT_VARIABLE" default:"default value"`
 | |
| func Init() *Config {
 | |
| 	var cryptovault string
 | |
| 	cfg := DefaultConfig()
 | |
| 
 | |
| 	cfgInfo, err := getStructInfo(cfg)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("[FATAL] %v", err)
 | |
| 	}
 | |
| 
 | |
| 	for _, info := range cfgInfo {
 | |
| 		switch info.Type.String() {
 | |
| 		case "string":
 | |
| 			var dv string
 | |
| 
 | |
| 			if info.DefaultValue != nil {
 | |
| 				dv = info.DefaultValue.(string)
 | |
| 			}
 | |
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*string)
 | |
| 			flag.StringVar(p, info.Name, getEnvString(info.Name, dv), "("+info.Key+")")
 | |
| 		case "bool":
 | |
| 			var dv bool
 | |
| 
 | |
| 			if info.DefaultValue != nil {
 | |
| 				dv = info.DefaultValue.(bool)
 | |
| 			}
 | |
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool)
 | |
| 			flag.BoolVar(p, info.Name, getEnvBool(info.Name, dv), "("+info.Key+")")
 | |
| 		case "int":
 | |
| 			var dv int
 | |
| 
 | |
| 			if info.DefaultValue != nil {
 | |
| 				dv = int(info.DefaultValue.(int64))
 | |
| 			}
 | |
| 			p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int)
 | |
| 			flag.IntVar(p, info.Name, getEnvInt(info.Name, dv), "("+info.Key+")")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	flag.StringVar(&cryptovault, "label", "", "Deprecated support feature. Please use -SecretID.\nCryptovault compatibility feature used to identify the ID of the secret")
 | |
| 	flag.Parse()
 | |
| 
 | |
| 	if len(cryptovault) != 0 {
 | |
| 		cfg.SecretID = cryptovault
 | |
| 	}
 | |
| 
 | |
| 	// set logging level
 | |
| 	cfg.setLogLevel()
 | |
| 
 | |
| 	// check if configuration file is present, if not create the example config file
 | |
| 	if _, err := os.Stat(cfg.ConfigFile); os.IsNotExist(err) {
 | |
| 		log.Printf("[WARNING] No configuration file present. Creating example configuration file at %s", cfg.ConfigFile)
 | |
| 		file, err := os.Create(cfg.ConfigFile)
 | |
| 		if err != nil {
 | |
| 			log.Fatalf("[ERROR] Unable to create the config file %s: %v", cfg.ConfigFile, err)
 | |
| 		}
 | |
| 		defer file.Close()
 | |
| 
 | |
| 		file.Write([]byte(cfg.ConfigFileExample))
 | |
| 	}
 | |
| 
 | |
| 	// readConfiguration File
 | |
| 	fileData, err := ioutil.ReadFile(cfg.ConfigFile)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("[ERROR] Unable to read the config file %s: %v", cfg.ConfigFile, err)
 | |
| 	}
 | |
| 	err = yaml.Unmarshal(fileData, &cfg.ConfigFileData)
 | |
| 	if err != nil {
 | |
| 		log.Printf("[ERROR] Unable to process the config file %s: %v", cfg.ConfigFile, err)
 | |
| 	}
 | |
| 
 | |
| 	// make sure there is a secure salt
 | |
| 	if cfg.ConfigFileData.Salt == "" || cfg.ConfigFileData.Salt == "exampleSalt" {
 | |
| 		s := make([]byte, 32)
 | |
| 		rand.Read(s)
 | |
| 		cfg.ConfigFileData.Salt = hex.EncodeToString(s)
 | |
| 	}
 | |
| 
 | |
| 	// timezone & format configuration
 | |
| 	cfg.TZoneUTC, _ = time.LoadLocation("UTC")
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones")
 | |
| 	}
 | |
| 	cfg.TZoneLocal, err = time.LoadLocation(cfg.TimeZoneLocal)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("[ERROR] Unable to parse timezone string. Please use one of the timezone database values listed here: %s", "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones")
 | |
| 	}
 | |
| 	time.Now().Format(cfg.TimeFormat)
 | |
| 
 | |
| 	// print running config
 | |
| 	cfg.printRunningConfig(cfgInfo)
 | |
| 
 | |
| 	log.Println("[INFO] initialization complete")
 | |
| 	return cfg
 | |
| }
 |