mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
refactor(config): make config its own package
Refactor config into its own package. Trying to tease the config from the server so that all of the control surfaces are exposed in the Server for easier testing.
This commit is contained in:
525
server/config.go
525
server/config.go
@@ -1,525 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/third_party/github.com/BurntSushi/toml"
|
||||
"github.com/coreos/etcd/log"
|
||||
)
|
||||
|
||||
// The default location for the etcd configuration file.
|
||||
const DefaultSystemConfigPath = "/etc/etcd/etcd.conf"
|
||||
|
||||
// A lookup of deprecated flags to their new flag name.
|
||||
var newFlagNameLookup = map[string]string{
|
||||
"C": "peers",
|
||||
"CF": "peers-file",
|
||||
"n": "name",
|
||||
"c": "addr",
|
||||
"cl": "bind-addr",
|
||||
"s": "peer-addr",
|
||||
"sl": "peer-bind-addr",
|
||||
"d": "data-dir",
|
||||
"m": "max-result-buffer",
|
||||
"r": "max-retry-attempts",
|
||||
"maxsize": "max-cluster-size",
|
||||
"clientCAFile": "ca-file",
|
||||
"clientCert": "cert-file",
|
||||
"clientKey": "key-file",
|
||||
"serverCAFile": "peer-ca-file",
|
||||
"serverCert": "peer-cert-file",
|
||||
"serverKey": "peer-key-file",
|
||||
"snapshotCount": "snapshot-count",
|
||||
}
|
||||
|
||||
// Config represents the server configuration.
|
||||
type Config struct {
|
||||
SystemPath string
|
||||
|
||||
Addr string `toml:"addr" env:"ETCD_ADDR"`
|
||||
BindAddr string `toml:"bind_addr" env:"ETCD_BIND_ADDR"`
|
||||
BootstrapURL string `toml:"bootstrap_url" env:"ETCD_BOOTSTRAP_URL"`
|
||||
CAFile string `toml:"ca_file" env:"ETCD_CA_FILE"`
|
||||
CertFile string `toml:"cert_file" env:"ETCD_CERT_FILE"`
|
||||
CPUProfileFile string
|
||||
CorsOrigins []string `toml:"cors" env:"ETCD_CORS"`
|
||||
DataDir string `toml:"data_dir" env:"ETCD_DATA_DIR"`
|
||||
Force bool
|
||||
KeyFile string `toml:"key_file" env:"ETCD_KEY_FILE"`
|
||||
Peers []string `toml:"peers" env:"ETCD_PEERS"`
|
||||
PeersFile string `toml:"peers_file" env:"ETCD_PEERS_FILE"`
|
||||
MaxClusterSize int `toml:"max_cluster_size" env:"ETCD_MAX_CLUSTER_SIZE"`
|
||||
MaxResultBuffer int `toml:"max_result_buffer" env:"ETCD_MAX_RESULT_BUFFER"`
|
||||
MaxRetryAttempts int `toml:"max_retry_attempts" env:"ETCD_MAX_RETRY_ATTEMPTS"`
|
||||
Name string `toml:"name" env:"ETCD_NAME"`
|
||||
Snapshot bool `toml:"snapshot" env:"ETCD_SNAPSHOT"`
|
||||
SnapshotCount int `toml:"snapshot_count" env:"ETCD_SNAPSHOTCOUNT"`
|
||||
ShowHelp bool
|
||||
ShowVersion bool
|
||||
Verbose bool `toml:"verbose" env:"ETCD_VERBOSE"`
|
||||
VeryVerbose bool `toml:"very_verbose" env:"ETCD_VERY_VERBOSE"`
|
||||
VeryVeryVerbose bool `toml:"very_very_verbose" env:"ETCD_VERY_VERY_VERBOSE"`
|
||||
Peer struct {
|
||||
Addr string `toml:"addr" env:"ETCD_PEER_ADDR"`
|
||||
BindAddr string `toml:"bind_addr" env:"ETCD_PEER_BIND_ADDR"`
|
||||
CAFile string `toml:"ca_file" env:"ETCD_PEER_CA_FILE"`
|
||||
CertFile string `toml:"cert_file" env:"ETCD_PEER_CERT_FILE"`
|
||||
KeyFile string `toml:"key_file" env:"ETCD_PEER_KEY_FILE"`
|
||||
HeartbeatTimeout int `toml:"heartbeat_timeout" env:"ETCD_PEER_HEARTBEAT_TIMEOUT"`
|
||||
ElectionTimeout int `toml:"election_timeout" env:"ETCD_PEER_ELECTION_TIMEOUT"`
|
||||
}
|
||||
strTrace string `toml:"trace" env:"ETCD_TRACE"`
|
||||
GraphiteHost string `toml:"graphite_host" env:"ETCD_GRAPHITE_HOST"`
|
||||
}
|
||||
|
||||
// NewConfig returns a Config initialized with default values.
|
||||
func NewConfig() *Config {
|
||||
c := new(Config)
|
||||
c.SystemPath = DefaultSystemConfigPath
|
||||
c.Addr = "127.0.0.1:4001"
|
||||
c.MaxClusterSize = 9
|
||||
c.MaxResultBuffer = 1024
|
||||
c.MaxRetryAttempts = 3
|
||||
c.Snapshot = true
|
||||
c.SnapshotCount = 10000
|
||||
c.Peer.Addr = "127.0.0.1:7001"
|
||||
c.Peer.HeartbeatTimeout = defaultHeartbeatTimeout
|
||||
c.Peer.ElectionTimeout = defaultElectionTimeout
|
||||
return c
|
||||
}
|
||||
|
||||
// Loads the configuration from the system config, command line config,
|
||||
// environment variables, and finally command line arguments.
|
||||
func (c *Config) Load(arguments []string) error {
|
||||
var path string
|
||||
f := flag.NewFlagSet("etcd", -1)
|
||||
f.SetOutput(ioutil.Discard)
|
||||
f.StringVar(&path, "config", "", "path to config file")
|
||||
f.Parse(arguments)
|
||||
|
||||
// Load from system file.
|
||||
if err := c.LoadSystemFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load from config file specified in arguments.
|
||||
if path != "" {
|
||||
if err := c.LoadFile(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Load from the environment variables next.
|
||||
if err := c.LoadEnv(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load from command line flags.
|
||||
if err := c.LoadFlags(arguments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Loads peers if a peer file was specified.
|
||||
if err := c.LoadPeersFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sanitize all the input fields.
|
||||
if err := c.Sanitize(); err != nil {
|
||||
return fmt.Errorf("sanitize: %v", err)
|
||||
}
|
||||
|
||||
// Force remove server configuration if specified.
|
||||
if c.Force {
|
||||
c.Reset()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loads from the system etcd configuration file if it exists.
|
||||
func (c *Config) LoadSystemFile() error {
|
||||
if _, err := os.Stat(c.SystemPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return c.LoadFile(c.SystemPath)
|
||||
}
|
||||
|
||||
// Loads configuration from a file.
|
||||
func (c *Config) LoadFile(path string) error {
|
||||
_, err := toml.DecodeFile(path, &c)
|
||||
return err
|
||||
}
|
||||
|
||||
// LoadEnv loads the configuration via environment variables.
|
||||
func (c *Config) LoadEnv() error {
|
||||
if err := c.loadEnv(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.loadEnv(&c.Peer); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) loadEnv(target interface{}) error {
|
||||
value := reflect.Indirect(reflect.ValueOf(target))
|
||||
typ := value.Type()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
// Retrieve environment variable.
|
||||
v := strings.TrimSpace(os.Getenv(field.Tag.Get("env")))
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set the appropriate type.
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Bool:
|
||||
value.Field(i).SetBool(v != "0" && v != "false")
|
||||
case reflect.Int:
|
||||
newValue, err := strconv.ParseInt(v, 10, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Parse error: %s: %s", field.Tag.Get("env"), err)
|
||||
}
|
||||
value.Field(i).SetInt(newValue)
|
||||
case reflect.String:
|
||||
value.Field(i).SetString(v)
|
||||
case reflect.Slice:
|
||||
value.Field(i).Set(reflect.ValueOf(trimsplit(v, ",")))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loads configuration from command line flags.
|
||||
func (c *Config) LoadFlags(arguments []string) error {
|
||||
var peers, cors, path string
|
||||
|
||||
f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||
f.SetOutput(ioutil.Discard)
|
||||
|
||||
f.BoolVar(&c.ShowHelp, "h", false, "")
|
||||
f.BoolVar(&c.ShowHelp, "help", false, "")
|
||||
f.BoolVar(&c.ShowVersion, "version", false, "")
|
||||
|
||||
f.BoolVar(&c.Force, "f", false, "")
|
||||
f.BoolVar(&c.Force, "force", false, "")
|
||||
|
||||
f.BoolVar(&c.Verbose, "v", c.Verbose, "")
|
||||
f.BoolVar(&c.VeryVerbose, "vv", c.VeryVerbose, "")
|
||||
f.BoolVar(&c.VeryVeryVerbose, "vvv", c.VeryVeryVerbose, "")
|
||||
|
||||
f.StringVar(&peers, "peers", "", "")
|
||||
f.StringVar(&c.PeersFile, "peers-file", c.PeersFile, "")
|
||||
|
||||
f.StringVar(&c.Name, "name", c.Name, "")
|
||||
f.StringVar(&c.Addr, "addr", c.Addr, "")
|
||||
f.StringVar(&c.BootstrapURL, "bootstrap-url", c.BootstrapURL, "")
|
||||
f.StringVar(&c.BindAddr, "bind-addr", c.BindAddr, "")
|
||||
f.StringVar(&c.Peer.Addr, "peer-addr", c.Peer.Addr, "")
|
||||
f.StringVar(&c.Peer.BindAddr, "peer-bind-addr", c.Peer.BindAddr, "")
|
||||
|
||||
f.StringVar(&c.CAFile, "ca-file", c.CAFile, "")
|
||||
f.StringVar(&c.CertFile, "cert-file", c.CertFile, "")
|
||||
f.StringVar(&c.KeyFile, "key-file", c.KeyFile, "")
|
||||
|
||||
f.StringVar(&c.Peer.CAFile, "peer-ca-file", c.Peer.CAFile, "")
|
||||
f.StringVar(&c.Peer.CertFile, "peer-cert-file", c.Peer.CertFile, "")
|
||||
f.StringVar(&c.Peer.KeyFile, "peer-key-file", c.Peer.KeyFile, "")
|
||||
|
||||
f.StringVar(&c.DataDir, "data-dir", c.DataDir, "")
|
||||
f.IntVar(&c.MaxResultBuffer, "max-result-buffer", c.MaxResultBuffer, "")
|
||||
f.IntVar(&c.MaxRetryAttempts, "max-retry-attempts", c.MaxRetryAttempts, "")
|
||||
f.IntVar(&c.MaxClusterSize, "max-cluster-size", c.MaxClusterSize, "")
|
||||
f.IntVar(&c.Peer.HeartbeatTimeout, "peer-heartbeat-timeout", c.Peer.HeartbeatTimeout, "")
|
||||
f.IntVar(&c.Peer.ElectionTimeout, "peer-election-timeout", c.Peer.ElectionTimeout, "")
|
||||
|
||||
f.StringVar(&cors, "cors", "", "")
|
||||
|
||||
f.BoolVar(&c.Snapshot, "snapshot", c.Snapshot, "")
|
||||
f.IntVar(&c.SnapshotCount, "snapshot-count", c.SnapshotCount, "")
|
||||
f.StringVar(&c.CPUProfileFile, "cpuprofile", "", "")
|
||||
|
||||
f.StringVar(&c.strTrace, "trace", "", "")
|
||||
f.StringVar(&c.GraphiteHost, "graphite-host", "", "")
|
||||
|
||||
// BEGIN IGNORED FLAGS
|
||||
f.StringVar(&path, "config", "", "")
|
||||
// BEGIN IGNORED FLAGS
|
||||
|
||||
// BEGIN DEPRECATED FLAGS
|
||||
f.StringVar(&peers, "C", "", "(deprecated)")
|
||||
f.StringVar(&c.PeersFile, "CF", c.PeersFile, "(deprecated)")
|
||||
f.StringVar(&c.Name, "n", c.Name, "(deprecated)")
|
||||
f.StringVar(&c.Addr, "c", c.Addr, "(deprecated)")
|
||||
f.StringVar(&c.BindAddr, "cl", c.BindAddr, "(deprecated)")
|
||||
f.StringVar(&c.Peer.Addr, "s", c.Peer.Addr, "(deprecated)")
|
||||
f.StringVar(&c.Peer.BindAddr, "sl", c.Peer.BindAddr, "(deprecated)")
|
||||
f.StringVar(&c.Peer.CAFile, "serverCAFile", c.Peer.CAFile, "(deprecated)")
|
||||
f.StringVar(&c.Peer.CertFile, "serverCert", c.Peer.CertFile, "(deprecated)")
|
||||
f.StringVar(&c.Peer.KeyFile, "serverKey", c.Peer.KeyFile, "(deprecated)")
|
||||
f.StringVar(&c.CAFile, "clientCAFile", c.CAFile, "(deprecated)")
|
||||
f.StringVar(&c.CertFile, "clientCert", c.CertFile, "(deprecated)")
|
||||
f.StringVar(&c.KeyFile, "clientKey", c.KeyFile, "(deprecated)")
|
||||
f.StringVar(&c.DataDir, "d", c.DataDir, "(deprecated)")
|
||||
f.IntVar(&c.MaxResultBuffer, "m", c.MaxResultBuffer, "(deprecated)")
|
||||
f.IntVar(&c.MaxRetryAttempts, "r", c.MaxRetryAttempts, "(deprecated)")
|
||||
f.IntVar(&c.MaxClusterSize, "maxsize", c.MaxClusterSize, "(deprecated)")
|
||||
f.IntVar(&c.SnapshotCount, "snapshotCount", c.SnapshotCount, "(deprecated)")
|
||||
// END DEPRECATED FLAGS
|
||||
|
||||
if err := f.Parse(arguments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print deprecation warnings on STDERR.
|
||||
f.Visit(func(f *flag.Flag) {
|
||||
if len(newFlagNameLookup[f.Name]) > 0 {
|
||||
fmt.Fprintf(os.Stderr, "[deprecated] use -%s, not -%s\n", newFlagNameLookup[f.Name], f.Name)
|
||||
}
|
||||
})
|
||||
|
||||
// Convert some parameters to lists.
|
||||
if peers != "" {
|
||||
c.Peers = trimsplit(peers, ",")
|
||||
}
|
||||
if cors != "" {
|
||||
c.CorsOrigins = trimsplit(cors, ",")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadPeersFile loads the peers listed in the peers file.
|
||||
func (c *Config) LoadPeersFile() error {
|
||||
if c.PeersFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(c.PeersFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Peers file error: %s", err)
|
||||
}
|
||||
c.Peers = trimsplit(string(b), ",")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DataDirFromName sets the data dir from a machine name and issue a warning
|
||||
// that etcd is guessing.
|
||||
func (c *Config) DataDirFromName() {
|
||||
c.DataDir = c.Name + ".etcd"
|
||||
log.Warnf("Using the directory %s as the etcd curation directory because a directory was not specified. ", c.DataDir)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NameFromHostname sets the machine name from the hostname. This is to help
|
||||
// people get started without thinking up a name.
|
||||
func (c *Config) NameFromHostname() {
|
||||
host, err := os.Hostname()
|
||||
if err != nil && host == "" {
|
||||
log.Fatal("Node name required and hostname not set. e.g. '-name=name'")
|
||||
}
|
||||
c.Name = host
|
||||
}
|
||||
|
||||
// Reset removes all server configuration files.
|
||||
func (c *Config) Reset() error {
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "info")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "log")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "conf")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "snapshot")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reads the info file from the file system or initializes it based on the config.
|
||||
func (c *Config) Info() (*Info, error) {
|
||||
info := &Info{}
|
||||
path := filepath.Join(c.DataDir, "info")
|
||||
|
||||
// Open info file and read it out.
|
||||
f, err := os.Open(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
} else if f != nil {
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// If the file doesn't exist then initialize it.
|
||||
info.Name = strings.TrimSpace(c.Name)
|
||||
info.EtcdURL = c.Addr
|
||||
info.EtcdListenHost = c.BindAddr
|
||||
info.RaftURL = c.Peer.Addr
|
||||
info.RaftListenHost = c.Peer.BindAddr
|
||||
info.EtcdTLS = c.TLSInfo()
|
||||
info.RaftTLS = c.PeerTLSInfo()
|
||||
|
||||
// Write to file.
|
||||
f, err = os.Create(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := json.NewEncoder(f).Encode(info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// Sanitize cleans the input fields.
|
||||
func (c *Config) Sanitize() error {
|
||||
tlsConfig, err := c.TLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerTlsConfig, err := c.PeerTLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sanitize the URLs first.
|
||||
if c.Addr, err = sanitizeURL(c.Addr, tlsConfig.Scheme); err != nil {
|
||||
return fmt.Errorf("Advertised URL: %s", err)
|
||||
}
|
||||
if c.BindAddr, err = sanitizeBindAddr(c.BindAddr, c.Addr); err != nil {
|
||||
return fmt.Errorf("Listen Host: %s", err)
|
||||
}
|
||||
if c.Peer.Addr, err = sanitizeURL(c.Peer.Addr, peerTlsConfig.Scheme); err != nil {
|
||||
return fmt.Errorf("Peer Advertised URL: %s", err)
|
||||
}
|
||||
if c.Peer.BindAddr, err = sanitizeBindAddr(c.Peer.BindAddr, c.Peer.Addr); err != nil {
|
||||
return fmt.Errorf("Peer Listen Host: %s", err)
|
||||
}
|
||||
|
||||
// Only guess the machine name if there is no data dir specified
|
||||
// because the info file should have our name
|
||||
if c.Name == "" && c.DataDir == "" {
|
||||
c.NameFromHostname()
|
||||
}
|
||||
|
||||
if c.DataDir == "" && c.Name != "" && !c.ShowVersion && !c.ShowHelp {
|
||||
c.DataDirFromName()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TLSInfo retrieves a TLSInfo object for the client server.
|
||||
func (c *Config) TLSInfo() TLSInfo {
|
||||
return TLSInfo{
|
||||
CAFile: c.CAFile,
|
||||
CertFile: c.CertFile,
|
||||
KeyFile: c.KeyFile,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientTLSConfig generates the TLS configuration for the client server.
|
||||
func (c *Config) TLSConfig() (TLSConfig, error) {
|
||||
return c.TLSInfo().Config()
|
||||
}
|
||||
|
||||
// PeerTLSInfo retrieves a TLSInfo object for the peer server.
|
||||
func (c *Config) PeerTLSInfo() TLSInfo {
|
||||
return TLSInfo{
|
||||
CAFile: c.Peer.CAFile,
|
||||
CertFile: c.Peer.CertFile,
|
||||
KeyFile: c.Peer.KeyFile,
|
||||
}
|
||||
}
|
||||
|
||||
// PeerTLSConfig generates the TLS configuration for the peer server.
|
||||
func (c *Config) PeerTLSConfig() (TLSConfig, error) {
|
||||
return c.PeerTLSInfo().Config()
|
||||
}
|
||||
|
||||
// MetricsBucketName generates the name that should be used for a
|
||||
// corresponding MetricsBucket object
|
||||
func (c *Config) MetricsBucketName() string {
|
||||
return fmt.Sprintf("etcd.%s", c.Name)
|
||||
}
|
||||
|
||||
// Trace determines if any trace-level information should be emitted
|
||||
func (c *Config) Trace() bool {
|
||||
return c.strTrace == "*"
|
||||
}
|
||||
|
||||
// sanitizeURL will cleanup a host string in the format hostname[:port] and
|
||||
// attach a schema.
|
||||
func sanitizeURL(host string, defaultScheme string) (string, error) {
|
||||
// Blank URLs are fine input, just return it
|
||||
if len(host) == 0 {
|
||||
return host, nil
|
||||
}
|
||||
|
||||
p, err := url.Parse(host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Make sure the host is in Host:Port format
|
||||
_, _, err = net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
p = &url.URL{Host: host, Scheme: defaultScheme}
|
||||
return p.String(), nil
|
||||
}
|
||||
|
||||
// sanitizeBindAddr cleans up the BindAddr parameter and appends a port
|
||||
// if necessary based on the advertised port.
|
||||
func sanitizeBindAddr(bindAddr string, addr string) (string, error) {
|
||||
aurl, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If it is a valid host:port simply return with no further checks.
|
||||
bhost, bport, err := net.SplitHostPort(bindAddr)
|
||||
if err == nil && bhost != "" {
|
||||
return bindAddr, nil
|
||||
}
|
||||
|
||||
// SplitHostPort makes the host optional, but we don't want that.
|
||||
if bhost == "" && bport != "" {
|
||||
return "", fmt.Errorf("IP required can't use a port only")
|
||||
}
|
||||
|
||||
// bindAddr doesn't have a port if we reach here so take the port from the
|
||||
// advertised URL.
|
||||
_, aport, err := net.SplitHostPort(aurl.Host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return net.JoinHostPort(bindAddr, aport), nil
|
||||
}
|
||||
@@ -1,729 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/third_party/github.com/BurntSushi/toml"
|
||||
"github.com/coreos/etcd/third_party/github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Ensures that a configuration can be deserialized from TOML.
|
||||
func TestConfigTOML(t *testing.T) {
|
||||
content := `
|
||||
addr = "127.0.0.1:4002"
|
||||
ca_file = "/tmp/file.ca"
|
||||
cert_file = "/tmp/file.cert"
|
||||
cors = ["*"]
|
||||
cpu_profile_file = "XXX"
|
||||
data_dir = "/tmp/data"
|
||||
key_file = "/tmp/file.key"
|
||||
bind_addr = "127.0.0.1:4003"
|
||||
peers = ["coreos.com:4001", "coreos.com:4002"]
|
||||
peers_file = "/tmp/peers"
|
||||
max_cluster_size = 10
|
||||
max_result_buffer = 512
|
||||
max_retry_attempts = 5
|
||||
name = "test-name"
|
||||
snapshot = true
|
||||
verbose = true
|
||||
very_verbose = true
|
||||
|
||||
[peer]
|
||||
addr = "127.0.0.1:7002"
|
||||
ca_file = "/tmp/peer/file.ca"
|
||||
cert_file = "/tmp/peer/file.cert"
|
||||
key_file = "/tmp/peer/file.key"
|
||||
bind_addr = "127.0.0.1:7003"
|
||||
`
|
||||
c := NewConfig()
|
||||
_, err := toml.Decode(content, &c)
|
||||
assert.Nil(t, err, "")
|
||||
assert.Equal(t, c.Addr, "127.0.0.1:4002", "")
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
assert.Equal(t, c.CorsOrigins, []string{"*"}, "")
|
||||
assert.Equal(t, c.DataDir, "/tmp/data", "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
|
||||
assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
assert.Equal(t, c.PeersFile, "/tmp/peers", "")
|
||||
assert.Equal(t, c.MaxClusterSize, 10, "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 5, "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
assert.Equal(t, c.Peer.Addr, "127.0.0.1:7002", "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:7003", "")
|
||||
}
|
||||
|
||||
// Ensures that a configuration can be retrieved from environment variables.
|
||||
func TestConfigEnv(t *testing.T) {
|
||||
os.Setenv("ETCD_CA_FILE", "/tmp/file.ca")
|
||||
os.Setenv("ETCD_CERT_FILE", "/tmp/file.cert")
|
||||
os.Setenv("ETCD_CPU_PROFILE_FILE", "XXX")
|
||||
os.Setenv("ETCD_CORS", "localhost:4001,localhost:4002")
|
||||
os.Setenv("ETCD_DATA_DIR", "/tmp/data")
|
||||
os.Setenv("ETCD_KEY_FILE", "/tmp/file.key")
|
||||
os.Setenv("ETCD_BIND_ADDR", "127.0.0.1:4003")
|
||||
os.Setenv("ETCD_PEERS", "coreos.com:4001,coreos.com:4002")
|
||||
os.Setenv("ETCD_PEERS_FILE", "/tmp/peers")
|
||||
os.Setenv("ETCD_MAX_CLUSTER_SIZE", "10")
|
||||
os.Setenv("ETCD_MAX_RESULT_BUFFER", "512")
|
||||
os.Setenv("ETCD_MAX_RETRY_ATTEMPTS", "5")
|
||||
os.Setenv("ETCD_NAME", "test-name")
|
||||
os.Setenv("ETCD_SNAPSHOT", "true")
|
||||
os.Setenv("ETCD_VERBOSE", "1")
|
||||
os.Setenv("ETCD_VERY_VERBOSE", "yes")
|
||||
os.Setenv("ETCD_PEER_ADDR", "127.0.0.1:7002")
|
||||
os.Setenv("ETCD_PEER_CA_FILE", "/tmp/peer/file.ca")
|
||||
os.Setenv("ETCD_PEER_CERT_FILE", "/tmp/peer/file.cert")
|
||||
os.Setenv("ETCD_PEER_KEY_FILE", "/tmp/peer/file.key")
|
||||
os.Setenv("ETCD_PEER_BIND_ADDR", "127.0.0.1:7003")
|
||||
|
||||
c := NewConfig()
|
||||
c.LoadEnv()
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
assert.Equal(t, c.CorsOrigins, []string{"localhost:4001", "localhost:4002"}, "")
|
||||
assert.Equal(t, c.DataDir, "/tmp/data", "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
|
||||
assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
assert.Equal(t, c.PeersFile, "/tmp/peers", "")
|
||||
assert.Equal(t, c.MaxClusterSize, 10, "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 5, "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
assert.Equal(t, c.Peer.Addr, "127.0.0.1:7002", "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:7003", "")
|
||||
}
|
||||
|
||||
// Ensures that the "help" flag can be parsed.
|
||||
func TestConfigHelpFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-help"}), "")
|
||||
assert.True(t, c.ShowHelp)
|
||||
}
|
||||
|
||||
// Ensures that the abbreviated "help" flag can be parsed.
|
||||
func TestConfigAbbreviatedHelpFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-h"}), "")
|
||||
assert.True(t, c.ShowHelp)
|
||||
}
|
||||
|
||||
// Ensures that the "version" flag can be parsed.
|
||||
func TestConfigVersionFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-version"}), "")
|
||||
assert.True(t, c.ShowVersion)
|
||||
}
|
||||
|
||||
// Ensures that the "force config" flag can be parsed.
|
||||
func TestConfigForceFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-force"}), "")
|
||||
assert.True(t, c.Force)
|
||||
}
|
||||
|
||||
// Ensures that the abbreviated "force config" flag can be parsed.
|
||||
func TestConfigAbbreviatedForceFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-f"}), "")
|
||||
assert.True(t, c.Force)
|
||||
}
|
||||
|
||||
// Ensures that a the advertised url can be parsed from the environment.
|
||||
func TestConfigAddrEnv(t *testing.T) {
|
||||
withEnv("ETCD_ADDR", "127.0.0.1:4002", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Addr, "127.0.0.1:4002", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the advertised flag can be parsed.
|
||||
func TestConfigAddrFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-addr", "127.0.0.1:4002"}), "")
|
||||
assert.Equal(t, c.Addr, "127.0.0.1:4002", "")
|
||||
}
|
||||
|
||||
// Ensures that a the CA file can be parsed from the environment.
|
||||
func TestConfigCAFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_CA_FILE", "/tmp/file.ca", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the CA file flag can be parsed.
|
||||
func TestConfigCAFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-ca-file", "/tmp/file.ca"}), "")
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
}
|
||||
|
||||
// Ensures that a the CA file can be parsed from the environment.
|
||||
func TestConfigCertFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_CERT_FILE", "/tmp/file.cert", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Cert file flag can be parsed.
|
||||
func TestConfigCertFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-cert-file", "/tmp/file.cert"}), "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Key file can be parsed from the environment.
|
||||
func TestConfigKeyFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_KEY_FILE", "/tmp/file.key", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Key file flag can be parsed.
|
||||
func TestConfigKeyFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-key-file", "/tmp/file.key"}), "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host can be parsed from the environment.
|
||||
func TestConfigBindAddrEnv(t *testing.T) {
|
||||
withEnv("ETCD_BIND_ADDR", "127.0.0.1:4003", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host file flag can be parsed.
|
||||
func TestConfigBindAddrFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-bind-addr", "127.0.0.1:4003"}), "")
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host port overrides the advertised port
|
||||
func TestConfigBindAddrOverride(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-addr", "127.0.0.1:4009", "-bind-addr", "127.0.0.1:4010"}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4010", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host inherits its port from the advertised addr
|
||||
func TestConfigBindAddrInheritPort(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-addr", "127.0.0.1:4009", "-bind-addr", "127.0.0.1"}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4009", "")
|
||||
}
|
||||
|
||||
// Ensures that a port only argument errors out
|
||||
func TestConfigBindAddrErrorOnNoHost(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-addr", "127.0.0.1:4009", "-bind-addr", ":4010"}), "")
|
||||
assert.Error(t, c.Sanitize())
|
||||
}
|
||||
|
||||
// Ensures that the peers can be parsed from the environment.
|
||||
func TestConfigPeersEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEERS", "coreos.com:4001,coreos.com:4002", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peers flag can be parsed.
|
||||
func TestConfigPeersFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-peers", "coreos.com:4001,coreos.com:4002"}), "")
|
||||
assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
}
|
||||
|
||||
// Ensures that the Peers File can be parsed from the environment.
|
||||
func TestConfigPeersFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEERS_FILE", "/tmp/peers", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.PeersFile, "/tmp/peers", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peers File flag can be parsed.
|
||||
func TestConfigPeersFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-peers-file", "/tmp/peers"}), "")
|
||||
assert.Equal(t, c.PeersFile, "/tmp/peers", "")
|
||||
}
|
||||
|
||||
// Ensures that the Max Cluster Size can be parsed from the environment.
|
||||
func TestConfigMaxClusterSizeEnv(t *testing.T) {
|
||||
withEnv("ETCD_MAX_CLUSTER_SIZE", "5", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.MaxClusterSize, 5, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Max Cluster Size flag can be parsed.
|
||||
func TestConfigMaxClusterSizeFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-max-cluster-size", "5"}), "")
|
||||
assert.Equal(t, c.MaxClusterSize, 5, "")
|
||||
}
|
||||
|
||||
// Ensures that the Max Result Buffer can be parsed from the environment.
|
||||
func TestConfigMaxResultBufferEnv(t *testing.T) {
|
||||
withEnv("ETCD_MAX_RESULT_BUFFER", "512", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Max Result Buffer flag can be parsed.
|
||||
func TestConfigMaxResultBufferFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-max-result-buffer", "512"}), "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
}
|
||||
|
||||
// Ensures that the Max Retry Attempts can be parsed from the environment.
|
||||
func TestConfigMaxRetryAttemptsEnv(t *testing.T) {
|
||||
withEnv("ETCD_MAX_RETRY_ATTEMPTS", "10", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 10, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Max Retry Attempts flag can be parsed.
|
||||
func TestConfigMaxRetryAttemptsFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-max-retry-attempts", "10"}), "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 10, "")
|
||||
}
|
||||
|
||||
// Ensures that the Name can be parsed from the environment.
|
||||
func TestConfigNameEnv(t *testing.T) {
|
||||
withEnv("ETCD_NAME", "test-name", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Name flag can be parsed.
|
||||
func TestConfigNameFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-name", "test-name"}), "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
}
|
||||
|
||||
// Ensures that a Name gets guessed if not specified
|
||||
func TestConfigNameGuess(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
name, _ := os.Hostname()
|
||||
assert.Equal(t, c.Name, name, "")
|
||||
}
|
||||
|
||||
// Ensures that a DataDir gets guessed if not specified
|
||||
func TestConfigDataDirGuess(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{}), "")
|
||||
assert.Nil(t, c.Sanitize())
|
||||
name, _ := os.Hostname()
|
||||
assert.Equal(t, c.DataDir, name+".etcd", "")
|
||||
}
|
||||
|
||||
// Ensures that Snapshot can be parsed from the environment.
|
||||
func TestConfigSnapshotEnv(t *testing.T) {
|
||||
withEnv("ETCD_SNAPSHOT", "1", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Snapshot flag can be parsed.
|
||||
func TestConfigSnapshotFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-snapshot"}), "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
}
|
||||
|
||||
// Ensures that Verbose can be parsed from the environment.
|
||||
func TestConfigVerboseEnv(t *testing.T) {
|
||||
withEnv("ETCD_VERBOSE", "true", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Verbose flag can be parsed.
|
||||
func TestConfigVerboseFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-v"}), "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
}
|
||||
|
||||
// Ensures that Very Verbose can be parsed from the environment.
|
||||
func TestConfigVeryVerboseEnv(t *testing.T) {
|
||||
withEnv("ETCD_VERY_VERBOSE", "true", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Very Verbose flag can be parsed.
|
||||
func TestConfigVeryVerboseFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-vv"}), "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Advertised URL can be parsed from the environment.
|
||||
func TestConfigPeerAddrEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_ADDR", "localhost:7002", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.Addr, "localhost:7002", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peer Advertised URL flag can be parsed.
|
||||
func TestConfigPeerAddrFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-peer-addr", "localhost:7002"}), "")
|
||||
assert.Equal(t, c.Peer.Addr, "localhost:7002", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer CA File can be parsed from the environment.
|
||||
func TestConfigPeerCAFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_CA_FILE", "/tmp/peer/file.ca", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peer CA file flag can be parsed.
|
||||
func TestConfigPeerCAFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-peer-ca-file", "/tmp/peer/file.ca"}), "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Cert File can be parsed from the environment.
|
||||
func TestConfigPeerCertFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_CERT_FILE", "/tmp/peer/file.cert", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Cert file flag can be parsed.
|
||||
func TestConfigPeerCertFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-peer-cert-file", "/tmp/peer/file.cert"}), "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Key File can be parsed from the environment.
|
||||
func TestConfigPeerKeyFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_KEY_FILE", "/tmp/peer/file.key", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peer Key file flag can be parsed.
|
||||
func TestConfigPeerKeyFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-peer-key-file", "/tmp/peer/file.key"}), "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Listen Host can be parsed from the environment.
|
||||
func TestConfigPeerBindAddrEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_BIND_ADDR", "localhost:7004", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.BindAddr, "localhost:7004", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a bad flag returns an error.
|
||||
func TestConfigBadFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-no-such-flag"})
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), `flag provided but not defined: -no-such-flag`)
|
||||
}
|
||||
|
||||
// Ensures that a the Peer Listen Host file flag can be parsed.
|
||||
func TestConfigPeerBindAddrFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-peer-bind-addr", "127.0.0.1:4003"}), "")
|
||||
assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:4003", "")
|
||||
}
|
||||
|
||||
// Ensures that a system config field is overridden by a custom config field.
|
||||
func TestConfigCustomConfigOverrideSystemConfig(t *testing.T) {
|
||||
system := `addr = "127.0.0.1:5000"`
|
||||
custom := `addr = "127.0.0.1:6000"`
|
||||
withTempFile(system, func(p1 string) {
|
||||
withTempFile(custom, func(p2 string) {
|
||||
c := NewConfig()
|
||||
c.SystemPath = p1
|
||||
assert.Nil(t, c.Load([]string{"-config", p2}), "")
|
||||
assert.Equal(t, c.Addr, "http://127.0.0.1:6000", "")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a custom config field is overridden by an environment variable.
|
||||
func TestConfigEnvVarOverrideCustomConfig(t *testing.T) {
|
||||
os.Setenv("ETCD_PEER_ADDR", "127.0.0.1:8000")
|
||||
defer os.Setenv("ETCD_PEER_ADDR", "")
|
||||
|
||||
custom := `[peer]` + "\n" + `advertised_url = "127.0.0.1:9000"`
|
||||
withTempFile(custom, func(path string) {
|
||||
c := NewConfig()
|
||||
c.SystemPath = ""
|
||||
assert.Nil(t, c.Load([]string{"-config", path}), "")
|
||||
assert.Equal(t, c.Peer.Addr, "http://127.0.0.1:8000", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that an environment variable field is overridden by a command line argument.
|
||||
func TestConfigCLIArgsOverrideEnvVar(t *testing.T) {
|
||||
os.Setenv("ETCD_ADDR", "127.0.0.1:1000")
|
||||
defer os.Setenv("ETCD_ADDR", "")
|
||||
|
||||
c := NewConfig()
|
||||
c.SystemPath = ""
|
||||
assert.Nil(t, c.Load([]string{"-addr", "127.0.0.1:2000"}), "")
|
||||
assert.Equal(t, c.Addr, "http://127.0.0.1:2000", "")
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// DEPRECATED (v1)
|
||||
//--------------------------------------
|
||||
|
||||
func TestConfigDeprecatedAddrFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-c", "127.0.0.1:4002"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Addr, "127.0.0.1:4002")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -addr, not -c\n")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedBindAddrFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-cl", "127.0.0.1:4003"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.BindAddr, "127.0.0.1:4003", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -bind-addr, not -cl\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedCAFileFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-clientCAFile", "/tmp/file.ca"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -ca-file, not -clientCAFile\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedCertFileFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-clientCert", "/tmp/file.cert"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -cert-file, not -clientCert\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedKeyFileFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-clientKey", "/tmp/file.key"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -key-file, not -clientKey\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedPeersFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-C", "coreos.com:4001,coreos.com:4002"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Peers, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -peers, not -C\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedPeersFileFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-CF", "/tmp/machines"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.PeersFile, "/tmp/machines", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -peers-file, not -CF\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedMaxClusterSizeFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-maxsize", "5"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.MaxClusterSize, 5, "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -max-cluster-size, not -maxsize\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedMaxResultBufferFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-m", "512"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -max-result-buffer, not -m\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedMaxRetryAttemptsFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-r", "10"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.MaxRetryAttempts, 10, "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -max-retry-attempts, not -r\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedNameFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-n", "test-name"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -name, not -n\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedPeerAddrFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-s", "localhost:7002"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Peer.Addr, "localhost:7002", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -peer-addr, not -s\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedPeerBindAddrFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-sl", "127.0.0.1:4003"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:4003", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -peer-bind-addr, not -sl\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedPeerCAFileFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-serverCAFile", "/tmp/peer/file.ca"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -peer-ca-file, not -serverCAFile\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedPeerCertFileFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-serverCert", "/tmp/peer/file.cert"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -peer-cert-file, not -serverCert\n", "")
|
||||
}
|
||||
|
||||
func TestConfigDeprecatedPeerKeyFileFlag(t *testing.T) {
|
||||
_, stderr := capture(func() {
|
||||
c := NewConfig()
|
||||
err := c.LoadFlags([]string{"-serverKey", "/tmp/peer/file.key"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
})
|
||||
assert.Equal(t, stderr, "[deprecated] use -peer-key-file, not -serverKey\n", "")
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------
|
||||
|
||||
// Sets up the environment with a given environment variable set.
|
||||
func withEnv(key, value string, f func(c *Config)) {
|
||||
os.Setenv(key, value)
|
||||
defer os.Setenv(key, "")
|
||||
c := NewConfig()
|
||||
f(c)
|
||||
}
|
||||
|
||||
// Creates a temp file and calls a function with the context.
|
||||
func withTempFile(content string, fn func(string)) {
|
||||
f, _ := ioutil.TempFile("", "")
|
||||
f.WriteString(content)
|
||||
f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
fn(f.Name())
|
||||
}
|
||||
|
||||
// Captures STDOUT & STDERR and returns the output as strings.
|
||||
func capture(fn func()) (string, string) {
|
||||
// Create temp files.
|
||||
tmpout, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(tmpout.Name())
|
||||
tmperr, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(tmperr.Name())
|
||||
|
||||
stdout, stderr := os.Stdout, os.Stderr
|
||||
os.Stdout, os.Stderr = tmpout, tmperr
|
||||
|
||||
// Execute function argument and then reassign stdout/stderr.
|
||||
fn()
|
||||
os.Stdout, os.Stderr = stdout, stderr
|
||||
|
||||
// Close temp files and read them.
|
||||
tmpout.Close()
|
||||
bout, _ := ioutil.ReadFile(tmpout.Name())
|
||||
tmperr.Close()
|
||||
berr, _ := ioutil.ReadFile(tmperr.Name())
|
||||
|
||||
return string(bout), string(berr)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package server
|
||||
|
||||
const (
|
||||
// The amount of time (in ms) to elapse without a heartbeat before becoming a candidate
|
||||
defaultElectionTimeout = 200
|
||||
|
||||
// The frequency (in ms) by which heartbeats are sent to followers.
|
||||
defaultHeartbeatTimeout = 50
|
||||
)
|
||||
Reference in New Issue
Block a user