Link Search Menu Expand Document

Config

Table of contents

  1. CLI usage
    1. Secrets
  2. From Services
  3. Advanced Concepts
    1. Merging Config Values

Config is a way to store configuration data for services. Config is both accessible from the Micro CLI and from services themselves.

CLI usage

Let’s assume we have a service called helloworld from which we want to read configuration data. First we have to insert said data with the cli. Config data can be organized under different “paths” with the dot notation. It’s a good convention to save all config data belonging to a service under a top level path segment matching the service name:

$ micro config set helloworld.somekey hello
$ micro config get helloworld.somekey
hello

We can save an other key too and read all values in one go with the dot notation:

$ micro config set helloworld.someotherkey "Hi there!"
$ micro config get helloworld
{"somekey":"hello","someotherkey":"Hi there!"}

As it can be seen, the config (by default) stores configuration data as JSONs. We can save any type:

$ micro config set helloworld.someboolkey true
$ micro config get helloworld.someboolkey
true
$ micro config get helloworld
{"someboolkey":true,"somekey":"hello","someotherkey":"Hi there!"}

So far we have only saved top level keys. Let’s explore the advantages of the dot notation.

$ micro config set helloworld.keywithsubs.subkey1 "So easy!"
{"keywithsubs":{"subkey1":"So easy!"},"someboolkey":true,"somekey":"hello","someotherkey":"Hi there!"}

Some of the example keys are getting in our way, let’s learn how to delete:

$ micro config del helloworld.someotherkey
$ micro config get helloworld
{"keywithsubs":{"subkey1":"So easy!"},"someboolkey":true,"somekey":"hello"}

We can of course delete not just leaf level keys, but top level ones too:

$ micro config del helloworld.keywithsubs
$ micro config get helloworld
{"someboolkey":true,"somekey":"hello"}

Secrets

The config also supports secrets - values encrypted at rest. This helps in case of leaks, be it a security one or an accidental copypaste. They are fairly easy to save:

$ micro config set --secret helloworld.hushkey "Very secret stuff" 
$ micro config get helloworld.hushkey
[secret]

$ micro config get --secret helloworld.hushkey
Very secret stuff

$ micro config get helloworld
{"hushkey":"[secret]","someboolkey":true,"somekey":"hello"}

$ micro config get --secret helloworld
{"hushkey":"Very secret stuff","someboolkey":true,"somekey":"hello"}

Even bool or number values can be saved as secrets, and they will appear as the string constant [secret] unless decrypted:

$ micro config set --secret helloworld.hush_number_key 42
$ micro config get helloworld
{"hush_number_key":"[secret]","hushkey":"[secret]","someboolkey":true,"somekey":"hello"}

# micro config get --secret helloworld
{"hush_number_key":42,"hushkey":"Very secret stuff","someboolkey":true,"somekey":"hello"}

From Services

It is simiarly easy to access and set config values from a service. A good example of reading values is the config example test service:

package main

import (
	"fmt"
	"time"

	"github.com/micro/micro/v3/service"
	"github.com/micro/micro/v3/service/config"
)

type keyConfig struct {
	Subkey  string `json:"subkey"`
	Subkey1 int    `json:"subkey1"`
}

type conf struct {
	Key keyConfig `json:"key"`
}

func main() {
	go func() {
		for {
			time.Sleep(time.Second)
			val, err := config.Get("key.subkey")
			fmt.Println("Value of key.subkey: ", val.String(""), err)

			val, err = config.Get("key", config.Secret(true))
			if err != nil {
				fmt.Println(err)
			}
			c := conf{}
			err = val.Scan(&c.Key)
			fmt.Println("Value of key.subkey1: ", c.Key.Subkey1, err)
		}
	}()

	// run the service
	service.Run()
}

The above service will print the value of key.subkey and key.subkey every second. By passing in the config.Secret(true) option, we tell config to decrypt secret values for us, similarly to the --secret CLI flag.

The config interface specifies not just Get Set and Delete to access values, but a few convenience functions too in the Value interface.

It is worth noting that String Int etc methods will do a best effort try at coercing types, ie. if the value saved is a string, Int will try to parse it. However, the same does not apply to the Scan method, which uses json.Unmarshal under the hood, which we all know fails when encountering type mismatches.

Get should, in all cases, return a non nil Value, so even if the Get errors, Value.Int() and other operations should never panic.

Advanced Concepts

Merging Config Values

When saving a string with the CLI that is a valid JSON map, it gets expanded to be saved as a proper map structure, instead of a string, ie

$ micro config set helloworld '{"a": "val1", "b": "val2"}'
$ micro config get helloworld.a
val1
# If the string would be saved as is, `helloworld.a` would be a nonexistent path

The advantages of this become particularly visible when Setting a complex type with the library:

type conf struct {
	A string `json:"a"`
	B string `json:"b"`
}

c1 := conf{"val1", "val2"}
config.Set("key", c1)

v, _ := config.Get("key")
c2 := &conf{}
v.Scan(c2)
// c1 and c2 should be equal

Or with the following example

$ micro config del helloworld
$ micro config set helloworld '{"a":1}'
$ micro config get helloworld
{"a":1}
$ micro config set helloworld '{"b":2}'
$ micro config get helloworld
{"a":1,"b":2}