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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
@ -28,7 +29,9 @@ import (
|
||||
"github.com/coreos/etcd/pkg/cors"
|
||||
"github.com/coreos/etcd/pkg/flags"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"github.com/coreos/etcd/pkg/types"
|
||||
"github.com/coreos/etcd/version"
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -44,6 +47,9 @@ const (
|
||||
|
||||
defaultName = "default"
|
||||
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.
|
||||
// More details are listed in ../Documentation/tuning.md#time-parameters.
|
||||
@ -77,49 +83,61 @@ type config struct {
|
||||
|
||||
// member
|
||||
corsInfo *cors.CORSInfo
|
||||
dir string
|
||||
walDir string
|
||||
lpurls, lcurls []url.URL
|
||||
maxSnapFiles uint
|
||||
maxWalFiles uint
|
||||
name string
|
||||
snapCount uint64
|
||||
Dir string `json:"data-dir"`
|
||||
WalDir string `json:"wal-dir"`
|
||||
MaxSnapFiles uint `json:"max-snapshots"`
|
||||
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.
|
||||
// TODO: decouple tickMs and heartbeat tick (current heartbeat tick = 1).
|
||||
// make ticks a cluster wide configuration.
|
||||
TickMs uint
|
||||
ElectionMs uint
|
||||
quotaBackendBytes int64
|
||||
TickMs uint `json:"heartbeat-interval"`
|
||||
ElectionMs uint `json:"election-timeout"`
|
||||
QuotaBackendBytes int64 `json:"quota-backend-bytes"`
|
||||
|
||||
// clustering
|
||||
apurls, acurls []url.URL
|
||||
clusterState *flags.StringsFlag
|
||||
dnsCluster string
|
||||
dproxy string
|
||||
durl string
|
||||
DnsCluster string `json:"discovery-srv"`
|
||||
Dproxy string `json:"discovery-proxy"`
|
||||
Durl string `json:"discovery"`
|
||||
fallback *flags.StringsFlag
|
||||
initialCluster string
|
||||
initialClusterToken string
|
||||
strictReconfigCheck bool
|
||||
InitialCluster string `json:"initial-cluster"`
|
||||
InitialClusterToken string `json:"initial-cluster-token"`
|
||||
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 *flags.StringsFlag
|
||||
proxyFailureWaitMs uint
|
||||
proxyRefreshIntervalMs uint
|
||||
proxyDialTimeoutMs uint
|
||||
proxyWriteTimeoutMs uint
|
||||
proxyReadTimeoutMs uint
|
||||
ProxyFailureWaitMs uint `json:"proxy-failure-wait"`
|
||||
ProxyRefreshIntervalMs uint `json:"proxy-refresh-interval"`
|
||||
ProxyDialTimeoutMs uint `json:"proxy-dial-timeout"`
|
||||
ProxyWriteTimeoutMs uint `json:"proxy-write-timeout"`
|
||||
ProxyReadTimeoutMs uint `json:"proxy-read-timeout"`
|
||||
ProxyCfgFile string `json:"proxy"`
|
||||
|
||||
// security
|
||||
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 bool
|
||||
logPkgLevels string
|
||||
// Debug logging
|
||||
Debug bool `json:"debug"`
|
||||
LogPkgLevels string `json:"log-package-levels"`
|
||||
|
||||
// unsafe
|
||||
forceNewCluster bool
|
||||
// ForceNewCluster is unsafe
|
||||
ForceNewCluster bool `json:"force-new-cluster"`
|
||||
|
||||
printVersion bool
|
||||
|
||||
@ -127,9 +145,20 @@ type config struct {
|
||||
|
||||
enablePprof bool
|
||||
|
||||
configFile 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 {
|
||||
cfg := &config{
|
||||
corsInfo: &cors.CORSInfo{},
|
||||
@ -155,39 +184,41 @@ func NewConfig() *config {
|
||||
fmt.Println(usageline)
|
||||
}
|
||||
|
||||
fs.StringVar(&cfg.configFile, "config-file", "", "Path to the server configuration file")
|
||||
|
||||
// member
|
||||
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.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("http://localhost:2379,http://localhost:4001"), "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.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.Uint64Var(&cfg.snapCount, "snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot to disk.")
|
||||
fs.StringVar(&cfg.Dir, "data-dir", "", "Path to the data directory.")
|
||||
fs.StringVar(&cfg.WalDir, "wal-dir", "", "Path to the dedicated wal directory.")
|
||||
fs.Var(flags.NewURLsValue(defaultListenPeerURLs), "listen-peer-urls", "List of URLs to listen on for peer 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.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.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.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
|
||||
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.StringVar(&cfg.durl, "discovery", "", "Discovery URL used to bootstrap the cluster.")
|
||||
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.Var(cfg.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %s", strings.Join(cfg.fallback.Values, ", ")))
|
||||
if err := cfg.fallback.Set(fallbackFlagProxy); err != nil {
|
||||
// Should never happen.
|
||||
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.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.initialClusterToken, "initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap.")
|
||||
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.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.Var(cfg.clusterState, "initial-cluster-state", "Initial cluster state ('new' or 'existing').")
|
||||
if err := cfg.clusterState.Set(clusterStateFlagNew); err != nil {
|
||||
// Should never happen.
|
||||
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
|
||||
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.
|
||||
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.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.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.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.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.ProxyReadTimeoutMs, "proxy-read-timeout", 0, "Time (in milliseconds) for a read to timeout.")
|
||||
|
||||
// security
|
||||
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.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.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.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.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.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
|
||||
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.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').")
|
||||
|
||||
// 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
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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, "bind-addr", "addr")
|
||||
|
||||
@ -307,16 +336,126 @@ func (cfg *config) Parse(arguments []string) error {
|
||||
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.
|
||||
// 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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
func TestConfigParsingMemberFlags(t *testing.T) {
|
||||
@ -32,42 +38,56 @@ func TestConfigParsingMemberFlags(t *testing.T) {
|
||||
// it should be set if -listen-client-urls is set
|
||||
"-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()
|
||||
err := cfg.Parse(args)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
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)
|
||||
|
||||
cfg := NewConfig()
|
||||
err = cfg.Parse(args)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
validateMemberFlags(t, cfg)
|
||||
}
|
||||
|
||||
func TestConfigParsingClusteringFlags(t *testing.T) {
|
||||
@ -79,37 +99,51 @@ func TestConfigParsingClusteringFlags(t *testing.T) {
|
||||
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
||||
"-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()
|
||||
err := cfg.Parse(args)
|
||||
if err != nil {
|
||||
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 {
|
||||
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)
|
||||
cfg := NewConfig()
|
||||
err = cfg.Parse(args)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
validateClusteringFlags(t, cfg)
|
||||
}
|
||||
|
||||
func TestConfigParsingOtherFlags(t *testing.T) {
|
||||
@ -124,33 +158,55 @@ func TestConfigParsingOtherFlags(t *testing.T) {
|
||||
"-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()
|
||||
err := cfg.Parse(args)
|
||||
if err != nil {
|
||||
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) {
|
||||
@ -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) {
|
||||
tests := []struct {
|
||||
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())
|
||||
|
||||
// TODO: check whether fields are set instead of whether fields have default value
|
||||
if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
|
||||
cfg.initialCluster = initialClusterFromName(cfg.name)
|
||||
if cfg.Name != defaultName && cfg.InitialCluster == initialClusterFromName(defaultName) {
|
||||
cfg.InitialCluster = initialClusterFromName(cfg.Name)
|
||||
}
|
||||
|
||||
if cfg.dir == "" {
|
||||
cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
|
||||
plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.dir)
|
||||
if cfg.Dir == "" {
|
||||
cfg.Dir = fmt.Sprintf("%v.etcd", cfg.Name)
|
||||
plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.Dir)
|
||||
}
|
||||
|
||||
which := identifyDataDirOrDie(cfg.dir)
|
||||
which := identifyDataDirOrDie(cfg.Dir)
|
||||
if which != dirEmpty {
|
||||
plog.Noticef("the server is already initialized as %v before, starting as etcd %v...", which, which)
|
||||
switch which {
|
||||
@ -141,17 +141,17 @@ func Main() {
|
||||
if derr, ok := err.(*etcdserver.DiscoveryError); ok {
|
||||
switch derr.Err {
|
||||
case discovery.ErrDuplicateID:
|
||||
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("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.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.")
|
||||
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 do not reuse the discovery token and generate a new one to bootstrap the cluster.")
|
||||
default:
|
||||
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.")
|
||||
}
|
||||
os.Exit(1)
|
||||
@ -159,13 +159,13 @@ func Main() {
|
||||
|
||||
if strings.Contains(err.Error(), "include") && strings.Contains(err.Error(), "--initial-cluster") {
|
||||
plog.Infof("%v", err)
|
||||
if cfg.initialCluster == initialClusterFromName(cfg.name) {
|
||||
if cfg.InitialCluster == initialClusterFromName(cfg.Name) {
|
||||
plog.Infof("forgot to set --initial-cluster flag?")
|
||||
}
|
||||
if types.URLs(cfg.apurls).String() == defaultInitialAdvertisePeerURLs {
|
||||
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.")
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
if cfg.peerAutoTLS && cfg.peerTLSInfo.Empty() {
|
||||
if cfg.PeerAutoTLS && cfg.peerTLSInfo.Empty() {
|
||||
var phosts []string
|
||||
for _, u := range cfg.lpurls {
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
|
||||
@ -257,16 +257,16 @@ func startEtcd(cfg *config) (<-chan struct{}, error) {
|
||||
plns = append(plns, l)
|
||||
}
|
||||
|
||||
if cfg.clientAutoTLS && cfg.clientTLSInfo.Empty() {
|
||||
if cfg.ClientAutoTLS && cfg.clientTLSInfo.Empty() {
|
||||
var chosts []string
|
||||
for _, u := range cfg.lcurls {
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
|
||||
@ -343,26 +343,26 @@ func startEtcd(cfg *config) (<-chan struct{}, error) {
|
||||
}
|
||||
|
||||
srvcfg := &etcdserver.ServerConfig{
|
||||
Name: cfg.name,
|
||||
Name: cfg.Name,
|
||||
ClientURLs: cfg.acurls,
|
||||
PeerURLs: cfg.apurls,
|
||||
DataDir: cfg.dir,
|
||||
DedicatedWALDir: cfg.walDir,
|
||||
SnapCount: cfg.snapCount,
|
||||
MaxSnapFiles: cfg.maxSnapFiles,
|
||||
MaxWALFiles: cfg.maxWalFiles,
|
||||
DataDir: cfg.Dir,
|
||||
DedicatedWALDir: cfg.WalDir,
|
||||
SnapCount: cfg.SnapCount,
|
||||
MaxSnapFiles: cfg.MaxSnapFiles,
|
||||
MaxWALFiles: cfg.MaxWalFiles,
|
||||
InitialPeerURLsMap: urlsmap,
|
||||
InitialClusterToken: token,
|
||||
DiscoveryURL: cfg.durl,
|
||||
DiscoveryProxy: cfg.dproxy,
|
||||
DiscoveryURL: cfg.Durl,
|
||||
DiscoveryProxy: cfg.Dproxy,
|
||||
NewCluster: cfg.isNewCluster(),
|
||||
ForceNewCluster: cfg.forceNewCluster,
|
||||
ForceNewCluster: cfg.ForceNewCluster,
|
||||
PeerTLSInfo: cfg.peerTLSInfo,
|
||||
TickMs: cfg.TickMs,
|
||||
ElectionTicks: cfg.electionTicks(),
|
||||
AutoCompactionRetention: cfg.autoCompactionRetention,
|
||||
QuotaBackendBytes: cfg.quotaBackendBytes,
|
||||
StrictReconfigCheck: cfg.strictReconfigCheck,
|
||||
QuotaBackendBytes: cfg.QuotaBackendBytes,
|
||||
StrictReconfigCheck: cfg.StrictReconfigCheck,
|
||||
EnablePprof: cfg.enablePprof,
|
||||
}
|
||||
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.
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.dir = path.Join(cfg.dir, "proxy")
|
||||
err = os.MkdirAll(cfg.dir, privateDirMode)
|
||||
cfg.Dir = path.Join(cfg.Dir, "proxy")
|
||||
err = os.MkdirAll(cfg.Dir, privateDirMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var peerURLs []string
|
||||
clusterfile := path.Join(cfg.dir, "cluster")
|
||||
clusterfile := path.Join(cfg.Dir, "cluster")
|
||||
|
||||
b, err := ioutil.ReadFile(clusterfile)
|
||||
switch {
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
urls := struct{ PeerURLs []string }{}
|
||||
@ -445,9 +445,9 @@ func startProxy(cfg *config) error {
|
||||
return fmt.Errorf("error setting up initial cluster: %v", err)
|
||||
}
|
||||
|
||||
if cfg.durl != "" {
|
||||
if cfg.Durl != "" {
|
||||
var s string
|
||||
s, err = discovery.GetCluster(cfg.durl, cfg.dproxy)
|
||||
s, err = discovery.GetCluster(cfg.Durl, cfg.Dproxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -499,7 +499,7 @@ func startProxy(cfg *config) error {
|
||||
|
||||
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{
|
||||
Handler: ph,
|
||||
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.
|
||||
func getPeerURLsMapAndToken(cfg *config, which string) (urlsmap types.URLsMap, token string, err error) {
|
||||
switch {
|
||||
case cfg.durl != "":
|
||||
case cfg.Durl != "":
|
||||
urlsmap = types.URLsMap{}
|
||||
// If using discovery, generate a temporary cluster based on
|
||||
// self's advertised peer URLs
|
||||
urlsmap[cfg.name] = cfg.apurls
|
||||
token = cfg.durl
|
||||
case cfg.dnsCluster != "":
|
||||
urlsmap[cfg.Name] = cfg.apurls
|
||||
token = cfg.Durl
|
||||
case cfg.DnsCluster != "":
|
||||
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 {
|
||||
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.
|
||||
// proxy does not need to belong to the discovered cluster.
|
||||
if which == "etcd" {
|
||||
if _, ok := urlsmap[cfg.name]; !ok {
|
||||
return nil, "", fmt.Errorf("cannot find local etcd member %q in SRV records", cfg.name)
|
||||
if _, ok := urlsmap[cfg.Name]; !ok {
|
||||
return nil, "", fmt.Errorf("cannot find local etcd member %q in SRV records", cfg.Name)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// We're statically configured, and cluster has appropriately been set.
|
||||
urlsmap, err = types.NewURLsMap(cfg.initialCluster)
|
||||
token = cfg.initialClusterToken
|
||||
urlsmap, err = types.NewURLsMap(cfg.InitialCluster)
|
||||
token = cfg.InitialClusterToken
|
||||
}
|
||||
return urlsmap, token, err
|
||||
}
|
||||
@ -606,12 +606,12 @@ func identifyDataDirOrDie(dir string) dirType {
|
||||
|
||||
func setupLogging(cfg *config) {
|
||||
capnslog.SetGlobalLogLevel(capnslog.INFO)
|
||||
if cfg.debug {
|
||||
if cfg.Debug {
|
||||
capnslog.SetGlobalLogLevel(capnslog.DEBUG)
|
||||
}
|
||||
if cfg.logPkgLevels != "" {
|
||||
if cfg.LogPkgLevels != "" {
|
||||
repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
|
||||
settings, err := repoLog.ParseLogLevelConfig(cfg.logPkgLevels)
|
||||
settings, err := repoLog.ParseLogLevelConfig(cfg.LogPkgLevels)
|
||||
if err != nil {
|
||||
plog.Warningf("couldn't parse log level string: %s, continuing with default levels", err.Error())
|
||||
return
|
||||
|
@ -25,6 +25,9 @@ var (
|
||||
|
||||
etcd -h | --help
|
||||
show the help information about etcd
|
||||
|
||||
etcd --config-file
|
||||
path to the server configuration file
|
||||
`
|
||||
flagsline = `
|
||||
member flags:
|
||||
|
Loading…
x
Reference in New Issue
Block a user