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" | ||||
| 	"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/task" | ||||
| 	"github.com/micro/go-micro/sync/task/local" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user