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 }