github.com/braveheart12/just@v0.8.7/configuration/configuration.go (about)

     1  /*
     2   *    Copyright 2019 Insolar Technologies
     3   *
     4   *    Licensed under the Apache License, Version 2.0 (the "License");
     5   *    you may not use this file except in compliance with the License.
     6   *    You may obtain a copy of the License at
     7   *
     8   *        http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   *    Unless required by applicable law or agreed to in writing, software
    11   *    distributed under the License is distributed on an "AS IS" BASIS,
    12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *    See the License for the specific language governing permissions and
    14   *    limitations under the License.
    15   */
    16  
    17  package configuration
    18  
    19  import (
    20  	"fmt"
    21  	stdlog "log"
    22  	"reflect"
    23  	"strings"
    24  
    25  	"github.com/spf13/viper"
    26  	yaml "gopkg.in/yaml.v2"
    27  )
    28  
    29  // Configuration contains configuration params for all Insolar components
    30  type Configuration struct {
    31  	Host            HostNetwork
    32  	Service         ServiceNetwork
    33  	Ledger          Ledger
    34  	Log             Log
    35  	Metrics         Metrics
    36  	LogicRunner     LogicRunner
    37  	APIRunner       APIRunner
    38  	Pulsar          Pulsar
    39  	VersionManager  VersionManager
    40  	KeysPath        string
    41  	CertificatePath string
    42  	Tracer          Tracer
    43  }
    44  
    45  // Holder provides methods to manage configuration
    46  type Holder struct {
    47  	Configuration Configuration
    48  	viper         *viper.Viper
    49  }
    50  
    51  // NewConfiguration creates new default configuration
    52  func NewConfiguration() Configuration {
    53  	cfg := Configuration{
    54  		Host:            NewHostNetwork(),
    55  		Service:         NewServiceNetwork(),
    56  		Ledger:          NewLedger(),
    57  		Log:             NewLog(),
    58  		Metrics:         NewMetrics(),
    59  		LogicRunner:     NewLogicRunner(),
    60  		APIRunner:       NewAPIRunner(),
    61  		Pulsar:          NewPulsar(),
    62  		VersionManager:  NewVersionManager(),
    63  		KeysPath:        "./",
    64  		CertificatePath: "",
    65  		Tracer:          NewTracer(),
    66  	}
    67  
    68  	return cfg
    69  }
    70  
    71  // MustInit wrapper around Init function which panics on error.
    72  func (c *Holder) MustInit(required bool) *Holder {
    73  	_, err := c.Init(required)
    74  	if err != nil {
    75  		panic(err)
    76  	}
    77  	return c
    78  }
    79  
    80  // Init init all configuration data from config file and environment.
    81  //
    82  // Does not fail on not found config file if the 'required' flag set to false.
    83  func (c *Holder) Init(required bool) (*Holder, error) {
    84  	err := c.Load()
    85  	if err != nil {
    86  		if required {
    87  			return nil, err
    88  		}
    89  		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
    90  			return nil, err
    91  		}
    92  	}
    93  	err = c.LoadEnv()
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	return c, nil
    98  }
    99  
   100  // NewHolder creates new Holder with default configuration
   101  func NewHolder() *Holder {
   102  	cfg := NewConfiguration()
   103  	holder := &Holder{Configuration: cfg, viper: viper.New()}
   104  
   105  	holder.viper.SetConfigName(".insolar")
   106  	holder.viper.AddConfigPath(".")
   107  	holder.viper.SetConfigType("yml")
   108  
   109  	holder.viper.SetDefault("insolar", cfg)
   110  
   111  	holder.viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   112  	holder.viper.SetEnvPrefix("insolar")
   113  	return holder
   114  }
   115  
   116  // Load method reads configuration from default file path
   117  func (c *Holder) Load() error {
   118  	err := c.viper.ReadInConfig()
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	return c.viper.UnmarshalKey("insolar", &c.Configuration)
   124  }
   125  
   126  // LoadEnv overrides configuration with env variables
   127  func (c *Holder) LoadEnv() error {
   128  	// workaround for AutomaticEnv issue https://github.com/spf13/viper/issues/188
   129  	bindEnvs(c.viper, c.Configuration)
   130  	return c.viper.Unmarshal(&c.Configuration)
   131  }
   132  
   133  // LoadFromFile method reads configuration from particular file path
   134  func (c *Holder) LoadFromFile(path string) error {
   135  	c.viper.SetConfigFile(path)
   136  	return c.Load()
   137  }
   138  
   139  // Save method writes configuration to default file path
   140  func (c *Holder) Save() error {
   141  	c.viper.Set("insolar", c.Configuration)
   142  	return c.viper.WriteConfig()
   143  }
   144  
   145  // SaveAs method writes configuration to particular file path
   146  func (c *Holder) SaveAs(path string) error {
   147  	return c.viper.WriteConfigAs(path)
   148  }
   149  
   150  func bindEnvs(v *viper.Viper, iface interface{}, parts ...string) {
   151  	ifv := reflect.ValueOf(iface)
   152  	ift := reflect.TypeOf(iface)
   153  	for i := 0; i < ift.NumField(); i++ {
   154  		fieldv := ifv.Field(i)
   155  		t := ift.Field(i)
   156  		name := strings.ToLower(t.Name)
   157  		tag, ok := t.Tag.Lookup("mapstructure")
   158  		if ok {
   159  			name = tag
   160  		}
   161  		path := append(parts, name)
   162  		switch fieldv.Kind() {
   163  		case reflect.Struct:
   164  			bindEnvs(v, fieldv.Interface(), path...)
   165  		default:
   166  			err := v.BindEnv(strings.Join(path, "."))
   167  			if err != nil {
   168  				stdlog.Println("bindEnv failed:", err.Error())
   169  			}
   170  		}
   171  	}
   172  }
   173  
   174  // ToString converts any configuration struct to yaml string
   175  func ToString(in interface{}) string {
   176  	d, err := yaml.Marshal(in)
   177  	if err != nil {
   178  		return fmt.Sprintf("error: %v", err)
   179  	}
   180  	return string(d)
   181  }