Etcd DNS discovery (#2521)

* etcd: Add discovery_srv option
This commit is contained in:
Jonathan Sokolowski 2017-04-05 01:50:44 +10:00 committed by Jeff Mitchell
parent 9ec414016d
commit a4ceaf0035
4 changed files with 72 additions and 23 deletions

View file

@ -3,7 +3,10 @@ package physical
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"net/url"
"os" "os"
"strings"
"github.com/coreos/etcd/client" "github.com/coreos/etcd/client"
"github.com/coreos/go-semver/semver" "github.com/coreos/go-semver/semver"
@ -13,6 +16,7 @@ import (
var ( var (
EtcdSyncConfigError = errors.New("client setup failed: unable to parse etcd sync field in config") EtcdSyncConfigError = errors.New("client setup failed: unable to parse etcd sync field in config")
EtcdSyncClusterError = errors.New("client setup failed: unable to sync etcd cluster") EtcdSyncClusterError = errors.New("client setup failed: unable to sync etcd cluster")
EtcdMultipleBootstrapError = errors.New("client setup failed: multiple discovery or bootstrap flags specified, use either \"address\" or \"discovery_srv\"")
EtcdAddressError = errors.New("client setup failed: address must be valid URL (ex. 'scheme://host:port')") EtcdAddressError = errors.New("client setup failed: address must be valid URL (ex. 'scheme://host:port')")
EtcdSemaphoreKeysEmptyError = errors.New("lock queue is empty") EtcdSemaphoreKeysEmptyError = errors.New("lock queue is empty")
EtcdLockHeldError = errors.New("lock already held") EtcdLockHeldError = errors.New("lock already held")
@ -95,3 +99,47 @@ func getEtcdAPIVersion(c client.Client) (string, error) {
return "3", nil return "3", nil
} }
// Retrieves the config option in order of priority:
// 1. The named environment variable if it exist
// 2. The key in the config map
func getEtcdOption(conf map[string]string, confKey, envVar string) (string, bool) {
confVal, inConf := conf[confKey]
envVal, inEnv := os.LookupEnv(envVar)
if inEnv {
return envVal, true
}
return confVal, inConf
}
func getEtcdEndpoints(conf map[string]string) ([]string, error) {
address, staticBootstrap := getEtcdOption(conf, "address", "ETCD_ADDR")
domain, useSrv := getEtcdOption(conf, "discovery_srv", "ETCD_DISCOVERY_SRV")
if useSrv && staticBootstrap {
return nil, EtcdMultipleBootstrapError
}
if staticBootstrap {
endpoints := strings.Split(address, Etcd2MachineDelimiter)
// Verify that the machines are valid URLs
for _, e := range endpoints {
u, urlErr := url.Parse(e)
if urlErr != nil || u.Scheme == "" {
return nil, EtcdAddressError
}
}
return endpoints, nil
}
if useSrv {
discoverer := client.NewSRVDiscover()
endpoints, err := discoverer.Discover(domain)
if err != nil {
return nil, fmt.Errorf("failed to discover etcd endpoints through SRV discovery: %v", err)
}
return endpoints, nil
}
// Set a default endpoints list if no option was set
return []string{"http://127.0.0.1:2379"}, nil
}

View file

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -118,23 +117,9 @@ func newEtcd2Backend(conf map[string]string, logger log.Logger) (Backend, error)
} }
func newEtcdV2Client(conf map[string]string) (client.Client, error) { func newEtcdV2Client(conf map[string]string) (client.Client, error) {
// Set a default machines list and check for an overriding address value. endpoints, err := getEtcdEndpoints(conf)
machines := "http://127.0.0.1:2379" if err != nil {
if address, ok := conf["address"]; ok { return nil, err
machines = address
}
machinesEnv := os.Getenv("ETCD_ADDR")
if machinesEnv != "" {
machines = machinesEnv
}
machinesParsed := strings.Split(machines, Etcd2MachineDelimiter)
// Verify that the machines are valid URLs
for _, machine := range machinesParsed {
u, urlErr := url.Parse(machine)
if urlErr != nil || u.Scheme == "" {
return nil, EtcdAddressError
}
} }
// Create a new client from the supplied address and attempt to sync with the // Create a new client from the supplied address and attempt to sync with the
@ -160,7 +145,7 @@ func newEtcdV2Client(conf map[string]string) (client.Client, error) {
} }
cfg := client.Config{ cfg := client.Config{
Endpoints: machinesParsed, Endpoints: endpoints,
Transport: cTransport, Transport: cTransport,
} }

View file

@ -48,10 +48,9 @@ func newEtcd3Backend(conf map[string]string, logger log.Logger) (Backend, error)
path = "/" + path path = "/" + path
} }
// Set a default machines list and check for an overriding address value. endpoints, err := getEtcdEndpoints(conf)
endpoints := []string{"http://127.0.0.1:2379"} if err != nil {
if address, ok := conf["address"]; ok { return nil, err
endpoints = strings.Split(address, ",")
} }
cfg := clientv3.Config{ cfg := clientv3.Config{

View file

@ -36,6 +36,10 @@ storage "etcd" {
Etcd instances as a comma-separated list. This can also be provided via the Etcd instances as a comma-separated list. This can also be provided via the
environment variable `ETCD_ADDR`. environment variable `ETCD_ADDR`.
- `discovery_srv` `(string: "example.com")` - Specifies the domain name to
query for SRV records describing cluster endpoints. This can also be provided
via the environment variable `ETCD_DISCOVERY_SRV`.
- `etcd_api` `(string: "<varies>")` Specifies the version of the API to - `etcd_api` `(string: "<varies>")` Specifies the version of the API to
communicate with. By default, this is derived automatically. If the cluster communicate with. By default, this is derived automatically. If the cluster
version is 3.1+ and there has been no data written using the v2 API, the version is 3.1+ and there has been no data written using the v2 API, the
@ -89,6 +93,18 @@ discussed in more detail in the [HA concepts page](/docs/concepts/ha.html).
## `etcd` Examples ## `etcd` Examples
### DNS Discovery of cluster members
This example configures vault to discover the Etcd cluster members via SRV
records as outlined in the
[DNS Discovery protocol documentation][dns discovery].
```hcl
storage "etcd" {
discovery_srv = "example.com"
}
```
### Custom Authentication ### Custom Authentication
This example shows connecting to the Etcd cluster using a username and password. This example shows connecting to the Etcd cluster using a username and password.
@ -122,3 +138,4 @@ storage "etcd" {
``` ```
[etcd]: https://coreos.com/etcd "Etcd by CoreOS" [etcd]: https://coreos.com/etcd "Etcd by CoreOS"
[dns discovery]: https://coreos.com/etcd/docs/latest/op-guide/clustering.html#dns-discovery "Etcd cluster DNS Discovery"