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:
Ajit Yagaty 2016-05-05 00:47:38 -07:00
parent 3bcd2b5b9f
commit 8bc5ab9f8d
5 changed files with 707 additions and 184 deletions

View File

@ -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)
}

View File

@ -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)
}
}

View 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

View File

@ -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

View File

@ -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: