package config import ( "flag" "fmt" "os" "reflect" "strconv" "strings" ) type structInfo struct { Name string Alt string Info string Key string Field reflect.Value Tags reflect.StructTag Type reflect.Type DefaultValue interface{} Secret interface{} } // getEnvString returns string from environment variable func getEnvString(env string, def string) string { val := os.Getenv(env) if len(val) == 0 { return def } return "" } // getEnvBool returns boolean from environment variable func getEnvBool(env string, def bool) (bool, error) { val := os.Getenv(env) if len(val) == 0 { return def, nil } ret, err := strconv.ParseBool(val) if err != nil { return false, fmt.Errorf("Environment variable is not of type bool: %v", env) } return ret, nil } // getEnvInt returns int from environment variable func getEnvInt(env string, def int) (int, error) { val := os.Getenv(env) if len(val) == 0 { return def, nil } ret, err := strconv.Atoi(val) if err != nil { return 0, fmt.Errorf("Environment variable is not of type int: %v", env) } return ret, nil } // getEnvInt64 return int64 from environment variable func getEnvInt64(env string, def int64) (int64, error) { val := os.Getenv(env) if len(val) == 0 { return def, nil } ret, err := strconv.ParseInt(val, 10, 64) if err != nil { return 0, fmt.Errorf("Environment variable is not of type int64: %v", env) } return ret, nil } // getEnvFloat64 return float64 from environment variable func getEnvFloat64(env string, def float64) (float64, error) { val := os.Getenv(env) if len(val) == 0 { return def, nil } ret, err := strconv.ParseFloat(val, 64) if err != nil { return 0, fmt.Errorf("Environment variable is not of type float64: %v", env) } return ret, nil } func getStructInfo(spec interface{}) ([]structInfo, error) { s := reflect.ValueOf(spec) if s.Kind() != reflect.Pointer { return []structInfo{}, fmt.Errorf("getStructInfo() was sent a %s instead of a pointer to a struct.\n", s.Kind()) } s = s.Elem() if s.Kind() != reflect.Struct { return []structInfo{}, fmt.Errorf("getStructInfo() was sent a %s instead of a struct.\n", s.Kind()) } typeOfSpec := s.Type() infos := make([]structInfo, 0, s.NumField()) for i := 0; i < s.NumField(); i++ { f := s.Field(i) ftype := typeOfSpec.Field(i) ignored, _ := strconv.ParseBool(ftype.Tag.Get("ignored")) if !f.CanSet() || ignored { continue } for f.Kind() == reflect.Pointer { if f.IsNil() { if f.Type().Elem().Kind() != reflect.Struct { break } f.Set(reflect.New(f.Type().Elem())) } f = f.Elem() } secret, err := typeConversion(ftype.Type.String(), ftype.Tag.Get("secret")) if err != nil { secret = false } var desc string if len(ftype.Tag.Get("info")) != 0 { desc = fmt.Sprintf("(%s) %s", strings.ToUpper(ftype.Tag.Get("env")), ftype.Tag.Get("info")) } else { desc = fmt.Sprintf("(%s)", strings.ToUpper(ftype.Tag.Get("env"))) } info := structInfo{ Name: ftype.Name, Alt: strings.ToUpper(ftype.Tag.Get("env")), Info: desc, Key: ftype.Name, Field: f, Tags: ftype.Tag, Type: ftype.Type, Secret: secret, } if info.Alt != "" { info.Key = info.Alt } info.Key = strings.ToUpper(info.Key) if ftype.Tag.Get("default") != "" { v, err := typeConversion(ftype.Type.String(), ftype.Tag.Get("default")) if err != nil { return []structInfo{}, err } info.DefaultValue = v } infos = append(infos, info) } return infos, nil } func typeConversion(t, v string) (interface{}, error) { switch t { case "string": //nolint:goconst return v, nil case "int": //nolint:goconst return strconv.ParseInt(v, 10, 0) case "int8": return strconv.ParseInt(v, 10, 8) case "int16": return strconv.ParseInt(v, 10, 16) case "int32": return strconv.ParseInt(v, 10, 32) case "int64": return strconv.ParseInt(v, 10, 64) case "uint": return strconv.ParseUint(v, 10, 0) case "uint16": return strconv.ParseUint(v, 10, 16) case "uint32": return strconv.ParseUint(v, 10, 32) case "uint64": return strconv.ParseUint(v, 10, 64) case "float32": return strconv.ParseFloat(v, 32) case "float64": return strconv.ParseFloat(v, 64) case "complex64": return strconv.ParseComplex(v, 64) case "complex128": return strconv.ParseComplex(v, 128) case "bool": //nolint:goconst return strconv.ParseBool(v) } return nil, fmt.Errorf("Unable to identify type.") } func (cfg *Config) parseFlags(cfgInfo []structInfo) { 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.Alt, dv), info.Info) case "bool": var dv bool if info.DefaultValue != nil { dv = info.DefaultValue.(bool) } p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*bool) retVal, err := getEnvBool(info.Alt, dv) if err != nil { cfg.Log.Error("Error Encountered", "error", err) os.Exit(1) } flag.BoolVar(p, info.Name, retVal, info.Info) case "int": var dv int if info.DefaultValue != nil { dv = info.DefaultValue.(int) } p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int) retVal, err := getEnvInt(info.Alt, dv) if err != nil { cfg.Log.Error("Error Encountered", "error", err) os.Exit(1) } flag.IntVar(p, info.Name, retVal, info.Info) case "int64": var dv int64 if info.DefaultValue != nil { dv = info.DefaultValue.(int64) } p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*int64) retVal, err := getEnvInt64(info.Alt, dv) if err != nil { cfg.Log.Error("Error Encountered", "error", err) os.Exit(1) } flag.Int64Var(p, info.Name, retVal, info.Info) case "float64": var dv float64 if info.DefaultValue != nil { dv = info.DefaultValue.(float64) } p := reflect.ValueOf(cfg).Elem().FieldByName(info.Name).Addr().Interface().(*float64) retVal, err := getEnvFloat64(info.Alt, dv) if err != nil { cfg.Log.Error("Error Encountered", "error", err) os.Exit(1) } flag.Float64Var(p, info.Name, retVal, info.Info) } } flag.Parse() }