mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
etcd: Configuration file for etcd server.
Added a new command line option to etcd server to read in a YAML based configuration file. I've also added an example configuration file with comments and a set of test cases.
This commit is contained in:
parent
3bcd2b5b9f
commit
8bc5ab9f8d
@ -19,6 +19,7 @@ package etcdmain
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -28,7 +29,9 @@ import (
|
|||||||
"github.com/coreos/etcd/pkg/cors"
|
"github.com/coreos/etcd/pkg/cors"
|
||||||
"github.com/coreos/etcd/pkg/flags"
|
"github.com/coreos/etcd/pkg/flags"
|
||||||
"github.com/coreos/etcd/pkg/transport"
|
"github.com/coreos/etcd/pkg/transport"
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
"github.com/coreos/etcd/version"
|
"github.com/coreos/etcd/version"
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -44,6 +47,9 @@ const (
|
|||||||
|
|
||||||
defaultName = "default"
|
defaultName = "default"
|
||||||
defaultInitialAdvertisePeerURLs = "http://localhost:2380,http://localhost:7001"
|
defaultInitialAdvertisePeerURLs = "http://localhost:2380,http://localhost:7001"
|
||||||
|
defaultAdvertiseClientURLs = "http://localhost:2379,http://localhost:4001"
|
||||||
|
defaultListenPeerURLs = "http://localhost:2380,http://localhost:7001"
|
||||||
|
defaultListenClientURLs = "http://localhost:2379,http://localhost:4001"
|
||||||
|
|
||||||
// maxElectionMs specifies the maximum value of election timeout.
|
// maxElectionMs specifies the maximum value of election timeout.
|
||||||
// More details are listed in ../Documentation/tuning.md#time-parameters.
|
// More details are listed in ../Documentation/tuning.md#time-parameters.
|
||||||
@ -77,49 +83,61 @@ type config struct {
|
|||||||
|
|
||||||
// member
|
// member
|
||||||
corsInfo *cors.CORSInfo
|
corsInfo *cors.CORSInfo
|
||||||
dir string
|
|
||||||
walDir string
|
|
||||||
lpurls, lcurls []url.URL
|
lpurls, lcurls []url.URL
|
||||||
maxSnapFiles uint
|
Dir string `json:"data-dir"`
|
||||||
maxWalFiles uint
|
WalDir string `json:"wal-dir"`
|
||||||
name string
|
MaxSnapFiles uint `json:"max-snapshots"`
|
||||||
snapCount uint64
|
MaxWalFiles uint `json:"max-wals"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SnapCount uint64 `json:"snapshot-count"`
|
||||||
|
LPUrlsCfgFile string `json:"listen-peer-urls"`
|
||||||
|
LCUrlsCfgFile string `json:"listen-client-urls"`
|
||||||
|
CorsCfgFile string `json:"cors"`
|
||||||
|
|
||||||
// TickMs is the number of milliseconds between heartbeat ticks.
|
// TickMs is the number of milliseconds between heartbeat ticks.
|
||||||
// TODO: decouple tickMs and heartbeat tick (current heartbeat tick = 1).
|
// TODO: decouple tickMs and heartbeat tick (current heartbeat tick = 1).
|
||||||
// make ticks a cluster wide configuration.
|
// make ticks a cluster wide configuration.
|
||||||
TickMs uint
|
TickMs uint `json:"heartbeat-interval"`
|
||||||
ElectionMs uint
|
ElectionMs uint `json:"election-timeout"`
|
||||||
quotaBackendBytes int64
|
QuotaBackendBytes int64 `json:"quota-backend-bytes"`
|
||||||
|
|
||||||
// clustering
|
// clustering
|
||||||
apurls, acurls []url.URL
|
apurls, acurls []url.URL
|
||||||
clusterState *flags.StringsFlag
|
clusterState *flags.StringsFlag
|
||||||
dnsCluster string
|
DnsCluster string `json:"discovery-srv"`
|
||||||
dproxy string
|
Dproxy string `json:"discovery-proxy"`
|
||||||
durl string
|
Durl string `json:"discovery"`
|
||||||
fallback *flags.StringsFlag
|
fallback *flags.StringsFlag
|
||||||
initialCluster string
|
InitialCluster string `json:"initial-cluster"`
|
||||||
initialClusterToken string
|
InitialClusterToken string `json:"initial-cluster-token"`
|
||||||
strictReconfigCheck bool
|
StrictReconfigCheck bool `json:"strict-reconfig-check"`
|
||||||
|
ApurlsCfgFile string `json:"initial-advertise-peer-urls"`
|
||||||
|
AcurlsCfgFile string `json:"advertise-client-urls"`
|
||||||
|
ClusterStateCfgFile string `json:"initial-cluster-state"`
|
||||||
|
FallbackCfgFile string `json:"discovery-fallback"`
|
||||||
|
|
||||||
// proxy
|
// proxy
|
||||||
proxy *flags.StringsFlag
|
proxy *flags.StringsFlag
|
||||||
proxyFailureWaitMs uint
|
ProxyFailureWaitMs uint `json:"proxy-failure-wait"`
|
||||||
proxyRefreshIntervalMs uint
|
ProxyRefreshIntervalMs uint `json:"proxy-refresh-interval"`
|
||||||
proxyDialTimeoutMs uint
|
ProxyDialTimeoutMs uint `json:"proxy-dial-timeout"`
|
||||||
proxyWriteTimeoutMs uint
|
ProxyWriteTimeoutMs uint `json:"proxy-write-timeout"`
|
||||||
proxyReadTimeoutMs uint
|
ProxyReadTimeoutMs uint `json:"proxy-read-timeout"`
|
||||||
|
ProxyCfgFile string `json:"proxy"`
|
||||||
|
|
||||||
// security
|
// security
|
||||||
clientTLSInfo, peerTLSInfo transport.TLSInfo
|
clientTLSInfo, peerTLSInfo transport.TLSInfo
|
||||||
clientAutoTLS, peerAutoTLS bool
|
ClientAutoTLS bool
|
||||||
|
PeerAutoTLS bool
|
||||||
|
ClientSecurityCfgFile securityConfig `json:"client-transport-security"`
|
||||||
|
PeerSecurityCfgFile securityConfig `json:"peer-transport-security"`
|
||||||
|
|
||||||
// logging
|
// Debug logging
|
||||||
debug bool
|
Debug bool `json:"debug"`
|
||||||
logPkgLevels string
|
LogPkgLevels string `json:"log-package-levels"`
|
||||||
|
|
||||||
// unsafe
|
// ForceNewCluster is unsafe
|
||||||
forceNewCluster bool
|
ForceNewCluster bool `json:"force-new-cluster"`
|
||||||
|
|
||||||
printVersion bool
|
printVersion bool
|
||||||
|
|
||||||
@ -127,9 +145,20 @@ type config struct {
|
|||||||
|
|
||||||
enablePprof bool
|
enablePprof bool
|
||||||
|
|
||||||
|
configFile string
|
||||||
|
|
||||||
ignored []string
|
ignored []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type securityConfig struct {
|
||||||
|
CAFile string `json:"ca-file"`
|
||||||
|
CertFile string `json:"cert-file"`
|
||||||
|
KeyFile string `json:"key-file"`
|
||||||
|
CertAuth bool `json:"client-cert-auth"`
|
||||||
|
TrustedCAFile string `json:"trusted-ca-file"`
|
||||||
|
AutoTLS bool `json:"auto-tls"`
|
||||||
|
}
|
||||||
|
|
||||||
func NewConfig() *config {
|
func NewConfig() *config {
|
||||||
cfg := &config{
|
cfg := &config{
|
||||||
corsInfo: &cors.CORSInfo{},
|
corsInfo: &cors.CORSInfo{},
|
||||||
@ -155,39 +184,41 @@ func NewConfig() *config {
|
|||||||
fmt.Println(usageline)
|
fmt.Println(usageline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs.StringVar(&cfg.configFile, "config-file", "", "Path to the server configuration file")
|
||||||
|
|
||||||
// member
|
// member
|
||||||
fs.Var(cfg.corsInfo, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
|
fs.Var(cfg.corsInfo, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
|
||||||
fs.StringVar(&cfg.dir, "data-dir", "", "Path to the data directory.")
|
fs.StringVar(&cfg.Dir, "data-dir", "", "Path to the data directory.")
|
||||||
fs.StringVar(&cfg.walDir, "wal-dir", "", "Path to the dedicated wal directory.")
|
fs.StringVar(&cfg.WalDir, "wal-dir", "", "Path to the dedicated wal directory.")
|
||||||
fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of URLs to listen on for peer traffic.")
|
fs.Var(flags.NewURLsValue(defaultListenPeerURLs), "listen-peer-urls", "List of URLs to listen on for peer traffic.")
|
||||||
fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of URLs to listen on for client traffic.")
|
fs.Var(flags.NewURLsValue(defaultListenClientURLs), "listen-client-urls", "List of URLs to listen on for client traffic.")
|
||||||
fs.UintVar(&cfg.maxSnapFiles, "max-snapshots", defaultMaxSnapshots, "Maximum number of snapshot files to retain (0 is unlimited).")
|
fs.UintVar(&cfg.MaxSnapFiles, "max-snapshots", defaultMaxSnapshots, "Maximum number of snapshot files to retain (0 is unlimited).")
|
||||||
fs.UintVar(&cfg.maxWalFiles, "max-wals", defaultMaxWALs, "Maximum number of wal files to retain (0 is unlimited).")
|
fs.UintVar(&cfg.MaxWalFiles, "max-wals", defaultMaxWALs, "Maximum number of wal files to retain (0 is unlimited).")
|
||||||
fs.StringVar(&cfg.name, "name", defaultName, "Human-readable name for this member.")
|
fs.StringVar(&cfg.Name, "name", defaultName, "Human-readable name for this member.")
|
||||||
fs.Uint64Var(&cfg.snapCount, "snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot to disk.")
|
fs.Uint64Var(&cfg.SnapCount, "snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot to disk.")
|
||||||
fs.UintVar(&cfg.TickMs, "heartbeat-interval", 100, "Time (in milliseconds) of a heartbeat interval.")
|
fs.UintVar(&cfg.TickMs, "heartbeat-interval", 100, "Time (in milliseconds) of a heartbeat interval.")
|
||||||
fs.UintVar(&cfg.ElectionMs, "election-timeout", 1000, "Time (in milliseconds) for an election to timeout.")
|
fs.UintVar(&cfg.ElectionMs, "election-timeout", 1000, "Time (in milliseconds) for an election to timeout.")
|
||||||
fs.Int64Var(&cfg.quotaBackendBytes, "quota-backend-bytes", 0, "Raise alarms when backend size exceeds the given quota. 0 means use the default quota.")
|
fs.Int64Var(&cfg.QuotaBackendBytes, "quota-backend-bytes", 0, "Raise alarms when backend size exceeds the given quota. 0 means use the default quota.")
|
||||||
|
|
||||||
// clustering
|
// clustering
|
||||||
fs.Var(flags.NewURLsValue(defaultInitialAdvertisePeerURLs), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster.")
|
fs.Var(flags.NewURLsValue(defaultInitialAdvertisePeerURLs), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster.")
|
||||||
fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the public.")
|
fs.Var(flags.NewURLsValue(defaultAdvertiseClientURLs), "advertise-client-urls", "List of this member's client URLs to advertise to the public.")
|
||||||
fs.StringVar(&cfg.durl, "discovery", "", "Discovery URL used to bootstrap the cluster.")
|
fs.StringVar(&cfg.Durl, "discovery", "", "Discovery URL used to bootstrap the cluster.")
|
||||||
fs.Var(cfg.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %s", strings.Join(cfg.fallback.Values, ", ")))
|
fs.Var(cfg.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %s", strings.Join(cfg.fallback.Values, ", ")))
|
||||||
if err := cfg.fallback.Set(fallbackFlagProxy); err != nil {
|
if err := cfg.fallback.Set(fallbackFlagProxy); err != nil {
|
||||||
// Should never happen.
|
// Should never happen.
|
||||||
plog.Panicf("unexpected error setting up discovery-fallback flag: %v", err)
|
plog.Panicf("unexpected error setting up discovery-fallback flag: %v", err)
|
||||||
}
|
}
|
||||||
fs.StringVar(&cfg.dproxy, "discovery-proxy", "", "HTTP proxy to use for traffic to discovery service.")
|
fs.StringVar(&cfg.Dproxy, "discovery-proxy", "", "HTTP proxy to use for traffic to discovery service.")
|
||||||
fs.StringVar(&cfg.dnsCluster, "discovery-srv", "", "DNS domain used to bootstrap initial cluster.")
|
fs.StringVar(&cfg.DnsCluster, "discovery-srv", "", "DNS domain used to bootstrap initial cluster.")
|
||||||
fs.StringVar(&cfg.initialCluster, "initial-cluster", initialClusterFromName(defaultName), "Initial cluster configuration for bootstrapping.")
|
fs.StringVar(&cfg.InitialCluster, "initial-cluster", initialClusterFromName(defaultName), "Initial cluster configuration for bootstrapping.")
|
||||||
fs.StringVar(&cfg.initialClusterToken, "initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap.")
|
fs.StringVar(&cfg.InitialClusterToken, "initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap.")
|
||||||
fs.Var(cfg.clusterState, "initial-cluster-state", "Initial cluster state ('new' or 'existing').")
|
fs.Var(cfg.clusterState, "initial-cluster-state", "Initial cluster state ('new' or 'existing').")
|
||||||
if err := cfg.clusterState.Set(clusterStateFlagNew); err != nil {
|
if err := cfg.clusterState.Set(clusterStateFlagNew); err != nil {
|
||||||
// Should never happen.
|
// Should never happen.
|
||||||
plog.Panicf("unexpected error setting up clusterStateFlag: %v", err)
|
plog.Panicf("unexpected error setting up clusterStateFlag: %v", err)
|
||||||
}
|
}
|
||||||
fs.BoolVar(&cfg.strictReconfigCheck, "strict-reconfig-check", false, "Reject reconfiguration requests that would cause quorum loss.")
|
fs.BoolVar(&cfg.StrictReconfigCheck, "strict-reconfig-check", false, "Reject reconfiguration requests that would cause quorum loss.")
|
||||||
|
|
||||||
// proxy
|
// proxy
|
||||||
fs.Var(cfg.proxy, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(cfg.proxy.Values, ", ")))
|
fs.Var(cfg.proxy, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(cfg.proxy.Values, ", ")))
|
||||||
@ -195,11 +226,11 @@ func NewConfig() *config {
|
|||||||
// Should never happen.
|
// Should never happen.
|
||||||
plog.Panicf("unexpected error setting up proxyFlag: %v", err)
|
plog.Panicf("unexpected error setting up proxyFlag: %v", err)
|
||||||
}
|
}
|
||||||
fs.UintVar(&cfg.proxyFailureWaitMs, "proxy-failure-wait", 5000, "Time (in milliseconds) an endpoint will be held in a failed state.")
|
fs.UintVar(&cfg.ProxyFailureWaitMs, "proxy-failure-wait", 5000, "Time (in milliseconds) an endpoint will be held in a failed state.")
|
||||||
fs.UintVar(&cfg.proxyRefreshIntervalMs, "proxy-refresh-interval", 30000, "Time (in milliseconds) of the endpoints refresh interval.")
|
fs.UintVar(&cfg.ProxyRefreshIntervalMs, "proxy-refresh-interval", 30000, "Time (in milliseconds) of the endpoints refresh interval.")
|
||||||
fs.UintVar(&cfg.proxyDialTimeoutMs, "proxy-dial-timeout", 1000, "Time (in milliseconds) for a dial to timeout.")
|
fs.UintVar(&cfg.ProxyDialTimeoutMs, "proxy-dial-timeout", 1000, "Time (in milliseconds) for a dial to timeout.")
|
||||||
fs.UintVar(&cfg.proxyWriteTimeoutMs, "proxy-write-timeout", 5000, "Time (in milliseconds) for a write to timeout.")
|
fs.UintVar(&cfg.ProxyWriteTimeoutMs, "proxy-write-timeout", 5000, "Time (in milliseconds) for a write to timeout.")
|
||||||
fs.UintVar(&cfg.proxyReadTimeoutMs, "proxy-read-timeout", 0, "Time (in milliseconds) for a read to timeout.")
|
fs.UintVar(&cfg.ProxyReadTimeoutMs, "proxy-read-timeout", 0, "Time (in milliseconds) for a read to timeout.")
|
||||||
|
|
||||||
// security
|
// security
|
||||||
fs.StringVar(&cfg.clientTLSInfo.CAFile, "ca-file", "", "DEPRECATED: Path to the client server TLS CA file.")
|
fs.StringVar(&cfg.clientTLSInfo.CAFile, "ca-file", "", "DEPRECATED: Path to the client server TLS CA file.")
|
||||||
@ -207,20 +238,20 @@ func NewConfig() *config {
|
|||||||
fs.StringVar(&cfg.clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
|
fs.StringVar(&cfg.clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
|
||||||
fs.BoolVar(&cfg.clientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.")
|
fs.BoolVar(&cfg.clientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.")
|
||||||
fs.StringVar(&cfg.clientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA key file.")
|
fs.StringVar(&cfg.clientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA key file.")
|
||||||
fs.BoolVar(&cfg.clientAutoTLS, "auto-tls", false, "Client TLS using generated certificates")
|
fs.BoolVar(&cfg.ClientAutoTLS, "auto-tls", false, "Client TLS using generated certificates")
|
||||||
fs.StringVar(&cfg.peerTLSInfo.CAFile, "peer-ca-file", "", "DEPRECATED: Path to the peer server TLS CA file.")
|
fs.StringVar(&cfg.peerTLSInfo.CAFile, "peer-ca-file", "", "DEPRECATED: Path to the peer server TLS CA file.")
|
||||||
fs.StringVar(&cfg.peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
|
fs.StringVar(&cfg.peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
|
||||||
fs.StringVar(&cfg.peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
|
fs.StringVar(&cfg.peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
|
||||||
fs.BoolVar(&cfg.peerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.")
|
fs.BoolVar(&cfg.peerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.")
|
||||||
fs.StringVar(&cfg.peerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.")
|
fs.StringVar(&cfg.peerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.")
|
||||||
fs.BoolVar(&cfg.peerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates")
|
fs.BoolVar(&cfg.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates")
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
fs.BoolVar(&cfg.debug, "debug", false, "Enable debug-level logging for etcd.")
|
fs.BoolVar(&cfg.Debug, "debug", false, "Enable debug-level logging for etcd.")
|
||||||
fs.StringVar(&cfg.logPkgLevels, "log-package-levels", "", "Specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').")
|
fs.StringVar(&cfg.LogPkgLevels, "log-package-levels", "", "Specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').")
|
||||||
|
|
||||||
// unsafe
|
// unsafe
|
||||||
fs.BoolVar(&cfg.forceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster.")
|
fs.BoolVar(&cfg.ForceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster.")
|
||||||
|
|
||||||
// version
|
// version
|
||||||
fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.")
|
fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.")
|
||||||
@ -268,25 +299,23 @@ func (cfg *config) Parse(arguments []string) error {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if cfg.configFile != "" {
|
||||||
|
plog.Infof("Loading server configuration from %q", cfg.configFile)
|
||||||
|
err = cfg.configFromFile()
|
||||||
|
} else {
|
||||||
|
err = cfg.configFromCmdLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *config) configFromCmdLine() error {
|
||||||
err := flags.SetFlagsFromEnv("ETCD", cfg.FlagSet)
|
err := flags.SetFlagsFromEnv("ETCD", cfg.FlagSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Fatalf("%v", err)
|
plog.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
set := make(map[string]bool)
|
|
||||||
cfg.FlagSet.Visit(func(f *flag.Flag) {
|
|
||||||
set[f.Name] = true
|
|
||||||
})
|
|
||||||
nSet := 0
|
|
||||||
for _, v := range []bool{set["discovery"], set["initial-cluster"], set["discovery-srv"]} {
|
|
||||||
if v {
|
|
||||||
nSet += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if nSet > 1 {
|
|
||||||
return ErrConflictBootstrapFlags
|
|
||||||
}
|
|
||||||
|
|
||||||
flags.SetBindAddrFromAddr(cfg.FlagSet, "peer-bind-addr", "peer-addr")
|
flags.SetBindAddrFromAddr(cfg.FlagSet, "peer-bind-addr", "peer-addr")
|
||||||
flags.SetBindAddrFromAddr(cfg.FlagSet, "bind-addr", "addr")
|
flags.SetBindAddrFromAddr(cfg.FlagSet, "bind-addr", "addr")
|
||||||
|
|
||||||
@ -307,16 +336,126 @@ func (cfg *config) Parse(arguments []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cfg.validateConfig(func(field string) bool {
|
||||||
|
return flags.IsSet(cfg.FlagSet, field)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *config) configFromFile() error {
|
||||||
|
b, err := ioutil.ReadFile(cfg.configFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(b, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.LPUrlsCfgFile != "" {
|
||||||
|
u, err := types.NewURLs(strings.Split(cfg.LPUrlsCfgFile, ","))
|
||||||
|
if err != nil {
|
||||||
|
plog.Fatalf("unexpected error setting up listen-peer-urls: %v", err)
|
||||||
|
}
|
||||||
|
cfg.lpurls = []url.URL(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.LCUrlsCfgFile != "" {
|
||||||
|
u, err := types.NewURLs(strings.Split(cfg.LCUrlsCfgFile, ","))
|
||||||
|
if err != nil {
|
||||||
|
plog.Fatalf("unexpected error setting up listen-client-urls: %v", err)
|
||||||
|
}
|
||||||
|
cfg.lcurls = []url.URL(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.CorsCfgFile != "" {
|
||||||
|
if err := cfg.corsInfo.Set(cfg.CorsCfgFile); err != nil {
|
||||||
|
plog.Panicf("unexpected error setting up cors: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ApurlsCfgFile != "" {
|
||||||
|
u, err := types.NewURLs(strings.Split(cfg.ApurlsCfgFile, ","))
|
||||||
|
if err != nil {
|
||||||
|
plog.Fatalf("unexpected error setting up initial-advertise-peer-urls: %v", err)
|
||||||
|
}
|
||||||
|
cfg.apurls = []url.URL(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.AcurlsCfgFile != "" {
|
||||||
|
u, err := types.NewURLs(strings.Split(cfg.AcurlsCfgFile, ","))
|
||||||
|
if err != nil {
|
||||||
|
plog.Fatalf("unexpected error setting up advertise-peer-urls: %v", err)
|
||||||
|
}
|
||||||
|
cfg.acurls = []url.URL(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ClusterStateCfgFile != "" {
|
||||||
|
if err := cfg.clusterState.Set(cfg.ClusterStateCfgFile); err != nil {
|
||||||
|
plog.Panicf("unexpected error setting up clusterStateFlag: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.FallbackCfgFile != "" {
|
||||||
|
if err := cfg.fallback.Set(cfg.FallbackCfgFile); err != nil {
|
||||||
|
plog.Panicf("unexpected error setting up discovery-fallback flag: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ProxyCfgFile != "" {
|
||||||
|
if err := cfg.proxy.Set(cfg.ProxyCfgFile); err != nil {
|
||||||
|
plog.Panicf("unexpected error setting up proxyFlag: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
copySecurityDetails := func(tls *transport.TLSInfo, ysc *securityConfig) {
|
||||||
|
tls.CAFile = ysc.CAFile
|
||||||
|
tls.CertFile = ysc.CertFile
|
||||||
|
tls.KeyFile = ysc.KeyFile
|
||||||
|
tls.ClientCertAuth = ysc.CertAuth
|
||||||
|
tls.TrustedCAFile = ysc.TrustedCAFile
|
||||||
|
}
|
||||||
|
copySecurityDetails(&cfg.clientTLSInfo, &cfg.ClientSecurityCfgFile)
|
||||||
|
copySecurityDetails(&cfg.peerTLSInfo, &cfg.PeerSecurityCfgFile)
|
||||||
|
cfg.ClientAutoTLS = cfg.ClientSecurityCfgFile.AutoTLS
|
||||||
|
cfg.PeerAutoTLS = cfg.PeerSecurityCfgFile.AutoTLS
|
||||||
|
|
||||||
|
fieldsToBeChecked := map[string]bool{
|
||||||
|
"discovery": (cfg.Durl != ""),
|
||||||
|
"listen-client-urls": (cfg.LCUrlsCfgFile != ""),
|
||||||
|
"advertise-client-urls": (cfg.AcurlsCfgFile != ""),
|
||||||
|
"initial-cluster": (cfg.InitialCluster != ""),
|
||||||
|
"discovery-srv": (cfg.DnsCluster != ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.validateConfig(func(field string) bool {
|
||||||
|
return fieldsToBeChecked[field]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *config) validateConfig(isSet func(field string) bool) error {
|
||||||
// when etcd runs in member mode user needs to set --advertise-client-urls if --listen-client-urls is set.
|
// when etcd runs in member mode user needs to set --advertise-client-urls if --listen-client-urls is set.
|
||||||
// TODO(yichengq): check this for joining through discovery service case
|
// TODO(yichengq): check this for joining through discovery service case
|
||||||
mayFallbackToProxy := flags.IsSet(cfg.FlagSet, "discovery") && cfg.fallback.String() == fallbackFlagProxy
|
mayFallbackToProxy := isSet("discovery") && cfg.fallback.String() == fallbackFlagProxy
|
||||||
mayBeProxy := cfg.proxy.String() != proxyFlagOff || mayFallbackToProxy
|
mayBeProxy := cfg.proxy.String() != proxyFlagOff || mayFallbackToProxy
|
||||||
if !mayBeProxy {
|
if !mayBeProxy {
|
||||||
if flags.IsSet(cfg.FlagSet, "listen-client-urls") && !flags.IsSet(cfg.FlagSet, "advertise-client-urls") {
|
if isSet("listen-client-urls") && !isSet("advertise-client-urls") {
|
||||||
return errUnsetAdvertiseClientURLsFlag
|
return errUnsetAdvertiseClientURLsFlag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if conflicting flags are passed.
|
||||||
|
nSet := 0
|
||||||
|
for _, v := range []bool{isSet("discovery"), isSet("initial-cluster"), isSet("discovery-srv")} {
|
||||||
|
if v {
|
||||||
|
nSet += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nSet > 1 {
|
||||||
|
return ErrConflictBootstrapFlags
|
||||||
|
}
|
||||||
|
|
||||||
if 5*cfg.TickMs > cfg.ElectionMs {
|
if 5*cfg.TickMs > cfg.ElectionMs {
|
||||||
return fmt.Errorf("--election-timeout[%vms] should be at least as 5 times as --heartbeat-interval[%vms]", cfg.ElectionMs, cfg.TickMs)
|
return fmt.Errorf("--election-timeout[%vms] should be at least as 5 times as --heartbeat-interval[%vms]", cfg.ElectionMs, cfg.TickMs)
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,15 @@
|
|||||||
package etcdmain
|
package etcdmain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigParsingMemberFlags(t *testing.T) {
|
func TestConfigParsingMemberFlags(t *testing.T) {
|
||||||
@ -32,42 +38,56 @@ func TestConfigParsingMemberFlags(t *testing.T) {
|
|||||||
// it should be set if -listen-client-urls is set
|
// it should be set if -listen-client-urls is set
|
||||||
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
||||||
}
|
}
|
||||||
wcfg := &config{
|
|
||||||
dir: "testdir",
|
|
||||||
lpurls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
|
|
||||||
lcurls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
|
|
||||||
maxSnapFiles: 10,
|
|
||||||
maxWalFiles: 10,
|
|
||||||
name: "testname",
|
|
||||||
snapCount: 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := NewConfig()
|
cfg := NewConfig()
|
||||||
err := cfg.Parse(args)
|
err := cfg.Parse(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if cfg.dir != wcfg.dir {
|
|
||||||
t.Errorf("dir = %v, want %v", cfg.dir, wcfg.dir)
|
validateMemberFlags(t, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigFileMemberFields(t *testing.T) {
|
||||||
|
yc := struct {
|
||||||
|
Dir string `json:"data-dir"`
|
||||||
|
MaxSnapFiles uint `json:"max-snapshots"`
|
||||||
|
MaxWalFiles uint `json:"max-wals"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SnapCount uint64 `json:"snapshot-count"`
|
||||||
|
LPUrls string `json:"listen-peer-urls"`
|
||||||
|
LCUrls string `json:"listen-client-urls"`
|
||||||
|
AcurlsCfgFile string `json:"advertise-client-urls"`
|
||||||
|
}{
|
||||||
|
"testdir",
|
||||||
|
10,
|
||||||
|
10,
|
||||||
|
"testname",
|
||||||
|
10,
|
||||||
|
"http://localhost:8000,https://localhost:8001",
|
||||||
|
"http://localhost:7000,https://localhost:7001",
|
||||||
|
"http://localhost:7000,https://localhost:7001",
|
||||||
}
|
}
|
||||||
if cfg.maxSnapFiles != wcfg.maxSnapFiles {
|
|
||||||
t.Errorf("maxsnap = %v, want %v", cfg.maxSnapFiles, wcfg.maxSnapFiles)
|
b, err := yaml.Marshal(&yc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if cfg.maxWalFiles != wcfg.maxWalFiles {
|
|
||||||
t.Errorf("maxwal = %v, want %v", cfg.maxWalFiles, wcfg.maxWalFiles)
|
tmpfile := mustCreateCfgFile(t, b)
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
fmt.Sprintf("--config-file=%s", tmpfile.Name()),
|
||||||
}
|
}
|
||||||
if cfg.name != wcfg.name {
|
|
||||||
t.Errorf("name = %v, want %v", cfg.name, wcfg.name)
|
cfg := NewConfig()
|
||||||
}
|
err = cfg.Parse(args)
|
||||||
if cfg.snapCount != wcfg.snapCount {
|
if err != nil {
|
||||||
t.Errorf("snapcount = %v, want %v", cfg.snapCount, wcfg.snapCount)
|
t.Fatal(err)
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(cfg.lpurls, wcfg.lpurls) {
|
|
||||||
t.Errorf("listen-peer-urls = %v, want %v", cfg.lpurls, wcfg.lpurls)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(cfg.lcurls, wcfg.lcurls) {
|
|
||||||
t.Errorf("listen-client-urls = %v, want %v", cfg.lcurls, wcfg.lcurls)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateMemberFlags(t, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigParsingClusteringFlags(t *testing.T) {
|
func TestConfigParsingClusteringFlags(t *testing.T) {
|
||||||
@ -79,37 +99,51 @@ func TestConfigParsingClusteringFlags(t *testing.T) {
|
|||||||
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
||||||
"-discovery-fallback=exit",
|
"-discovery-fallback=exit",
|
||||||
}
|
}
|
||||||
wcfg := NewConfig()
|
|
||||||
wcfg.apurls = []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}}
|
|
||||||
wcfg.acurls = []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}}
|
|
||||||
wcfg.clusterState.Set(clusterStateFlagExisting)
|
|
||||||
wcfg.fallback.Set(fallbackFlagExit)
|
|
||||||
wcfg.initialCluster = "0=http://localhost:8000"
|
|
||||||
wcfg.initialClusterToken = "etcdtest"
|
|
||||||
|
|
||||||
cfg := NewConfig()
|
cfg := NewConfig()
|
||||||
err := cfg.Parse(args)
|
err := cfg.Parse(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if cfg.clusterState.String() != wcfg.clusterState.String() {
|
|
||||||
t.Errorf("clusterState = %v, want %v", cfg.clusterState, wcfg.clusterState)
|
validateClusteringFlags(t, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigFileClusteringFields(t *testing.T) {
|
||||||
|
yc := struct {
|
||||||
|
InitialCluster string `json:"initial-cluster"`
|
||||||
|
ClusterState string `json:"initial-cluster-state"`
|
||||||
|
InitialClusterToken string `json:"initial-cluster-token"`
|
||||||
|
Apurls string `json:"initial-advertise-peer-urls"`
|
||||||
|
Acurls string `json:"advertise-client-urls"`
|
||||||
|
Fallback string `json:"discovery-fallback"`
|
||||||
|
}{
|
||||||
|
"0=http://localhost:8000",
|
||||||
|
"existing",
|
||||||
|
"etcdtest",
|
||||||
|
"http://localhost:8000,https://localhost:8001",
|
||||||
|
"http://localhost:7000,https://localhost:7001",
|
||||||
|
"exit",
|
||||||
}
|
}
|
||||||
if cfg.fallback.String() != wcfg.fallback.String() {
|
|
||||||
t.Errorf("fallback = %v, want %v", cfg.fallback, wcfg.fallback)
|
b, err := yaml.Marshal(&yc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if cfg.initialCluster != wcfg.initialCluster {
|
|
||||||
t.Errorf("initialCluster = %v, want %v", cfg.initialCluster, wcfg.initialCluster)
|
tmpfile := mustCreateCfgFile(t, b)
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
fmt.Sprintf("--config-file=%s", tmpfile.Name()),
|
||||||
}
|
}
|
||||||
if cfg.initialClusterToken != wcfg.initialClusterToken {
|
cfg := NewConfig()
|
||||||
t.Errorf("initialClusterToken = %v, want %v", cfg.initialClusterToken, wcfg.initialClusterToken)
|
err = cfg.Parse(args)
|
||||||
}
|
if err != nil {
|
||||||
if !reflect.DeepEqual(cfg.apurls, wcfg.apurls) {
|
t.Fatal(err)
|
||||||
t.Errorf("initial-advertise-peer-urls = %v, want %v", cfg.lpurls, wcfg.lpurls)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(cfg.acurls, wcfg.acurls) {
|
|
||||||
t.Errorf("advertise-client-urls = %v, want %v", cfg.lcurls, wcfg.lcurls)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateClusteringFlags(t, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigParsingOtherFlags(t *testing.T) {
|
func TestConfigParsingOtherFlags(t *testing.T) {
|
||||||
@ -124,33 +158,55 @@ func TestConfigParsingOtherFlags(t *testing.T) {
|
|||||||
"-force-new-cluster=true",
|
"-force-new-cluster=true",
|
||||||
}
|
}
|
||||||
|
|
||||||
wcfg := NewConfig()
|
|
||||||
wcfg.proxy.Set(proxyFlagReadonly)
|
|
||||||
wcfg.clientTLSInfo.CAFile = "cafile"
|
|
||||||
wcfg.clientTLSInfo.CertFile = "certfile"
|
|
||||||
wcfg.clientTLSInfo.KeyFile = "keyfile"
|
|
||||||
wcfg.peerTLSInfo.CAFile = "peercafile"
|
|
||||||
wcfg.peerTLSInfo.CertFile = "peercertfile"
|
|
||||||
wcfg.peerTLSInfo.KeyFile = "peerkeyfile"
|
|
||||||
wcfg.forceNewCluster = true
|
|
||||||
|
|
||||||
cfg := NewConfig()
|
cfg := NewConfig()
|
||||||
err := cfg.Parse(args)
|
err := cfg.Parse(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if cfg.proxy.String() != wcfg.proxy.String() {
|
|
||||||
t.Errorf("proxy = %v, want %v", cfg.proxy, wcfg.proxy)
|
validateOtherFlags(t, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigFileOtherFields(t *testing.T) {
|
||||||
|
yc := struct {
|
||||||
|
ProxyCfgFile string `json:"proxy"`
|
||||||
|
ClientSecurityCfgFile securityConfig `json:"client-transport-security"`
|
||||||
|
PeerSecurityCfgFile securityConfig `json:"peer-transport-security"`
|
||||||
|
ForceNewCluster bool `json:"force-new-cluster"`
|
||||||
|
}{
|
||||||
|
"readonly",
|
||||||
|
securityConfig{
|
||||||
|
CAFile: "cafile",
|
||||||
|
CertFile: "certfile",
|
||||||
|
KeyFile: "keyfile",
|
||||||
|
},
|
||||||
|
securityConfig{
|
||||||
|
CAFile: "peercafile",
|
||||||
|
CertFile: "peercertfile",
|
||||||
|
KeyFile: "peerkeyfile",
|
||||||
|
},
|
||||||
|
true,
|
||||||
}
|
}
|
||||||
if cfg.clientTLSInfo.String() != wcfg.clientTLSInfo.String() {
|
|
||||||
t.Errorf("clientTLS = %v, want %v", cfg.clientTLSInfo, wcfg.clientTLSInfo)
|
b, err := yaml.Marshal(&yc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if cfg.peerTLSInfo.String() != wcfg.peerTLSInfo.String() {
|
|
||||||
t.Errorf("peerTLS = %v, want %v", cfg.peerTLSInfo, wcfg.peerTLSInfo)
|
tmpfile := mustCreateCfgFile(t, b)
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
fmt.Sprintf("--config-file=%s", tmpfile.Name()),
|
||||||
}
|
}
|
||||||
if cfg.forceNewCluster != wcfg.forceNewCluster {
|
|
||||||
t.Errorf("forceNewCluster = %t, want %t", cfg.forceNewCluster, wcfg.forceNewCluster)
|
cfg := NewConfig()
|
||||||
|
err = cfg.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateOtherFlags(t, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigParsingV1Flags(t *testing.T) {
|
func TestConfigParsingV1Flags(t *testing.T) {
|
||||||
@ -212,6 +268,52 @@ func TestConfigParsingConflictClusteringFlags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigFileConflictClusteringFlags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
InitialCluster string `json:"initial-cluster"`
|
||||||
|
DnsCluster string `json:"discovery-srv"`
|
||||||
|
Durl string `json:"discovery"`
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
InitialCluster: "0=localhost:8000",
|
||||||
|
Durl: "http://example.com/abc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DnsCluster: "example.com",
|
||||||
|
Durl: "http://example.com/abc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InitialCluster: "0=localhost:8000",
|
||||||
|
DnsCluster: "example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InitialCluster: "0=localhost:8000",
|
||||||
|
Durl: "http://example.com/abc",
|
||||||
|
DnsCluster: "example.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
b, err := yaml.Marshal(&tt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpfile := mustCreateCfgFile(t, b)
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
fmt.Sprintf("--config-file=%s", tmpfile.Name()),
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := NewConfig()
|
||||||
|
err = cfg.Parse(args)
|
||||||
|
if err != ErrConflictBootstrapFlags {
|
||||||
|
t.Errorf("%d: err = %v, want %v", i, err, ErrConflictBootstrapFlags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfigParsingMissedAdvertiseClientURLsFlag(t *testing.T) {
|
func TestConfigParsingMissedAdvertiseClientURLsFlag(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
args []string
|
args []string
|
||||||
@ -354,3 +456,147 @@ func TestConfigShouldFallbackToProxy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigFileElectionTimeout(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
TickMs uint `json:"heartbeat-interval"`
|
||||||
|
ElectionMs uint `json:"election-timeout"`
|
||||||
|
errStr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ElectionMs: 1000,
|
||||||
|
TickMs: 800,
|
||||||
|
errStr: "should be at least as 5 times as",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ElectionMs: 60000,
|
||||||
|
errStr: "is too long, and should be set less than",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
b, err := yaml.Marshal(&tt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpfile := mustCreateCfgFile(t, b)
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
fmt.Sprintf("--config-file=%s", tmpfile.Name()),
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := NewConfig()
|
||||||
|
err = cfg.Parse(args)
|
||||||
|
if !strings.Contains(err.Error(), tt.errStr) {
|
||||||
|
t.Errorf("%d: Wrong err = %v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustCreateCfgFile(t *testing.T, b []byte) *os.File {
|
||||||
|
tmpfile, err := ioutil.TempFile("", "servercfg")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tmpfile.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = tmpfile.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpfile
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateMemberFlags(t *testing.T, cfg *config) {
|
||||||
|
wcfg := &config{
|
||||||
|
Dir: "testdir",
|
||||||
|
lpurls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
|
||||||
|
lcurls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
|
||||||
|
MaxSnapFiles: 10,
|
||||||
|
MaxWalFiles: 10,
|
||||||
|
Name: "testname",
|
||||||
|
SnapCount: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Dir != wcfg.Dir {
|
||||||
|
t.Errorf("dir = %v, want %v", cfg.Dir, wcfg.Dir)
|
||||||
|
}
|
||||||
|
if cfg.MaxSnapFiles != wcfg.MaxSnapFiles {
|
||||||
|
t.Errorf("maxsnap = %v, want %v", cfg.MaxSnapFiles, wcfg.MaxSnapFiles)
|
||||||
|
}
|
||||||
|
if cfg.MaxWalFiles != wcfg.MaxWalFiles {
|
||||||
|
t.Errorf("maxwal = %v, want %v", cfg.MaxWalFiles, wcfg.MaxWalFiles)
|
||||||
|
}
|
||||||
|
if cfg.Name != wcfg.Name {
|
||||||
|
t.Errorf("name = %v, want %v", cfg.Name, wcfg.Name)
|
||||||
|
}
|
||||||
|
if cfg.SnapCount != wcfg.SnapCount {
|
||||||
|
t.Errorf("snapcount = %v, want %v", cfg.SnapCount, wcfg.SnapCount)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(cfg.lpurls, wcfg.lpurls) {
|
||||||
|
t.Errorf("listen-peer-urls = %v, want %v", cfg.lpurls, wcfg.lpurls)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(cfg.lcurls, wcfg.lcurls) {
|
||||||
|
t.Errorf("listen-client-urls = %v, want %v", cfg.lcurls, wcfg.lcurls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateClusteringFlags(t *testing.T, cfg *config) {
|
||||||
|
wcfg := NewConfig()
|
||||||
|
wcfg.apurls = []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}}
|
||||||
|
wcfg.acurls = []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}}
|
||||||
|
wcfg.clusterState.Set(clusterStateFlagExisting)
|
||||||
|
wcfg.fallback.Set(fallbackFlagExit)
|
||||||
|
wcfg.InitialCluster = "0=http://localhost:8000"
|
||||||
|
wcfg.InitialClusterToken = "etcdtest"
|
||||||
|
|
||||||
|
if cfg.clusterState.String() != wcfg.clusterState.String() {
|
||||||
|
t.Errorf("clusterState = %v, want %v", cfg.clusterState, wcfg.clusterState)
|
||||||
|
}
|
||||||
|
if cfg.fallback.String() != wcfg.fallback.String() {
|
||||||
|
t.Errorf("fallback = %v, want %v", cfg.fallback, wcfg.fallback)
|
||||||
|
}
|
||||||
|
if cfg.InitialCluster != wcfg.InitialCluster {
|
||||||
|
t.Errorf("initialCluster = %v, want %v", cfg.InitialCluster, wcfg.InitialCluster)
|
||||||
|
}
|
||||||
|
if cfg.InitialClusterToken != wcfg.InitialClusterToken {
|
||||||
|
t.Errorf("initialClusterToken = %v, want %v", cfg.InitialClusterToken, wcfg.InitialClusterToken)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(cfg.apurls, wcfg.apurls) {
|
||||||
|
t.Errorf("initial-advertise-peer-urls = %v, want %v", cfg.lpurls, wcfg.lpurls)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(cfg.acurls, wcfg.acurls) {
|
||||||
|
t.Errorf("advertise-client-urls = %v, want %v", cfg.lcurls, wcfg.lcurls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateOtherFlags(t *testing.T, cfg *config) {
|
||||||
|
wcfg := NewConfig()
|
||||||
|
wcfg.proxy.Set(proxyFlagReadonly)
|
||||||
|
wcfg.clientTLSInfo.CAFile = "cafile"
|
||||||
|
wcfg.clientTLSInfo.CertFile = "certfile"
|
||||||
|
wcfg.clientTLSInfo.KeyFile = "keyfile"
|
||||||
|
wcfg.peerTLSInfo.CAFile = "peercafile"
|
||||||
|
wcfg.peerTLSInfo.CertFile = "peercertfile"
|
||||||
|
wcfg.peerTLSInfo.KeyFile = "peerkeyfile"
|
||||||
|
wcfg.ForceNewCluster = true
|
||||||
|
|
||||||
|
if cfg.proxy.String() != wcfg.proxy.String() {
|
||||||
|
t.Errorf("proxy = %v, want %v", cfg.proxy, wcfg.proxy)
|
||||||
|
}
|
||||||
|
if cfg.clientTLSInfo.String() != wcfg.clientTLSInfo.String() {
|
||||||
|
t.Errorf("clientTLS = %v, want %v", cfg.clientTLSInfo, wcfg.clientTLSInfo)
|
||||||
|
}
|
||||||
|
if cfg.peerTLSInfo.String() != wcfg.peerTLSInfo.String() {
|
||||||
|
t.Errorf("peerTLS = %v, want %v", cfg.peerTLSInfo, wcfg.peerTLSInfo)
|
||||||
|
}
|
||||||
|
if cfg.ForceNewCluster != wcfg.ForceNewCluster {
|
||||||
|
t.Errorf("forceNewCluster = %t, want %t", cfg.ForceNewCluster, wcfg.ForceNewCluster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
135
etcdmain/etcd.conf.sample.yml
Normal file
135
etcdmain/etcd.conf.sample.yml
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
# This is the configuration file for the etcd server.
|
||||||
|
|
||||||
|
# Human-readable name for this member.
|
||||||
|
name: 'default'
|
||||||
|
|
||||||
|
# Path to the data directory.
|
||||||
|
data-dir:
|
||||||
|
|
||||||
|
# Path to the dedicated wal directory.
|
||||||
|
wal-dir:
|
||||||
|
|
||||||
|
# Number of committed transactions to trigger a snapshot to disk.
|
||||||
|
snapshot-count: 10000
|
||||||
|
|
||||||
|
# Time (in milliseconds) of a heartbeat interval.
|
||||||
|
heartbeat-interval: 100
|
||||||
|
|
||||||
|
# Time (in milliseconds) for an election to timeout.
|
||||||
|
election-timeout: 1000
|
||||||
|
|
||||||
|
# Raise alarms when backend size exceeds the given quota. 0 means use the
|
||||||
|
# default quota.
|
||||||
|
quota-backend-bytes: 0
|
||||||
|
|
||||||
|
# List of comma separated URLs to listen on for peer traffic.
|
||||||
|
listen-peer-urls: http://localhost:2380,http://localhost:7001
|
||||||
|
|
||||||
|
# List of comma separated URLs to listen on for client traffic.
|
||||||
|
listen-client-urls: http://localhost:2379,http://localhost:4001
|
||||||
|
|
||||||
|
# Maximum number of snapshot files to retain (0 is unlimited).
|
||||||
|
max-snapshots: 5
|
||||||
|
|
||||||
|
# Maximum number of wal files to retain (0 is unlimited).
|
||||||
|
max-wals: 5
|
||||||
|
|
||||||
|
# Comma-separated white list of origins for CORS (cross-origin resource sharing).
|
||||||
|
cors:
|
||||||
|
|
||||||
|
# List of this member's peer URLs to advertise to the rest of the cluster.
|
||||||
|
# The URLs needed to be a comma-separated list.
|
||||||
|
initial-advertise-peer-urls: http://localhost:2380,http://localhost:7001
|
||||||
|
|
||||||
|
# List of this member's client URLs to advertise to the public.
|
||||||
|
# The URLs needed to be a comma-separated list.
|
||||||
|
advertise-client-urls: http://localhost:2379,http://localhost:4001
|
||||||
|
|
||||||
|
# Discovery URL used to bootstrap the cluster.
|
||||||
|
discovery:
|
||||||
|
|
||||||
|
# Valid values include 'exit', 'proxy'
|
||||||
|
discovery-fallback: 'proxy'
|
||||||
|
|
||||||
|
# HTTP proxy to use for traffic to discovery service.
|
||||||
|
discovery-proxy:
|
||||||
|
|
||||||
|
# DNS domain used to bootstrap initial cluster.
|
||||||
|
discovery-srv:
|
||||||
|
|
||||||
|
# Initial cluster configuration for bootstrapping.
|
||||||
|
initial-cluster:
|
||||||
|
|
||||||
|
# Initial cluster token for the etcd cluster during bootstrap.
|
||||||
|
initial-cluster-token: 'etcd-cluster'
|
||||||
|
|
||||||
|
# Initial cluster state ('new' or 'existing').
|
||||||
|
initial-cluster-state: 'new'
|
||||||
|
|
||||||
|
# Reject reconfiguration requests that would cause quorum loss.
|
||||||
|
strict-reconfig-check: false
|
||||||
|
|
||||||
|
# Valid values include 'on', 'readonly', 'off'
|
||||||
|
proxy: 'off'
|
||||||
|
|
||||||
|
# Time (in milliseconds) an endpoint will be held in a failed state.
|
||||||
|
proxy-failure-wait: 5000
|
||||||
|
|
||||||
|
# Time (in milliseconds) of the endpoints refresh interval.
|
||||||
|
proxy-refresh-interval: 30000
|
||||||
|
|
||||||
|
# Time (in milliseconds) for a dial to timeout.
|
||||||
|
proxy-dial-timeout: 1000
|
||||||
|
|
||||||
|
# Time (in milliseconds) for a write to timeout.
|
||||||
|
proxy-write-timeout: 5000
|
||||||
|
|
||||||
|
# Time (in milliseconds) for a read to timeout.
|
||||||
|
proxy-read-timeout: 0
|
||||||
|
|
||||||
|
client-transport-security:
|
||||||
|
# DEPRECATED: Path to the client server TLS CA file.
|
||||||
|
ca-file:
|
||||||
|
|
||||||
|
# Path to the client server TLS cert file.
|
||||||
|
cert-file:
|
||||||
|
|
||||||
|
# Path to the client server TLS key file.
|
||||||
|
key-file:
|
||||||
|
|
||||||
|
# Enable client cert authentication.
|
||||||
|
client-cert-auth: false
|
||||||
|
|
||||||
|
# Path to the client server TLS trusted CA key file.
|
||||||
|
trusted-ca-file:
|
||||||
|
|
||||||
|
# Client TLS using generated certificates
|
||||||
|
auto-tls: false
|
||||||
|
|
||||||
|
peer-transport-security:
|
||||||
|
# DEPRECATED: Path to the peer server TLS CA file.
|
||||||
|
ca-file:
|
||||||
|
|
||||||
|
# Path to the peer server TLS cert file.
|
||||||
|
peer-cert-file:
|
||||||
|
|
||||||
|
# Path to the peer server TLS key file.
|
||||||
|
peer-key-file:
|
||||||
|
|
||||||
|
# Enable peer client cert authentication.
|
||||||
|
peer-client-cert-auth: false
|
||||||
|
|
||||||
|
# Path to the peer server TLS trusted CA key file.
|
||||||
|
peer-trusted-ca-file:
|
||||||
|
|
||||||
|
# Peer TLS using generated certificates.
|
||||||
|
auto-tls: false
|
||||||
|
|
||||||
|
# Enable debug-level logging for etcd.
|
||||||
|
debug: false
|
||||||
|
|
||||||
|
# Specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG'.
|
||||||
|
log-package-levels:
|
||||||
|
|
||||||
|
# Force to create a new one member cluster.
|
||||||
|
force-new-cluster: false
|
102
etcdmain/etcd.go
102
etcdmain/etcd.go
@ -101,16 +101,16 @@ func Main() {
|
|||||||
plog.Infof("setting maximum number of CPUs to %d, total number of available CPUs is %d", GoMaxProcs, runtime.NumCPU())
|
plog.Infof("setting maximum number of CPUs to %d, total number of available CPUs is %d", GoMaxProcs, runtime.NumCPU())
|
||||||
|
|
||||||
// TODO: check whether fields are set instead of whether fields have default value
|
// TODO: check whether fields are set instead of whether fields have default value
|
||||||
if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
|
if cfg.Name != defaultName && cfg.InitialCluster == initialClusterFromName(defaultName) {
|
||||||
cfg.initialCluster = initialClusterFromName(cfg.name)
|
cfg.InitialCluster = initialClusterFromName(cfg.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.dir == "" {
|
if cfg.Dir == "" {
|
||||||
cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
|
cfg.Dir = fmt.Sprintf("%v.etcd", cfg.Name)
|
||||||
plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.dir)
|
plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.Dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
which := identifyDataDirOrDie(cfg.dir)
|
which := identifyDataDirOrDie(cfg.Dir)
|
||||||
if which != dirEmpty {
|
if which != dirEmpty {
|
||||||
plog.Noticef("the server is already initialized as %v before, starting as etcd %v...", which, which)
|
plog.Noticef("the server is already initialized as %v before, starting as etcd %v...", which, which)
|
||||||
switch which {
|
switch which {
|
||||||
@ -141,17 +141,17 @@ func Main() {
|
|||||||
if derr, ok := err.(*etcdserver.DiscoveryError); ok {
|
if derr, ok := err.(*etcdserver.DiscoveryError); ok {
|
||||||
switch derr.Err {
|
switch derr.Err {
|
||||||
case discovery.ErrDuplicateID:
|
case discovery.ErrDuplicateID:
|
||||||
plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.name, cfg.durl)
|
plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.Name, cfg.Durl)
|
||||||
plog.Errorf("But etcd could not find valid cluster configuration in the given data dir (%s).", cfg.dir)
|
plog.Errorf("But etcd could not find valid cluster configuration in the given data dir (%s).", cfg.Dir)
|
||||||
plog.Infof("Please check the given data dir path if the previous bootstrap succeeded")
|
plog.Infof("Please check the given data dir path if the previous bootstrap succeeded")
|
||||||
plog.Infof("or use a new discovery token if the previous bootstrap failed.")
|
plog.Infof("or use a new discovery token if the previous bootstrap failed.")
|
||||||
case discovery.ErrDuplicateName:
|
case discovery.ErrDuplicateName:
|
||||||
plog.Errorf("member with duplicated name has registered with discovery service token(%s).", cfg.durl)
|
plog.Errorf("member with duplicated name has registered with discovery service token(%s).", cfg.Durl)
|
||||||
plog.Errorf("please check (cURL) the discovery token for more information.")
|
plog.Errorf("please check (cURL) the discovery token for more information.")
|
||||||
plog.Errorf("please do not reuse the discovery token and generate a new one to bootstrap the cluster.")
|
plog.Errorf("please do not reuse the discovery token and generate a new one to bootstrap the cluster.")
|
||||||
default:
|
default:
|
||||||
plog.Errorf("%v", err)
|
plog.Errorf("%v", err)
|
||||||
plog.Infof("discovery token %s was used, but failed to bootstrap the cluster.", cfg.durl)
|
plog.Infof("discovery token %s was used, but failed to bootstrap the cluster.", cfg.Durl)
|
||||||
plog.Infof("please generate a new discovery token and try to bootstrap again.")
|
plog.Infof("please generate a new discovery token and try to bootstrap again.")
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -159,13 +159,13 @@ func Main() {
|
|||||||
|
|
||||||
if strings.Contains(err.Error(), "include") && strings.Contains(err.Error(), "--initial-cluster") {
|
if strings.Contains(err.Error(), "include") && strings.Contains(err.Error(), "--initial-cluster") {
|
||||||
plog.Infof("%v", err)
|
plog.Infof("%v", err)
|
||||||
if cfg.initialCluster == initialClusterFromName(cfg.name) {
|
if cfg.InitialCluster == initialClusterFromName(cfg.Name) {
|
||||||
plog.Infof("forgot to set --initial-cluster flag?")
|
plog.Infof("forgot to set --initial-cluster flag?")
|
||||||
}
|
}
|
||||||
if types.URLs(cfg.apurls).String() == defaultInitialAdvertisePeerURLs {
|
if types.URLs(cfg.apurls).String() == defaultInitialAdvertisePeerURLs {
|
||||||
plog.Infof("forgot to set --initial-advertise-peer-urls flag?")
|
plog.Infof("forgot to set --initial-advertise-peer-urls flag?")
|
||||||
}
|
}
|
||||||
if cfg.initialCluster == initialClusterFromName(cfg.name) && len(cfg.durl) == 0 {
|
if cfg.InitialCluster == initialClusterFromName(cfg.Name) && len(cfg.Durl) == 0 {
|
||||||
plog.Infof("if you want to use discovery service, please set --discovery flag.")
|
plog.Infof("if you want to use discovery service, please set --discovery flag.")
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -202,16 +202,16 @@ func startEtcd(cfg *config) (<-chan struct{}, error) {
|
|||||||
return nil, fmt.Errorf("error setting up initial cluster: %v", err)
|
return nil, fmt.Errorf("error setting up initial cluster: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.peerAutoTLS && cfg.peerTLSInfo.Empty() {
|
if cfg.PeerAutoTLS && cfg.peerTLSInfo.Empty() {
|
||||||
var phosts []string
|
var phosts []string
|
||||||
for _, u := range cfg.lpurls {
|
for _, u := range cfg.lpurls {
|
||||||
phosts = append(phosts, u.Host)
|
phosts = append(phosts, u.Host)
|
||||||
}
|
}
|
||||||
cfg.peerTLSInfo, err = transport.SelfCert(path.Join(cfg.dir, "fixtures/peer"), phosts)
|
cfg.peerTLSInfo, err = transport.SelfCert(path.Join(cfg.Dir, "fixtures/peer"), phosts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Fatalf("could not get certs (%v)", err)
|
plog.Fatalf("could not get certs (%v)", err)
|
||||||
}
|
}
|
||||||
} else if cfg.peerAutoTLS {
|
} else if cfg.PeerAutoTLS {
|
||||||
plog.Warningf("ignoring peer auto TLS since certs given")
|
plog.Warningf("ignoring peer auto TLS since certs given")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,16 +257,16 @@ func startEtcd(cfg *config) (<-chan struct{}, error) {
|
|||||||
plns = append(plns, l)
|
plns = append(plns, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.clientAutoTLS && cfg.clientTLSInfo.Empty() {
|
if cfg.ClientAutoTLS && cfg.clientTLSInfo.Empty() {
|
||||||
var chosts []string
|
var chosts []string
|
||||||
for _, u := range cfg.lcurls {
|
for _, u := range cfg.lcurls {
|
||||||
chosts = append(chosts, u.Host)
|
chosts = append(chosts, u.Host)
|
||||||
}
|
}
|
||||||
cfg.clientTLSInfo, err = transport.SelfCert(path.Join(cfg.dir, "fixtures/client"), chosts)
|
cfg.clientTLSInfo, err = transport.SelfCert(path.Join(cfg.Dir, "fixtures/client"), chosts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Fatalf("could not get certs (%v)", err)
|
plog.Fatalf("could not get certs (%v)", err)
|
||||||
}
|
}
|
||||||
} else if cfg.clientAutoTLS {
|
} else if cfg.ClientAutoTLS {
|
||||||
plog.Warningf("ignoring client auto TLS since certs given")
|
plog.Warningf("ignoring client auto TLS since certs given")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,26 +343,26 @@ func startEtcd(cfg *config) (<-chan struct{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
srvcfg := &etcdserver.ServerConfig{
|
srvcfg := &etcdserver.ServerConfig{
|
||||||
Name: cfg.name,
|
Name: cfg.Name,
|
||||||
ClientURLs: cfg.acurls,
|
ClientURLs: cfg.acurls,
|
||||||
PeerURLs: cfg.apurls,
|
PeerURLs: cfg.apurls,
|
||||||
DataDir: cfg.dir,
|
DataDir: cfg.Dir,
|
||||||
DedicatedWALDir: cfg.walDir,
|
DedicatedWALDir: cfg.WalDir,
|
||||||
SnapCount: cfg.snapCount,
|
SnapCount: cfg.SnapCount,
|
||||||
MaxSnapFiles: cfg.maxSnapFiles,
|
MaxSnapFiles: cfg.MaxSnapFiles,
|
||||||
MaxWALFiles: cfg.maxWalFiles,
|
MaxWALFiles: cfg.MaxWalFiles,
|
||||||
InitialPeerURLsMap: urlsmap,
|
InitialPeerURLsMap: urlsmap,
|
||||||
InitialClusterToken: token,
|
InitialClusterToken: token,
|
||||||
DiscoveryURL: cfg.durl,
|
DiscoveryURL: cfg.Durl,
|
||||||
DiscoveryProxy: cfg.dproxy,
|
DiscoveryProxy: cfg.Dproxy,
|
||||||
NewCluster: cfg.isNewCluster(),
|
NewCluster: cfg.isNewCluster(),
|
||||||
ForceNewCluster: cfg.forceNewCluster,
|
ForceNewCluster: cfg.ForceNewCluster,
|
||||||
PeerTLSInfo: cfg.peerTLSInfo,
|
PeerTLSInfo: cfg.peerTLSInfo,
|
||||||
TickMs: cfg.TickMs,
|
TickMs: cfg.TickMs,
|
||||||
ElectionTicks: cfg.electionTicks(),
|
ElectionTicks: cfg.electionTicks(),
|
||||||
AutoCompactionRetention: cfg.autoCompactionRetention,
|
AutoCompactionRetention: cfg.autoCompactionRetention,
|
||||||
QuotaBackendBytes: cfg.quotaBackendBytes,
|
QuotaBackendBytes: cfg.QuotaBackendBytes,
|
||||||
StrictReconfigCheck: cfg.strictReconfigCheck,
|
StrictReconfigCheck: cfg.StrictReconfigCheck,
|
||||||
EnablePprof: cfg.enablePprof,
|
EnablePprof: cfg.enablePprof,
|
||||||
}
|
}
|
||||||
var s *etcdserver.EtcdServer
|
var s *etcdserver.EtcdServer
|
||||||
@ -402,33 +402,33 @@ func startEtcd(cfg *config) (<-chan struct{}, error) {
|
|||||||
|
|
||||||
// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
|
// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
|
||||||
func startProxy(cfg *config) error {
|
func startProxy(cfg *config) error {
|
||||||
pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, time.Duration(cfg.proxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyWriteTimeoutMs)*time.Millisecond)
|
pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, time.Duration(cfg.ProxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.ProxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.ProxyWriteTimeoutMs)*time.Millisecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pt.MaxIdleConnsPerHost = httpproxy.DefaultMaxIdleConnsPerHost
|
pt.MaxIdleConnsPerHost = httpproxy.DefaultMaxIdleConnsPerHost
|
||||||
|
|
||||||
tr, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, time.Duration(cfg.proxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyWriteTimeoutMs)*time.Millisecond)
|
tr, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, time.Duration(cfg.ProxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.ProxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.ProxyWriteTimeoutMs)*time.Millisecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.dir = path.Join(cfg.dir, "proxy")
|
cfg.Dir = path.Join(cfg.Dir, "proxy")
|
||||||
err = os.MkdirAll(cfg.dir, privateDirMode)
|
err = os.MkdirAll(cfg.Dir, privateDirMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var peerURLs []string
|
var peerURLs []string
|
||||||
clusterfile := path.Join(cfg.dir, "cluster")
|
clusterfile := path.Join(cfg.Dir, "cluster")
|
||||||
|
|
||||||
b, err := ioutil.ReadFile(clusterfile)
|
b, err := ioutil.ReadFile(clusterfile)
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
if cfg.durl != "" {
|
if cfg.Durl != "" {
|
||||||
plog.Warningf("discovery token ignored since the proxy has already been initialized. Valid cluster file found at %q", clusterfile)
|
plog.Warningf("discovery token ignored since the proxy has already been initialized. Valid cluster file found at %q", clusterfile)
|
||||||
}
|
}
|
||||||
if cfg.dnsCluster != "" {
|
if cfg.DnsCluster != "" {
|
||||||
plog.Warningf("DNS SRV discovery ignored since the proxy has already been initialized. Valid cluster file found at %q", clusterfile)
|
plog.Warningf("DNS SRV discovery ignored since the proxy has already been initialized. Valid cluster file found at %q", clusterfile)
|
||||||
}
|
}
|
||||||
urls := struct{ PeerURLs []string }{}
|
urls := struct{ PeerURLs []string }{}
|
||||||
@ -445,9 +445,9 @@ func startProxy(cfg *config) error {
|
|||||||
return fmt.Errorf("error setting up initial cluster: %v", err)
|
return fmt.Errorf("error setting up initial cluster: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.durl != "" {
|
if cfg.Durl != "" {
|
||||||
var s string
|
var s string
|
||||||
s, err = discovery.GetCluster(cfg.durl, cfg.dproxy)
|
s, err = discovery.GetCluster(cfg.Durl, cfg.Dproxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -499,7 +499,7 @@ func startProxy(cfg *config) error {
|
|||||||
|
|
||||||
return clientURLs
|
return clientURLs
|
||||||
}
|
}
|
||||||
ph := httpproxy.NewHandler(pt, uf, time.Duration(cfg.proxyFailureWaitMs)*time.Millisecond, time.Duration(cfg.proxyRefreshIntervalMs)*time.Millisecond)
|
ph := httpproxy.NewHandler(pt, uf, time.Duration(cfg.ProxyFailureWaitMs)*time.Millisecond, time.Duration(cfg.ProxyRefreshIntervalMs)*time.Millisecond)
|
||||||
ph = &cors.CORSHandler{
|
ph = &cors.CORSHandler{
|
||||||
Handler: ph,
|
Handler: ph,
|
||||||
Info: cfg.corsInfo,
|
Info: cfg.corsInfo,
|
||||||
@ -541,15 +541,15 @@ func startProxy(cfg *config) error {
|
|||||||
// getPeerURLsMapAndToken sets up an initial peer URLsMap and cluster token for bootstrap or discovery.
|
// getPeerURLsMapAndToken sets up an initial peer URLsMap and cluster token for bootstrap or discovery.
|
||||||
func getPeerURLsMapAndToken(cfg *config, which string) (urlsmap types.URLsMap, token string, err error) {
|
func getPeerURLsMapAndToken(cfg *config, which string) (urlsmap types.URLsMap, token string, err error) {
|
||||||
switch {
|
switch {
|
||||||
case cfg.durl != "":
|
case cfg.Durl != "":
|
||||||
urlsmap = types.URLsMap{}
|
urlsmap = types.URLsMap{}
|
||||||
// If using discovery, generate a temporary cluster based on
|
// If using discovery, generate a temporary cluster based on
|
||||||
// self's advertised peer URLs
|
// self's advertised peer URLs
|
||||||
urlsmap[cfg.name] = cfg.apurls
|
urlsmap[cfg.Name] = cfg.apurls
|
||||||
token = cfg.durl
|
token = cfg.Durl
|
||||||
case cfg.dnsCluster != "":
|
case cfg.DnsCluster != "":
|
||||||
var clusterStr string
|
var clusterStr string
|
||||||
clusterStr, token, err = discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
|
clusterStr, token, err = discovery.SRVGetCluster(cfg.Name, cfg.DnsCluster, cfg.InitialClusterToken, cfg.apurls)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@ -557,14 +557,14 @@ func getPeerURLsMapAndToken(cfg *config, which string) (urlsmap types.URLsMap, t
|
|||||||
// only etcd member must belong to the discovered cluster.
|
// only etcd member must belong to the discovered cluster.
|
||||||
// proxy does not need to belong to the discovered cluster.
|
// proxy does not need to belong to the discovered cluster.
|
||||||
if which == "etcd" {
|
if which == "etcd" {
|
||||||
if _, ok := urlsmap[cfg.name]; !ok {
|
if _, ok := urlsmap[cfg.Name]; !ok {
|
||||||
return nil, "", fmt.Errorf("cannot find local etcd member %q in SRV records", cfg.name)
|
return nil, "", fmt.Errorf("cannot find local etcd member %q in SRV records", cfg.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// We're statically configured, and cluster has appropriately been set.
|
// We're statically configured, and cluster has appropriately been set.
|
||||||
urlsmap, err = types.NewURLsMap(cfg.initialCluster)
|
urlsmap, err = types.NewURLsMap(cfg.InitialCluster)
|
||||||
token = cfg.initialClusterToken
|
token = cfg.InitialClusterToken
|
||||||
}
|
}
|
||||||
return urlsmap, token, err
|
return urlsmap, token, err
|
||||||
}
|
}
|
||||||
@ -606,12 +606,12 @@ func identifyDataDirOrDie(dir string) dirType {
|
|||||||
|
|
||||||
func setupLogging(cfg *config) {
|
func setupLogging(cfg *config) {
|
||||||
capnslog.SetGlobalLogLevel(capnslog.INFO)
|
capnslog.SetGlobalLogLevel(capnslog.INFO)
|
||||||
if cfg.debug {
|
if cfg.Debug {
|
||||||
capnslog.SetGlobalLogLevel(capnslog.DEBUG)
|
capnslog.SetGlobalLogLevel(capnslog.DEBUG)
|
||||||
}
|
}
|
||||||
if cfg.logPkgLevels != "" {
|
if cfg.LogPkgLevels != "" {
|
||||||
repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
|
repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
|
||||||
settings, err := repoLog.ParseLogLevelConfig(cfg.logPkgLevels)
|
settings, err := repoLog.ParseLogLevelConfig(cfg.LogPkgLevels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
plog.Warningf("couldn't parse log level string: %s, continuing with default levels", err.Error())
|
plog.Warningf("couldn't parse log level string: %s, continuing with default levels", err.Error())
|
||||||
return
|
return
|
||||||
|
@ -25,6 +25,9 @@ var (
|
|||||||
|
|
||||||
etcd -h | --help
|
etcd -h | --help
|
||||||
show the help information about etcd
|
show the help information about etcd
|
||||||
|
|
||||||
|
etcd --config-file
|
||||||
|
path to the server configuration file
|
||||||
`
|
`
|
||||||
flagsline = `
|
flagsline = `
|
||||||
member flags:
|
member flags:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user