blob: 2a5faa746c9a5477c1da8b44aa6eb87f3d3ba8df [file] [log] [blame]
// Copyright 2015 The etcd Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
// Every change should be reflected on help.go as well.
package etcdmain
import (
var (
proxyFlagOff = "off"
proxyFlagReadonly = "readonly"
proxyFlagOn = "on"
fallbackFlagExit = "exit"
fallbackFlagProxy = "proxy"
ignored = []string{
// for coverage testing
type configProxy struct {
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"`
Fallback string
Proxy string
ProxyJSON string `json:"proxy"`
FallbackJSON string `json:"discovery-fallback"`
// config holds the config for a command line invocation of etcd
type config struct {
ec embed.Config
cp configProxy
cf configFlags
configFile string
printVersion bool
ignored []string
// configFlags has the set of flags used for command line parsing a Config
type configFlags struct {
flagSet *flag.FlagSet
clusterState *flags.StringsFlag
fallback *flags.StringsFlag
proxy *flags.StringsFlag
func newConfig() *config {
cfg := &config{
ec: *embed.NewConfig(),
cp: configProxy{
Proxy: proxyFlagOff,
ProxyFailureWaitMs: 5000,
ProxyRefreshIntervalMs: 30000,
ProxyDialTimeoutMs: 1000,
ProxyWriteTimeoutMs: 5000,
ignored: ignored,
} = configFlags{
flagSet: flag.NewFlagSet("etcd", flag.ContinueOnError),
clusterState: flags.NewStringsFlag(
fallback: flags.NewStringsFlag(
proxy: flags.NewStringsFlag(
fs :=
fs.Usage = func() {
fmt.Fprintln(os.Stderr, usageline)
fs.StringVar(&cfg.configFile, "config-file", "", "Path to the server configuration file")
// member
fs.Var(, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
fs.StringVar(&, "data-dir",, "Path to the data directory.")
fs.StringVar(&, "wal-dir",, "Path to the dedicated wal directory.")
fs.Var(flags.NewURLsValue(embed.DefaultListenPeerURLs), "listen-peer-urls", "List of URLs to listen on for peer traffic.")
fs.Var(flags.NewURLsValue(embed.DefaultListenClientURLs), "listen-client-urls", "List of URLs to listen on for client traffic.")
fs.StringVar(&, "listen-metrics-urls", "", "List of URLs to listen on for metrics.")
fs.UintVar(&, "max-snapshots",, "Maximum number of snapshot files to retain (0 is unlimited).")
fs.UintVar(&, "max-wals",, "Maximum number of wal files to retain (0 is unlimited).")
fs.StringVar(&, "name",, "Human-readable name for this member.")
fs.Uint64Var(&, "snapshot-count",, "Number of committed transactions to trigger a snapshot to disk.")
fs.UintVar(&, "heartbeat-interval",, "Time (in milliseconds) of a heartbeat interval.")
fs.UintVar(&, "election-timeout",, "Time (in milliseconds) for an election to timeout.")
fs.BoolVar(&, "initial-election-tick-advance",, "Whether to fast-forward initial election ticks on boot for faster election.")
fs.Int64Var(&, "quota-backend-bytes",, "Raise alarms when backend size exceeds the given quota. 0 means use the default quota.")
fs.UintVar(&, "max-txn-ops",, "Maximum number of operations permitted in a transaction.")
fs.UintVar(&, "max-request-bytes",, "Maximum client request size in bytes the server will accept.")
fs.DurationVar(&, "grpc-keepalive-min-time",, "Minimum interval duration that a client should wait before pinging server.")
fs.DurationVar(&, "grpc-keepalive-interval",, "Frequency duration of server-to-client ping to check if a connection is alive (0 to disable).")
fs.DurationVar(&, "grpc-keepalive-timeout",, "Additional duration of wait before closing a non-responsive connection (0 to disable).")
// clustering
fs.Var(flags.NewURLsValue(embed.DefaultInitialAdvertisePeerURLs), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster.")
fs.Var(flags.NewURLsValue(embed.DefaultAdvertiseClientURLs), "advertise-client-urls", "List of this member's client URLs to advertise to the public.")
fs.StringVar(&, "discovery",, "Discovery URL used to bootstrap the cluster.")
fs.Var(, "discovery-fallback", fmt.Sprintf("Valid values include %s", strings.Join(, ", ")))
fs.StringVar(&, "discovery-proxy",, "HTTP proxy to use for traffic to discovery service.")
fs.StringVar(&, "discovery-srv",, "DNS domain used to bootstrap initial cluster.")
fs.StringVar(&, "initial-cluster",, "Initial cluster configuration for bootstrapping.")
fs.StringVar(&, "initial-cluster-token",, "Initial cluster token for the etcd cluster during bootstrap.")
fs.Var(, "initial-cluster-state", "Initial cluster state ('new' or 'existing').")
fs.BoolVar(&, "strict-reconfig-check",, "Reject reconfiguration requests that would cause quorum loss.")
fs.BoolVar(&, "enable-v2",, "Accept etcd V2 client requests.")
fs.StringVar(&, "experimental-enable-v2v3",, "v3 prefix for serving emulated v2 state.")
// proxy
fs.Var(, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(, ", ")))
fs.UintVar(&cfg.cp.ProxyFailureWaitMs, "proxy-failure-wait", cfg.cp.ProxyFailureWaitMs, "Time (in milliseconds) an endpoint will be held in a failed state.")
fs.UintVar(&cfg.cp.ProxyRefreshIntervalMs, "proxy-refresh-interval", cfg.cp.ProxyRefreshIntervalMs, "Time (in milliseconds) of the endpoints refresh interval.")
fs.UintVar(&cfg.cp.ProxyDialTimeoutMs, "proxy-dial-timeout", cfg.cp.ProxyDialTimeoutMs, "Time (in milliseconds) for a dial to timeout.")
fs.UintVar(&cfg.cp.ProxyWriteTimeoutMs, "proxy-write-timeout", cfg.cp.ProxyWriteTimeoutMs, "Time (in milliseconds) for a write to timeout.")
fs.UintVar(&cfg.cp.ProxyReadTimeoutMs, "proxy-read-timeout", cfg.cp.ProxyReadTimeoutMs, "Time (in milliseconds) for a read to timeout.")
// security
fs.StringVar(&, "ca-file", "", "DEPRECATED: Path to the client server TLS CA file.")
fs.StringVar(&, "cert-file", "", "Path to the client server TLS cert file.")
fs.StringVar(&, "key-file", "", "Path to the client server TLS key file.")
fs.BoolVar(&, "client-cert-auth", false, "Enable client cert authentication.")
fs.StringVar(&, "client-crl-file", "", "Path to the client certificate revocation list file.")
fs.StringVar(&, "trusted-ca-file", "", "Path to the client server TLS trusted CA cert file.")
fs.BoolVar(&, "auto-tls", false, "Client TLS using generated certificates")
fs.StringVar(&, "peer-ca-file", "", "DEPRECATED: Path to the peer server TLS CA file.")
fs.StringVar(&, "peer-cert-file", "", "Path to the peer server TLS cert file.")
fs.StringVar(&, "peer-key-file", "", "Path to the peer server TLS key file.")
fs.BoolVar(&, "peer-client-cert-auth", false, "Enable peer client cert authentication.")
fs.StringVar(&, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.")
fs.BoolVar(&, "peer-auto-tls", false, "Peer TLS using generated certificates")
fs.StringVar(&, "peer-crl-file", "", "Path to the peer certificate revocation list file.")
fs.StringVar(&, "peer-cert-allowed-cn", "", "Allowed CN for inter peer authentication.")
fs.Var(flags.NewStringsValueV2(""), "cipher-suites", "Comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).")
// logging
fs.BoolVar(&, "debug", false, "Enable debug-level logging for etcd.")
fs.StringVar(&, "log-package-levels", "", "Specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').")
fs.StringVar(&, "log-output", embed.DefaultLogOutput, "Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.")
// unsafe
fs.BoolVar(&, "force-new-cluster", false, "Force to create a new one member cluster.")
// version
fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.")
fs.StringVar(&, "auto-compaction-retention", "0", "Auto compaction retention for mvcc key value store. 0 means disable auto compaction.")
fs.StringVar(&, "auto-compaction-mode", "periodic", "interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.")
// pprof profiler via HTTP
fs.BoolVar(&, "enable-pprof", false, "Enable runtime profiling data via HTTP server. Address is at client URL + \"/debug/pprof/\"")
// additional metrics
fs.StringVar(&, "metrics",, "Set level of detail for exported metrics, specify 'extensive' to include histogram metrics")
// auth
fs.StringVar(&, "auth-token",, "Specify auth token specific options.")
// experimental
fs.BoolVar(&, "experimental-initial-corrupt-check",, "Enable to check data corruption before serving any client/peer traffic.")
fs.DurationVar(&, "experimental-corrupt-check-time",, "Duration of time between cluster corruption check passes.")
// ignored
for _, f := range cfg.ignored {
fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
return cfg
func (cfg *config) parse(arguments []string) error {
perr :=
switch perr {
case nil:
case flag.ErrHelp:
if len( != 0 {
return fmt.Errorf("'%s' is not a valid flag",
if cfg.printVersion {
fmt.Printf("etcd Version: %s\n", version.Version)
fmt.Printf("Git SHA: %s\n", version.GitSHA)
fmt.Printf("Go Version: %s\n", runtime.Version())
fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
var err error
if cfg.configFile != "" {
plog.Infof("Loading server configuration from %q", cfg.configFile)
err = cfg.configFromFile(cfg.configFile)
} else {
err = cfg.configFromCmdLine()
return err
func (cfg *config) configFromCmdLine() error {
err := flags.SetFlagsFromEnv("ETCD",
if err != nil {
plog.Fatalf("%v", err)
} = flags.URLsFromFlag(, "listen-peer-urls") = flags.URLsFromFlag(, "initial-advertise-peer-urls") = flags.URLsFromFlag(, "listen-client-urls") = flags.URLsFromFlag(, "advertise-client-urls")
if len( > 0 {
u, err := types.NewURLs(strings.Split(, ","))
if err != nil {
plog.Fatalf("unexpected error setting up listen-metrics-urls: %v", err)
} = []url.URL(u)
} = flags.StringsFromFlagV2(, "cipher-suites") =
cfg.cp.Fallback =
cfg.cp.Proxy =
// disable default advertise-client-urls if lcurls is set
missingAC := flags.IsSet(, "listen-client-urls") && !flags.IsSet(, "advertise-client-urls")
if !cfg.mayBeProxy() && missingAC { = nil
// disable default initial-cluster if discovery is set
if ( != "" || != "") && !flags.IsSet(, "initial-cluster") { = ""
return cfg.validate()
func (cfg *config) configFromFile(path string) error {
eCfg, err := embed.ConfigFromFile(path)
if err != nil {
return err
} = *eCfg
// load extra config information
b, rerr := ioutil.ReadFile(path)
if rerr != nil {
return rerr
if yerr := yaml.Unmarshal(b, &cfg.cp); yerr != nil {
return yerr
if cfg.cp.FallbackJSON != "" {
if err :=; err != nil {
plog.Panicf("unexpected error setting up discovery-fallback flag: %v", err)
cfg.cp.Fallback =
if cfg.cp.ProxyJSON != "" {
if err :=; err != nil {
plog.Panicf("unexpected error setting up proxyFlag: %v", err)
cfg.cp.Proxy =
return nil
func (cfg *config) mayBeProxy() bool {
mayFallbackToProxy := != "" && cfg.cp.Fallback == fallbackFlagProxy
return cfg.cp.Proxy != proxyFlagOff || mayFallbackToProxy
func (cfg *config) validate() error {
err :=
// TODO(yichengq): check this for joining through discovery service case
if err == embed.ErrUnsetAdvertiseClientURLsFlag && cfg.mayBeProxy() {
return nil
return err
func (cfg config) isProxy() bool { return != proxyFlagOff }
func (cfg config) isReadonlyProxy() bool { return == proxyFlagReadonly }
func (cfg config) shouldFallbackToProxy() bool { return == fallbackFlagProxy }