github.com/vseinstrumentiru/lego@v1.0.2/internal/lego/config/provider.go (about)

     1  package config
     2  
     3  import (
     4  	"emperror.dev/emperror"
     5  	"emperror.dev/errors"
     6  	"fmt"
     7  	dynamicstruct "github.com/ompluscator/dynamic-struct"
     8  	"github.com/spf13/pflag"
     9  	"github.com/spf13/viper"
    10  	"github.com/vseinstrumentiru/lego/internal/lego"
    11  	"github.com/vseinstrumentiru/lego/internal/lego/build"
    12  	"os"
    13  	"reflect"
    14  	"strings"
    15  )
    16  
    17  const (
    18  	defaultEnvPrefix = "app"
    19  )
    20  
    21  type ErrConfigFileNotFound = viper.ConfigFileNotFoundError
    22  
    23  func IsFileNotFound(err error) bool {
    24  	_, ok := err.(viper.ConfigFileNotFoundError)
    25  
    26  	return ok
    27  }
    28  
    29  func configure() (*viper.Viper, *pflag.FlagSet) {
    30  	v, p := viper.New(), pflag.NewFlagSet("lego", pflag.ExitOnError)
    31  
    32  	v.AddConfigPath(".")
    33  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
    34  	v.AllowEmptyEnv(true)
    35  	v.AutomaticEnv()
    36  
    37  	p.String("config", "", "Configuration file")
    38  	p.String("config-path", "", "Search path for configuration file")
    39  	p.Bool("version", false, "Show version information")
    40  
    41  	v.AddConfigPath(".")
    42  
    43  	return v, p
    44  }
    45  
    46  func prepareCustomConfig(customCfg lego.Config) (lego.Config, bool) {
    47  	if customCfg == lego.Config(nil) {
    48  		return nil, false
    49  	}
    50  
    51  	cfgVal := reflect.ValueOf(customCfg)
    52  	if !cfgVal.IsValid() {
    53  		emperror.Panic(errors.New("config is nil pointer"))
    54  	}
    55  
    56  	return customCfg, true
    57  }
    58  
    59  func buildConfig(v *viper.Viper, customCfg lego.Config, customCfgPrefix string) (cfg Config, err error) {
    60  	builder := dynamicstruct.NewStruct().
    61  		AddField("Srv", Config{}, "")
    62  
    63  	var hasCustomCfg bool
    64  
    65  	if customCfg != nil {
    66  		hasCustomCfg = true
    67  		envPrefix := customCfgPrefix
    68  
    69  		if cApp, ok := customCfg.(lego.ConfigWithCustomEnvPrefix); ok {
    70  			envPrefix = cApp.GetEnvPrefix()
    71  		}
    72  		v.AddConfigPath(fmt.Sprintf("$%s_CONFIG_DIR/", strings.ToUpper(envPrefix)))
    73  
    74  		builder = builder.AddField("Custom", customCfg, fmt.Sprintf(`mapstructure:"%s"`, customCfgPrefix))
    75  	}
    76  
    77  	unmarshalStruct := builder.Build().New()
    78  	if err = v.Unmarshal(&unmarshalStruct); err != nil {
    79  		return Config{}, err
    80  	}
    81  
    82  	structReflect := reflect.ValueOf(unmarshalStruct).Elem()
    83  	cfg = structReflect.FieldByName("Srv").Interface().(Config)
    84  	if hasCustomCfg {
    85  		cfg.Custom = structReflect.FieldByName("Custom").Interface().(lego.Config)
    86  	}
    87  
    88  	cfg.Build = build.New()
    89  
    90  	return cfg, nil
    91  }
    92  
    93  type Option func(env *viper.Viper, flags *pflag.FlagSet)
    94  
    95  func WithDefaultName(name string) Option {
    96  	return func(env *viper.Viper, flags *pflag.FlagSet) {
    97  		env.SetDefault("srv.name", name)
    98  	}
    99  }
   100  
   101  func Provide(customCfg lego.Config, options ...Option) (Config, error) {
   102  	v, p := configure()
   103  
   104  	for _, opt := range options {
   105  		opt(v, p)
   106  	}
   107  
   108  	_ = p.Parse(os.Args[1:])
   109  
   110  	configBySrvName := false
   111  
   112  	if c, _ := p.GetString("config"); c != "" {
   113  		v.SetConfigFile(c)
   114  	} else if c, _ := p.GetString("config-path"); c != "" {
   115  		v.AddConfigPath(c)
   116  	} else if name := v.GetString("srv.name"); name != "" {
   117  		v.SetConfigName("config." + name)
   118  		configBySrvName = true
   119  	}
   120  
   121  	returnErr := v.ReadInConfig()
   122  
   123  	if IsFileNotFound(returnErr) && configBySrvName {
   124  		v.SetConfigName("config")
   125  		returnErr = v.ReadInConfig()
   126  	}
   127  
   128  	if !IsFileNotFound(returnErr) {
   129  		emperror.Panic(errors.Wrap(returnErr, "failed to read configuration"))
   130  	}
   131  
   132  	(Config{}).SetDefaults(v, p)
   133  
   134  	var hasAppCfg bool
   135  	customCfg, hasAppCfg = prepareCustomConfig(customCfg)
   136  
   137  	if hasAppCfg {
   138  		customCfg.SetDefaults(v, p)
   139  	}
   140  
   141  	cfg, err := buildConfig(v, customCfg, defaultEnvPrefix)
   142  	emperror.Panic(errors.Wrap(err, "failed to unmarshal application configuration"))
   143  
   144  	if v, _ := p.GetBool("version"); v {
   145  		fmt.Printf("%s version %s (%s) built on %s\n", cfg.Name, cfg.Build.Version, cfg.Build.CommitHash, cfg.Build.BuildDate)
   146  
   147  		os.Exit(0)
   148  	}
   149  
   150  	return cfg, returnErr
   151  }