change logger
This commit is contained in:
parent
7a3a7e2eaf
commit
2aba26d4d1
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user