github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/docs/v2/design/framework/config.md (about)

     1  # Overview
     2  
     3  Most config in applications are statically configured or include complex logic to load from multiple sources. 
     4  Go-config makes this easy, pluggable and mergeable. You'll never have to deal with config in the same way again.
     5  
     6  ## Features
     7  
     8  - **Dynamic Loading** - Load configuration from multiple source as and when needed. Go Config manages watching config sources
     9  in the background and automatically merges and updates an in memory view. 
    10  
    11  - **Pluggable Sources** - Choose from any number of sources to load and merge config. The backend source is abstracted away into 
    12  a standard format consumed internally and decoded via encoders. Sources can be env vars, flags, file, etcd, k8s configmap, etc.
    13  
    14  - **Mergeable Config** - If you specify multiple sources of config, regardless of format, they will be merged and presented in 
    15  a single view. This massively simplifies priority order loading and changes based on environment.
    16  
    17  - **Observe Changes** - Optionally watch the config for changes to specific values. Hot reload your app using Go Config's watcher.
    18  You don't have to handle ad-hoc hup reloading or whatever else, just keep reading the config and watch for changes if you need 
    19  to be notified.
    20  
    21  - **Safe Recovery** - In case config loads badly or is completely wiped away for some unknown reason, you can specify fallback 
    22  values when accessing any config values directly. This ensures you'll always be reading something default in the event of a problem.
    23  
    24  ## Getting Started
    25  
    26  - [Source](#source) - A backend from which config is loaded
    27  - [Encoder](#encoder) - Handles encoding/decoding source config 
    28  - [Reader](#reader) - Merges multiple encoded sources as a single format
    29  - [Config](#config) - Config manager which manages multiple sources 
    30  - [Secrets](#secrets) - Support for loading encoding secrets in config
    31  - [Usage](#usage) - Example usage of go-config
    32  - [FAQ](#faq) - General questions and answers
    33  - [TODO](#todo) - TODO tasks/features
    34  
    35  ## Sources
    36  
    37  A `Source` is a backend from which config is loaded. Multiple sources can be used at the same time.
    38  
    39  The following sources are supported:
    40  
    41  - [cli](https://github.com/micro/go-micro/tree/master/config/source/cli) - read from parsed CLI flags
    42  - [consul](https://github.com/micro/go-micro/tree/master/config/source/consul) - read from consul
    43  - [env](https://github.com/micro/go-micro/tree/master/config/source/env) - read from environment variables
    44  - [etcd](https://github.com/micro/go-micro/tree/master/config/source/etcd) - read from etcd v3
    45  - [file](https://github.com/micro/go-micro/tree/master/config/source/file) - read from file
    46  - [flag](https://github.com/micro/go-micro/tree/master/config/source/flag) - read from flags
    47  - [memory](https://github.com/micro/go-micro/tree/master/config/source/memory) - read from memory
    48  
    49  There are also community-supported plugins, which support the following sources:
    50  
    51  - [configmap](https://github.com/micro/go-plugins/tree/master/config/source/configmap) - read from k8s configmap
    52  - [grpc](https://github.com/micro/go-plugins/tree/master/config/source/grpc) - read from grpc server
    53  - [runtimevar](https://github.com/micro/go-plugins/tree/master/config/source/runtimevar) - read from Go Cloud Development Kit runtime variable
    54  - [url](https://github.com/micro/go-plugins/tree/master/config/source/url) - read from URL
    55  - [vault](https://github.com/micro/go-plugins/tree/master/config/source/vault) - read from Vault server
    56  
    57  TODO:
    58  
    59  - git url
    60  
    61  ### ChangeSet
    62  
    63  Sources return config as a ChangeSet. This is a single internal abstraction for multiple backends.
    64  
    65  ```go
    66  type ChangeSet struct {
    67  	// Raw encoded config data
    68  	Data      []byte
    69  	// MD5 checksum of the data
    70  	Checksum  string
    71  	// Encoding format e.g json, yaml, toml, xml
    72  	Format    string
    73  	// Source of the config e.g file, consul, etcd
    74  	Source    string
    75  	// Time of loading or update
    76  	Timestamp time.Time
    77  }
    78  ```
    79  
    80  ## Encoder
    81  
    82  An `Encoder` handles source config encoding/decoding. Backend sources may store config in many different 
    83  formats. Encoders give us the ability to handle any format. If an Encoder is not specified it defaults to json.
    84  
    85  The following encoding formats are supported:
    86  
    87  - json
    88  - yaml
    89  - toml
    90  - xml
    91  - hcl 
    92  
    93  ## Reader
    94  
    95  A `Reader` represents multiple changesets as a single merged and queryable set of values.
    96  
    97  ```go
    98  type Reader interface {
    99  	// Merge multiple changeset into a single format
   100  	Merge(...*source.ChangeSet) (*source.ChangeSet, error)
   101  	// Return return Go assertable values
   102  	Values(*source.ChangeSet) (Values, error)
   103  	// Name of the reader e.g a json reader
   104  	String() string
   105  }
   106  ```
   107  
   108  The reader makes use of Encoders to decode changesets into `map[string]interface{}` then merge them into 
   109  a single changeset. It looks at the Format field to determine the Encoder. The changeset is then represented 
   110  as a set of `Values` with the ability to retrive Go types and fallback where values cannot be loaded.
   111  
   112  ```go
   113  
   114  // Values is returned by the reader
   115  type Values interface {
   116  	// Return raw data
   117          Bytes() []byte
   118  	// Retrieve a value
   119          Get(path ...string) Value
   120  	// Return values as a map
   121          Map() map[string]interface{}
   122  	// Scan config into a Go type
   123          Scan(v interface{}) error
   124  }
   125  ```
   126  
   127  The `Value` interface allows casting/type asserting to go types with fallback defaults.
   128  
   129  ```go
   130  type Value interface {
   131  	Bool(def bool) bool
   132  	Int(def int) int
   133  	String(def string) string
   134  	Float64(def float64) float64
   135  	Duration(def time.Duration) time.Duration
   136  	StringSlice(def []string) []string
   137  	StringMap(def map[string]string) map[string]string
   138  	Scan(val interface{}) error
   139  	Bytes() []byte
   140  }
   141  ```
   142  
   143  ## Config 
   144  
   145  `Config` manages all config, abstracting away sources, encoders and the reader. 
   146  
   147  It manages reading, syncing, watching from multiple backend sources and represents them as a single merged and queryable source.
   148  
   149  ```go
   150  
   151  // Config is an interface abstraction for dynamic configuration
   152  type Config interface {
   153  	// provide the reader.Values interface
   154  	reader.Values
   155  	// Stop the config loader/watcher
   156  	Close() error
   157  	// Load config sources
   158  	Load(source ...source.Source) error
   159  	// Force a source changeset sync
   160  	Sync() error
   161  	// Watch a value for changes
   162  	Watch(path ...string) (Watcher, error)
   163  }
   164  ```
   165  
   166  ## Secrets
   167  
   168  Config should have the support for loading secrets. This is encoded data that requires a key to decrypt. 
   169  Implementations exist like Vault and Kubernetes secrets but we need something native to Micro we can 
   170  personally use.
   171  
   172  Ideally we specify the path to load secrets from in config and the secrets interface provides this to us
   173  
   174  ```
   175  type Secrets interface {
   176  	Load(path ...string) (reader.Values, error)
   177  }
   178  
   179  
   180  secrets.Load("auth", "token").String("")
   181  ```
   182  
   183  In an ideal world we automatically decode secrets when they are found but this may be a future thing. 
   184  
   185  Further to this design proposal we assume the config service would include a Secrets rpc endpoint 
   186  that would enable loading of secrets as and when needed. The config service would be started 
   187  with a public/private keypair for encoding/decoding secrets.
   188  
   189  ## Usage
   190  
   191  - [Sample Config](#sample-config)
   192  - [New Config](#new-config)
   193  - [Load File](#load-file)
   194  - [Read Config](#read-config)
   195  - [Read Values](#read-values)
   196  - [Watch Path](#watch-path)
   197  - [Multiple Sources](#merge-sources)
   198  - [Set Source Encoder](#set-source-encoder)
   199  - [Add Reader Encoder](#add-reader-encoder)
   200  
   201  
   202  
   203  ### Sample Config
   204  
   205  A config file can be of any format as long as we have an Encoder to support it.
   206  
   207  Example json config:
   208  
   209  ```json
   210  {
   211      "hosts": {
   212          "database": {
   213              "address": "10.0.0.1",
   214              "port": 3306
   215          },
   216          "cache": {
   217              "address": "10.0.0.2",
   218              "port": 6379
   219          }
   220      }
   221  }
   222  ```
   223  
   224  ### New Config
   225  
   226  Create a new config (or just make use of the default instance)
   227  
   228  ```go
   229  import "github.com/micro/go-micro/v2/config"
   230  
   231  conf := config.NewConfig()
   232  ```
   233  
   234  ### Load File
   235  
   236  Load config from a file source. It uses the file extension to determine config format.
   237  
   238  ```go
   239  import (
   240  	"github.com/micro/go-micro/v2/config"
   241  )
   242  
   243  // Load json config file
   244  config.LoadFile("/tmp/config.json")
   245  ```
   246  
   247  Load a yaml, toml or xml file by specifying a file with the appropriate file extension
   248  
   249  ```go
   250  // Load yaml config file
   251  config.LoadFile("/tmp/config.yaml")
   252  ```
   253  
   254  If an extension does not exist, specify the encoder
   255  
   256  ```go
   257  import (
   258  	"github.com/micro/go-micro/v2/config"
   259  	"github.com/micro/go-micro/v2/config/source/file"
   260  )
   261  
   262  enc := toml.NewEncoder()
   263  
   264  // Load toml file with encoder
   265  config.Load(file.NewSource(
   266          file.WithPath("/tmp/config"),
   267  	source.WithEncoder(enc),
   268  ))
   269  ```
   270  
   271  ### Read Config
   272  
   273  Read the entire config as a map
   274  
   275  ```go
   276  // retrieve map[string]interface{}
   277  conf := config.Map()
   278  
   279  // map[cache:map[address:10.0.0.2 port:6379] database:map[address:10.0.0.1 port:3306]]
   280  fmt.Println(conf["hosts"])
   281  ```
   282  
   283  Scan the config into a struct
   284  
   285  ```go
   286  type Host struct {
   287          Address string `json:"address"`
   288          Port int `json:"port"`
   289  }
   290  
   291  type Config struct{
   292  	Hosts map[string]Host `json:"hosts"`
   293  }
   294  
   295  var conf Config
   296  
   297  config.Scan(&conf)
   298  
   299  // 10.0.0.1 3306
   300  fmt.Println(conf.Hosts["database"].Address, conf.Hosts["database"].Port)
   301  ```
   302  
   303  ### Read Values
   304  
   305  Scan a value from the config into a struct
   306  
   307  ```go
   308  type Host struct {
   309  	Address string `json:"address"`
   310  	Port int `json:"port"`
   311  }
   312  
   313  var host Host
   314  
   315  config.Get("hosts", "database").Scan(&host)
   316  
   317  // 10.0.0.1 3306
   318  fmt.Println(host.Address, host.Port)
   319  ```
   320  
   321  Read individual values as Go types
   322  
   323  ```go
   324  // Get address. Set default to localhost as fallback
   325  address := config.Get("hosts", "database", "address").String("localhost")
   326  
   327  // Get port. Set default to 3000 as fallback
   328  port := config.Get("hosts", "database", "port").Int(3000)
   329  ```
   330  
   331  ### Watch Path
   332  
   333  Watch a path for changes. When the file changes the new value will be made available.
   334  
   335  ```go
   336  w, err := config.Watch("hosts", "database")
   337  if err != nil {
   338  	// do something
   339  }
   340  
   341  // wait for next value
   342  v, err := w.Next()
   343  if err != nil {
   344  	// do something
   345  }
   346  
   347  var host Host
   348  
   349  v.Scan(&host)
   350  ```
   351  
   352  ### Multiple Sources
   353  
   354  Multiple sources can be loaded and merged. Merging priority is in reverse order. 
   355  
   356  ```go
   357  config.Load(
   358  	// base config from env
   359  	env.NewSource(),
   360  	// override env with flags
   361  	flag.NewSource(),
   362  	// override flags with file
   363  	file.NewSource(
   364  		file.WithPath("/tmp/config.json"),
   365  	),
   366  )
   367  ```
   368  
   369  ### Set Source Encoder
   370  
   371  A source requires an encoder to encode/decode data and specify the changeset format.
   372  
   373  The default encoder is json. To change the encoder to yaml, xml, toml specify as an option.
   374  
   375  ```go
   376  e := yaml.NewEncoder()
   377  
   378  s := consul.NewSource(
   379  	source.WithEncoder(e),
   380  )
   381  ```
   382  
   383  ### Add Reader Encoder
   384  
   385  The reader uses encoders to decode data from sources with different formats.
   386  
   387  The default reader supports json, yaml, xml, toml and hcl. It represents the merged config as json.
   388  
   389  Add a new encoder by specifying it as an option.
   390  
   391  ```go
   392  e := yaml.NewEncoder()
   393  
   394  r := json.NewReader(
   395  	reader.WithEncoder(e),
   396  )
   397  ```
   398  
   399  ## FAQ
   400  
   401  ### How is this different from Viper?
   402  
   403  [Viper](https://github.com/spf13/viper) and go-config are solving the same problem. Go-config provides a different interface 
   404  and is part of the larger micro ecosystem of tooling.
   405  
   406  ### What's the difference between Encoder and Reader?
   407  
   408  The encoder is used by a backend source to encode/decode it's data. The reader uses encoders to decode data from multiple 
   409  sources with different formats, it then merges them into a single encoding format. 
   410  
   411  In the case of a file source , we use the file extension to determine the config format so the encoder is not used. 
   412  
   413  In the case of consul, etcd or similar key-value source we may load from a prefix containing multiple keys which means 
   414  the source needs to understand the encoding so it can return a single changeset. 
   415  
   416  In the case of environment variables and flags we also need a way to encode the values as bytes and specify the format so 
   417  it can later be merged by the reader.
   418  
   419  ### Why is changeset data not represented as map[string]interface{}?
   420  
   421  In some cases source data may not actually be key-value so it's easier to represent it as bytes and defer decoding to 
   422  the reader.
   423