change logger
This commit is contained in:
		| @@ -1,43 +0,0 @@ | |||||||
| # Vault Source |  | ||||||
|  |  | ||||||
| The vault source reads config from different secret engines in a Vault server. For example: |  | ||||||
| ``` |  | ||||||
| kv: secret/data/<my/secret> |  | ||||||
| database credentials: database/creds/<my-db-role> |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## New Source |  | ||||||
|  |  | ||||||
| Specify source with data |  | ||||||
|  |  | ||||||
| ```go |  | ||||||
| vaultSource := vault.NewSource( |  | ||||||
| 	// mandatory: it specifies server address.  |  | ||||||
| 	// It could have different formats: |  | ||||||
| 	// 127.0.0.1 -> https://127.0.0.1:8200 |  | ||||||
| 	// http://127.0.0.1 -> http://127.0.0.1:8200 |  | ||||||
| 	// http://127.0.0.1:2233 |  | ||||||
| 	vault.WithAddress("http://127.0.0.1:8200"), |  | ||||||
| 	// mandatory: it specifies a resource to been access |  | ||||||
| 	vault.WithResourcePath("secret/data/my/secret"), |  | ||||||
|     // mandatory: it specifies a resource to been access |  | ||||||
| 	vault.WithToken("<my-token>"), |  | ||||||
| 	// optional: path to store my secret. |  | ||||||
| 	// By default use resourcePath value  |  | ||||||
| 	vault.WithSecretName("my/secret"), |  | ||||||
| 	// optional: namespace. |  | ||||||
|     vault.WithNameSpace("myNameSpace"), |  | ||||||
| ) |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Load Source |  | ||||||
|  |  | ||||||
| Load the source into config |  | ||||||
|  |  | ||||||
| ```go |  | ||||||
| // Create new config |  | ||||||
| conf := config.NewConfig() |  | ||||||
|  |  | ||||||
| // Load file source |  | ||||||
| conf.Load(vaultSource) |  | ||||||
| ``` |  | ||||||
| @@ -1,63 +0,0 @@ | |||||||
| package vault |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/config/source" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type addressKey struct{} |  | ||||||
| type resourcePath struct{} |  | ||||||
| type nameSpace struct{} |  | ||||||
| type tokenKey struct{} |  | ||||||
| type secretName struct{} |  | ||||||
|  |  | ||||||
| // WithAddress sets the server address |  | ||||||
| func WithAddress(a string) source.Option { |  | ||||||
| 	return func(o *source.Options) { |  | ||||||
| 		if o.Context == nil { |  | ||||||
| 			o.Context = context.Background() |  | ||||||
| 		} |  | ||||||
| 		o.Context = context.WithValue(o.Context, addressKey{}, a) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithResourcePath sets the resource that will be access |  | ||||||
| func WithResourcePath(p string) source.Option { |  | ||||||
| 	return func(o *source.Options) { |  | ||||||
| 		if o.Context == nil { |  | ||||||
| 			o.Context = context.Background() |  | ||||||
| 		} |  | ||||||
| 		o.Context = context.WithValue(o.Context, resourcePath{}, p) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithNameSpace sets the namespace that its going to be access |  | ||||||
| func WithNameSpace(n string) source.Option { |  | ||||||
| 	return func(o *source.Options) { |  | ||||||
| 		if o.Context == nil { |  | ||||||
| 			o.Context = context.Background() |  | ||||||
| 		} |  | ||||||
| 		o.Context = context.WithValue(o.Context, nameSpace{}, n) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithToken sets the key token to use |  | ||||||
| func WithToken(t string) source.Option { |  | ||||||
| 	return func(o *source.Options) { |  | ||||||
| 		if o.Context == nil { |  | ||||||
| 			o.Context = context.Background() |  | ||||||
| 		} |  | ||||||
| 		o.Context = context.WithValue(o.Context, tokenKey{}, t) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithSecretName sets the name of the secret to wrap in on a map |  | ||||||
| func WithSecretName(t string) source.Option { |  | ||||||
| 	return func(o *source.Options) { |  | ||||||
| 		if o.Context == nil { |  | ||||||
| 			o.Context = context.Background() |  | ||||||
| 		} |  | ||||||
| 		o.Context = context.WithValue(o.Context, secretName{}, t) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| vault kv put secret/data/db/auth user=myuser password=mypassword2 host=128.23.33.21 port=3307 |  | ||||||
| @@ -1,98 +0,0 @@ | |||||||
| package vault |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/micro/go-micro/config/source" |  | ||||||
| 	"net" |  | ||||||
| 	"net/url" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func makeMap(kv map[string]interface{}, secretName string) (map[string]interface{}, error) { |  | ||||||
| 	data := make(map[string]interface{}) |  | ||||||
|  |  | ||||||
| 	// if secret version included |  | ||||||
| 	if kv["data"] != nil && kv["metadata"] != nil { |  | ||||||
| 		kv = kv["data"].(map[string]interface{}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	target := data |  | ||||||
|  |  | ||||||
| 	// if secretName defined, wrap secrets under a map |  | ||||||
| 	if secretName != "" { |  | ||||||
| 		path := strings.Split(secretName, "/") |  | ||||||
| 		// find (or create) the location we want to put this value at |  | ||||||
| 		for i, dir := range path { |  | ||||||
| 			if _, ok := target[dir]; !ok { |  | ||||||
| 				target[dir] = make(map[string]interface{}) |  | ||||||
| 			} |  | ||||||
| 			if i < len(path)-1 { |  | ||||||
| 				target = target[dir].(map[string]interface{}) |  | ||||||
| 			} else { |  | ||||||
| 				target[dir] = kv |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return data, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func getAddress(options source.Options) string { |  | ||||||
| 	// check if there are any addrs |  | ||||||
| 	a, ok := options.Context.Value(addressKey{}).(string) |  | ||||||
| 	if ok { |  | ||||||
| 		// check if http protocol is defined |  | ||||||
| 		if a[0] != 'h' { |  | ||||||
| 			addr, port, err := net.SplitHostPort(a) |  | ||||||
| 			if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" { |  | ||||||
| 				port = "8200" |  | ||||||
| 				addr = a |  | ||||||
| 				return fmt.Sprintf("https://%s:%s", addr, port) |  | ||||||
| 			} else if err == nil { |  | ||||||
| 				return fmt.Sprintf("https://%s:%s", addr, port) |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			u, _ := url.Parse(a) |  | ||||||
|  |  | ||||||
| 			if host, port, _ := net.SplitHostPort(u.Host); host == "" { |  | ||||||
| 				port = "8200" |  | ||||||
| 				return fmt.Sprintf("%s://%s:%s", u.Scheme, u.Host, port) |  | ||||||
| 			} else { |  | ||||||
| 				return fmt.Sprintf("%s://%s", u.Scheme, u.Host) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func getToken(options source.Options) string { |  | ||||||
| 	token, ok := options.Context.Value(tokenKey{}).(string) |  | ||||||
| 	if ok { |  | ||||||
| 		return token |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func getResourcePath(options source.Options) string { |  | ||||||
| 	path, ok := options.Context.Value(resourcePath{}).(string) |  | ||||||
| 	if ok { |  | ||||||
| 		return path |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func getNameSpace(options source.Options) string { |  | ||||||
| 	ns, ok := options.Context.Value(nameSpace{}).(string) |  | ||||||
| 	if ok { |  | ||||||
| 		return ns |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func getSecretName(options source.Options) string { |  | ||||||
| 	ns, ok := options.Context.Value(secretName{}).(string) |  | ||||||
| 	if ok { |  | ||||||
| 		return ns |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
| @@ -1,97 +0,0 @@ | |||||||
| package vault |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/hashicorp/vault/api" |  | ||||||
| 	"github.com/micro/go-micro/config/source" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Currently a single vault reader |  | ||||||
| type vault struct { |  | ||||||
| 	secretPath string |  | ||||||
| 	secretName string |  | ||||||
| 	opts       source.Options |  | ||||||
| 	client     *api.Client |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *vault) Read() (*source.ChangeSet, error) { |  | ||||||
| 	secret, err := c.client.Logical().Read(c.secretPath) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if secret == nil { |  | ||||||
| 		return nil, fmt.Errorf("source not found: %s", c.secretPath) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if secret.Data == nil && secret.Warnings != nil { |  | ||||||
| 		return nil, fmt.Errorf("source: %s errors: %v", c.secretPath, secret.Warnings) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	data, err := makeMap(secret.Data, c.secretName) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("error reading data: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	b, err := c.opts.Encoder.Encode(data) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("error reading source: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	cs := &source.ChangeSet{ |  | ||||||
| 		Timestamp: time.Now(), |  | ||||||
| 		Format:    c.opts.Encoder.String(), |  | ||||||
| 		Source:    c.String(), |  | ||||||
| 		Data:      b, |  | ||||||
| 	} |  | ||||||
| 	cs.Checksum = cs.Sum() |  | ||||||
|  |  | ||||||
| 	return cs, nil |  | ||||||
| 	//return nil, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *vault) String() string { |  | ||||||
| 	return "vault" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (c *vault) Watch() (source.Watcher, error) { |  | ||||||
| 	w := newWatcher(c.client) |  | ||||||
|  |  | ||||||
| 	return w, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // NewSource creates a new vault source |  | ||||||
| func NewSource(opts ...source.Option) source.Source { |  | ||||||
| 	options := source.NewOptions(opts...) |  | ||||||
|  |  | ||||||
| 	// create the client |  | ||||||
| 	client, _ := api.NewClient(api.DefaultConfig()) |  | ||||||
|  |  | ||||||
| 	// get and set options |  | ||||||
| 	if address := getAddress(options); address != "" { |  | ||||||
| 		_ = client.SetAddress(address) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if nameSpace := getNameSpace(options); nameSpace != "" { |  | ||||||
| 		client.SetNamespace(nameSpace) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if token := getToken(options); token != "" { |  | ||||||
| 		client.SetToken(token) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	path := getResourcePath(options) |  | ||||||
| 	name := getSecretName(options) |  | ||||||
| 	if name == "" { |  | ||||||
| 		name = path |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &vault{ |  | ||||||
| 		opts:       options, |  | ||||||
| 		client:     client, |  | ||||||
| 		secretPath: path, |  | ||||||
| 		secretName: name, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,134 +0,0 @@ | |||||||
| package vault |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/config" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestVaultMakeMap(t *testing.T) { |  | ||||||
| 	tt := []struct { |  | ||||||
| 		name       string |  | ||||||
| 		expected   []byte |  | ||||||
| 		input      []byte |  | ||||||
| 		secretName string |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			name:       "simple valid data 1", |  | ||||||
| 			secretName: "my/secret", |  | ||||||
| 			input:      []byte(`{"data":{"bar":"bazz", "tar":"par"}, "metadata":{"version":1, "destroyed": false}}`), |  | ||||||
| 			expected:   []byte(`{"my":{"secret":{"bar":"bazz", "tar":"par"}}}`), |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:       "simple valid data 2", |  | ||||||
| 			secretName: "my/secret", |  | ||||||
| 			input:      []byte(`{"bar":"bazz", "tar":"par"}`), |  | ||||||
| 			expected:   []byte(`{"my":{"secret":{"bar":"bazz", "tar":"par"}}}`), |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, tc := range tt { |  | ||||||
| 		t.Run(tc.name, func(t *testing.T) { |  | ||||||
| 			var input map[string]interface{} |  | ||||||
| 			var expected map[string]interface{} |  | ||||||
|  |  | ||||||
| 			_ = json.Unmarshal(tc.input, &input) |  | ||||||
| 			_ = json.Unmarshal(tc.expected, &expected) |  | ||||||
|  |  | ||||||
| 			out, _ := makeMap(input, tc.secretName) |  | ||||||
|  |  | ||||||
| 			if eq := reflect.DeepEqual(out, expected); !eq { |  | ||||||
| 				fmt.Println(eq) |  | ||||||
| 				t.Fatalf("expected %v and got %v", expected, out) |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestVault_Read(t *testing.T) { |  | ||||||
| 	if tr := os.Getenv("TRAVIS"); len(tr) > 0 { |  | ||||||
| 		t.Skip() |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var ( |  | ||||||
| 		address = "http://127.0.0.1" |  | ||||||
| 		resource = "secret/data/db/auth" |  | ||||||
| 		token = "s.Q4Zi0CSowXZl7sh0z96ijcT4" |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	data := []byte(`{"secret":{"data":{"db":{"auth":{"host":"128.23.33.21","password":"mypassword","port":"3306","user":"myuser"}}}}}`) |  | ||||||
|  |  | ||||||
| 	tt := []struct { |  | ||||||
| 		name      string |  | ||||||
| 		addr     string |  | ||||||
| 		resource string |  | ||||||
| 		token string |  | ||||||
| 	}{ |  | ||||||
| 		{name: "read data basic", addr: address, resource: resource, token: token}, |  | ||||||
| 		{name: "read data without token", addr: address, resource: resource, token: ""}, |  | ||||||
| 		{name: "read data full address format", addr: "http://127.0.0.1:8200", resource: resource, token: token}, |  | ||||||
| 		{name: "read data wrong resource path", addr: address, resource: "secrets/data/db/auth", token: token}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, tc := range tt { |  | ||||||
| 		t.Run(tc.name, func(t *testing.T) { |  | ||||||
| 			source := NewSource( |  | ||||||
| 				WithAddress(tc.addr), |  | ||||||
| 				WithResourcePath(tc.resource), |  | ||||||
| 				WithToken(tc.token), |  | ||||||
| 			) |  | ||||||
|  |  | ||||||
| 			r, err := source.Read() |  | ||||||
| 			if err != nil { |  | ||||||
| 				if tc.token == "" { |  | ||||||
| 					return |  | ||||||
| 				} else if strings.Compare(err.Error(), "source not found: secrets/data/db/auth") == 0 { |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 				t.Errorf("%s: not able to read the config values because: %v", tc.name, err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if string(r.Data) != string(data) { |  | ||||||
| 				t.Logf("data expected: %v", string(data)) |  | ||||||
| 				t.Logf("data got from configmap: %v", string(r.Data)) |  | ||||||
| 				t.Errorf("data from configmap does not match.") |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestVault_String(t *testing.T) { |  | ||||||
| 	source := NewSource() |  | ||||||
|  |  | ||||||
| 	if source.String() != "vault" { |  | ||||||
| 		t.Errorf("expecting to get %v and instead got %v", "vault", source) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestVaultNewSource(t *testing.T) { |  | ||||||
| 	if tr := os.Getenv("TRAVIS"); len(tr) > 0 { |  | ||||||
| 		t.Skip() |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	conf := config.NewConfig() |  | ||||||
|  |  | ||||||
| 	_ = conf.Load(NewSource( |  | ||||||
| 		WithAddress("http://127.0.0.1"), |  | ||||||
| 		WithResourcePath("secret/data/db/auth"), |  | ||||||
| 		WithToken("s.Q4Zi0CSowXZl7sh0z96ijcT4"), |  | ||||||
| 	)) |  | ||||||
|  |  | ||||||
| 	if user := conf.Get("secret", "data", "db", "auth", "user").String("user"); user != "myuser" { |  | ||||||
| 		t.Errorf("expected %v and got %v", "myuser", user) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if addr := conf.Get("secret", "data", "db", "auth", "host").String("host"); addr != "128.23.33.21" { |  | ||||||
| 		t.Errorf("expected %v and got %v", "128.23.33.21", addr) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| package vault |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"github.com/hashicorp/vault/api" |  | ||||||
| 	"github.com/micro/go-micro/config/source" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type watcher struct { |  | ||||||
| 	c    *api.Client |  | ||||||
| 	exit chan bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newWatcher(c *api.Client) *watcher { |  | ||||||
| 	return &watcher{ |  | ||||||
| 		c:    c, |  | ||||||
| 		exit: make(chan bool), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (w *watcher) Next() (*source.ChangeSet, error) { |  | ||||||
| 	<-w.exit |  | ||||||
| 	return nil, errors.New("url watcher stopped") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (w *watcher) Stop() error { |  | ||||||
| 	select { |  | ||||||
| 	case <-w.exit: |  | ||||||
| 	default: |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| @@ -5,7 +5,7 @@ import ( | |||||||
| 	"math" | 	"math" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/micro/go-log" | 	"github.com/micro/go-micro/util/log" | ||||||
| 	"github.com/micro/go-micro/sync/leader/consul" | 	"github.com/micro/go-micro/sync/leader/consul" | ||||||
| 	"github.com/micro/go-micro/sync/task" | 	"github.com/micro/go-micro/sync/task" | ||||||
| 	"github.com/micro/go-micro/sync/task/local" | 	"github.com/micro/go-micro/sync/task/local" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user