2017-08-03 17:24:27 +00:00
package etcd
2015-05-24 16:38:49 +00:00
import (
"errors"
2021-05-31 16:54:05 +00:00
"fmt"
2017-04-04 15:50:44 +00:00
"net/url"
2015-12-17 11:48:13 +00:00
"os"
2017-04-04 15:50:44 +00:00
"strings"
2015-05-24 16:38:49 +00:00
2018-04-03 00:46:59 +00:00
log "github.com/hashicorp/go-hclog"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/physical"
2021-09-29 18:28:13 +00:00
"go.etcd.io/etcd/client/v2"
2015-05-24 16:38:49 +00:00
)
var (
2022-02-22 21:48:04 +00:00
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')" )
EtcdLockHeldError = errors . New ( "lock already held" )
EtcdLockNotHeldError = errors . New ( "lock not held" )
EtcdVersionUnknown = errors . New ( "etcd: unknown API version" )
2015-05-24 16:38:49 +00:00
)
2017-08-03 17:24:27 +00:00
// NewEtcdBackend constructs a etcd backend using a given machine address.
func NewEtcdBackend ( conf map [ string ] string , logger log . Logger ) ( physical . Backend , error ) {
2017-01-03 19:43:46 +00:00
var (
apiVersion string
ok bool
)
2015-11-06 07:37:01 +00:00
2017-01-03 19:43:46 +00:00
if apiVersion , ok = conf [ "etcd_api" ] ; ! ok {
apiVersion = os . Getenv ( "ETCD_API" )
2015-11-16 22:30:02 +00:00
}
2017-01-26 22:19:13 +00:00
2017-01-03 19:43:46 +00:00
if apiVersion == "" {
2022-02-22 21:48:04 +00:00
apiVersion = "v3"
2017-01-26 22:19:13 +00:00
}
2015-12-17 11:48:13 +00:00
2017-01-03 19:43:46 +00:00
switch apiVersion {
case "3" , "etcd3" , "v3" :
return newEtcd3Backend ( conf , logger )
2016-01-27 22:15:52 +00:00
default :
2017-08-03 17:24:27 +00:00
return nil , EtcdVersionUnknown
2015-05-31 19:02:11 +00:00
}
}
2017-01-26 22:19:13 +00:00
2017-04-04 15:50:44 +00:00
// 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 {
2022-02-22 21:48:04 +00:00
endpoints := strings . Split ( address , "," )
2017-04-04 15:50:44 +00:00
// 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 {
2019-01-23 19:35:03 +00:00
srvName , _ := getEtcdOption ( conf , "discovery_srv_name" , "ETCD_DISCOVERY_SRV_NAME" )
2017-04-04 15:50:44 +00:00
discoverer := client . NewSRVDiscover ( )
2019-01-23 19:35:03 +00:00
endpoints , err := discoverer . Discover ( domain , srvName )
2017-04-04 15:50:44 +00:00
if err != nil {
2021-05-31 16:54:05 +00:00
return nil , fmt . Errorf ( "failed to discover etcd endpoints through SRV discovery: %w" , err )
2017-04-04 15:50:44 +00:00
}
return endpoints , nil
}
// Set a default endpoints list if no option was set
return [ ] string { "http://127.0.0.1:2379" } , nil
}