2016-05-13 06:51:48 +03:00
// Copyright 2015 The etcd Authors
2015-01-25 06:19:16 +03:00
//
// 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-12-20 01:47:07 +03:00
2015-11-10 20:26:33 +03:00
// Every change should be reflected on help.go as well.
2014-12-20 01:47:07 +03:00
package etcdmain
import (
"flag"
"fmt"
2016-05-05 10:47:38 +03:00
"io/ioutil"
2017-07-12 20:06:38 +03:00
"net/url"
2014-12-20 01:47:07 +03:00
"os"
2015-05-09 02:49:12 +03:00
"runtime"
2014-12-20 01:47:07 +03:00
"strings"
2016-07-10 21:06:08 +03:00
"github.com/coreos/etcd/embed"
2014-12-20 01:47:07 +03:00
"github.com/coreos/etcd/pkg/flags"
2017-07-12 20:06:38 +03:00
"github.com/coreos/etcd/pkg/types"
2014-12-20 01:47:07 +03:00
"github.com/coreos/etcd/version"
2016-05-05 10:47:38 +03:00
"github.com/ghodss/yaml"
2014-12-20 01:47:07 +03:00
)
2016-07-10 21:06:08 +03:00
var (
2014-12-20 01:47:07 +03:00
proxyFlagOff = "off"
proxyFlagReadonly = "readonly"
proxyFlagOn = "on"
fallbackFlagExit = "exit"
fallbackFlagProxy = "proxy"
ignored = [ ] string {
"cluster-active-size" ,
"cluster-remove-delay" ,
"cluster-sync-interval" ,
"config" ,
"force" ,
"max-result-buffer" ,
"max-retry-attempts" ,
"peer-heartbeat-interval" ,
"peer-election-timeout" ,
"retry-interval" ,
"snapshot" ,
"v" ,
"vv" ,
2017-01-31 03:04:40 +03:00
// for coverage testing
"test.coverprofile" ,
"test.outputdir" ,
2014-12-20 01:47:07 +03:00
}
)
2016-07-10 21:06:08 +03:00
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" `
}
2014-12-20 01:47:07 +03:00
2016-07-10 21:06:08 +03:00
// config holds the config for a command line invocation of etcd
type config struct {
embed . Config
configProxy
configFlags
configFile string
2014-12-20 01:47:07 +03:00
printVersion bool
2016-07-10 21:06:08 +03:00
ignored [ ] string
2016-11-03 22:30:25 +03:00
logOutput string
2014-12-20 01:47:07 +03:00
}
2016-07-10 21:06:08 +03:00
// configFlags has the set of flags used for command line parsing a Config
type configFlags struct {
* flag . FlagSet
clusterState * flags . StringsFlag
fallback * flags . StringsFlag
proxy * flags . StringsFlag
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
func newConfig ( ) * config {
2014-12-20 01:47:07 +03:00
cfg := & config {
2016-07-10 21:06:08 +03:00
Config : * embed . NewConfig ( ) ,
configProxy : configProxy {
Proxy : proxyFlagOff ,
ProxyFailureWaitMs : 5000 ,
ProxyRefreshIntervalMs : 30000 ,
ProxyDialTimeoutMs : 1000 ,
ProxyWriteTimeoutMs : 5000 ,
} ,
ignored : ignored ,
}
cfg . configFlags = configFlags {
FlagSet : flag . NewFlagSet ( "etcd" , flag . ContinueOnError ) ,
2014-12-20 01:47:07 +03:00
clusterState : flags . NewStringsFlag (
2016-07-10 21:06:08 +03:00
embed . ClusterStateFlagNew ,
embed . ClusterStateFlagExisting ,
2014-12-20 01:47:07 +03:00
) ,
fallback : flags . NewStringsFlag (
fallbackFlagProxy ,
2017-08-28 10:02:11 +03:00
fallbackFlagExit ,
2014-12-20 01:47:07 +03:00
) ,
proxy : flags . NewStringsFlag (
proxyFlagOff ,
proxyFlagReadonly ,
proxyFlagOn ,
) ,
}
fs := cfg . FlagSet
fs . Usage = func ( ) {
2015-09-30 23:43:38 +03:00
fmt . Fprintln ( os . Stderr , usageline )
2014-12-20 01:47:07 +03:00
}
2016-05-05 10:47:38 +03:00
fs . StringVar ( & cfg . configFile , "config-file" , "" , "Path to the server configuration file" )
2014-12-20 01:47:07 +03:00
// member
2016-07-10 21:06:08 +03:00
fs . Var ( cfg . CorsInfo , "cors" , "Comma-separated white list of origins for CORS (cross-origin resource sharing)." )
fs . StringVar ( & cfg . Dir , "data-dir" , cfg . Dir , "Path to the data directory." )
fs . StringVar ( & cfg . WalDir , "wal-dir" , cfg . WalDir , "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." )
2017-07-12 20:06:38 +03:00
fs . StringVar ( & cfg . ListenMetricsUrlsJSON , "listen-metrics-urls" , "" , "List of URLs to listen on for metrics." )
2016-07-10 21:06:08 +03:00
fs . UintVar ( & cfg . MaxSnapFiles , "max-snapshots" , cfg . MaxSnapFiles , "Maximum number of snapshot files to retain (0 is unlimited)." )
fs . UintVar ( & cfg . MaxWalFiles , "max-wals" , cfg . MaxWalFiles , "Maximum number of wal files to retain (0 is unlimited)." )
fs . StringVar ( & cfg . Name , "name" , cfg . Name , "Human-readable name for this member." )
fs . Uint64Var ( & cfg . SnapCount , "snapshot-count" , cfg . SnapCount , "Number of committed transactions to trigger a snapshot to disk." )
fs . UintVar ( & cfg . TickMs , "heartbeat-interval" , cfg . TickMs , "Time (in milliseconds) of a heartbeat interval." )
fs . UintVar ( & cfg . ElectionMs , "election-timeout" , cfg . ElectionMs , "Time (in milliseconds) for an election to timeout." )
fs . Int64Var ( & cfg . QuotaBackendBytes , "quota-backend-bytes" , cfg . QuotaBackendBytes , "Raise alarms when backend size exceeds the given quota. 0 means use the default quota." )
2017-05-24 21:04:17 +03:00
fs . UintVar ( & cfg . MaxTxnOps , "max-txn-ops" , cfg . MaxTxnOps , "Maximum number of operations permitted in a transaction." )
2017-05-23 02:54:50 +03:00
fs . UintVar ( & cfg . MaxRequestBytes , "max-request-bytes" , cfg . MaxRequestBytes , "Maximum client request size in bytes the server will accept." )
2017-08-25 23:27:50 +03:00
fs . DurationVar ( & cfg . GRPCKeepAliveMinTime , "grpc-keepalive-min-time" , cfg . Config . GRPCKeepAliveMinTime , "Minimum interval duration that a client should wait before pinging server." )
fs . DurationVar ( & cfg . GRPCKeepAliveInterval , "grpc-keepalive-interval" , cfg . Config . GRPCKeepAliveInterval , "Frequency duration of server-to-client ping to check if a connection is alive (0 to disable)." )
fs . DurationVar ( & cfg . GRPCKeepAliveTimeout , "grpc-keepalive-timeout" , cfg . Config . GRPCKeepAliveTimeout , "Additional duration of wait before closing a non-responsive connection (0 to disable)." )
2014-12-20 01:47:07 +03:00
// clustering
2016-07-10 21:06:08 +03:00
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 ( & cfg . Durl , "discovery" , cfg . Durl , "Discovery URL used to bootstrap the cluster." )
2014-12-20 01:47:07 +03:00
fs . Var ( cfg . fallback , "discovery-fallback" , fmt . Sprintf ( "Valid values include %s" , strings . Join ( cfg . fallback . Values , ", " ) ) )
2017-08-28 10:02:11 +03:00
2016-07-10 21:06:08 +03:00
fs . StringVar ( & cfg . Dproxy , "discovery-proxy" , cfg . Dproxy , "HTTP proxy to use for traffic to discovery service." )
fs . StringVar ( & cfg . DNSCluster , "discovery-srv" , cfg . DNSCluster , "DNS domain used to bootstrap initial cluster." )
fs . StringVar ( & cfg . InitialCluster , "initial-cluster" , cfg . InitialCluster , "Initial cluster configuration for bootstrapping." )
fs . StringVar ( & cfg . InitialClusterToken , "initial-cluster-token" , cfg . InitialClusterToken , "Initial cluster token for the etcd cluster during bootstrap." )
2015-11-11 00:00:08 +03:00
fs . Var ( cfg . clusterState , "initial-cluster-state" , "Initial cluster state ('new' or 'existing')." )
2017-08-28 10:02:11 +03:00
2016-07-10 21:06:08 +03:00
fs . BoolVar ( & cfg . StrictReconfigCheck , "strict-reconfig-check" , cfg . StrictReconfigCheck , "Reject reconfiguration requests that would cause quorum loss." )
2017-01-16 19:55:26 +03:00
fs . BoolVar ( & cfg . EnableV2 , "enable-v2" , true , "Accept etcd V2 client requests." )
2017-08-14 04:12:40 +03:00
fs . StringVar ( & cfg . ExperimentalEnableV2V3 , "experimental-enable-v2v3" , cfg . ExperimentalEnableV2V3 , "v3 prefix for serving emulated v2 state." )
2014-12-20 01:47:07 +03:00
// proxy
fs . Var ( cfg . proxy , "proxy" , fmt . Sprintf ( "Valid values include %s" , strings . Join ( cfg . proxy . Values , ", " ) ) )
2017-08-28 10:02:11 +03:00
2016-07-10 21:06:08 +03:00
fs . UintVar ( & cfg . ProxyFailureWaitMs , "proxy-failure-wait" , cfg . ProxyFailureWaitMs , "Time (in milliseconds) an endpoint will be held in a failed state." )
fs . UintVar ( & cfg . ProxyRefreshIntervalMs , "proxy-refresh-interval" , cfg . ProxyRefreshIntervalMs , "Time (in milliseconds) of the endpoints refresh interval." )
fs . UintVar ( & cfg . ProxyDialTimeoutMs , "proxy-dial-timeout" , cfg . ProxyDialTimeoutMs , "Time (in milliseconds) for a dial to timeout." )
fs . UintVar ( & cfg . ProxyWriteTimeoutMs , "proxy-write-timeout" , cfg . ProxyWriteTimeoutMs , "Time (in milliseconds) for a write to timeout." )
fs . UintVar ( & cfg . ProxyReadTimeoutMs , "proxy-read-timeout" , cfg . ProxyReadTimeoutMs , "Time (in milliseconds) for a read to timeout." )
2014-12-20 01:47:07 +03:00
// security
2016-07-10 21:06:08 +03:00
fs . StringVar ( & cfg . ClientTLSInfo . CAFile , "ca-file" , "" , "DEPRECATED: Path to the client server TLS CA file." )
fs . StringVar ( & cfg . ClientTLSInfo . CertFile , "cert-file" , "" , "Path to the client server TLS cert file." )
fs . StringVar ( & cfg . ClientTLSInfo . KeyFile , "key-file" , "" , "Path to the client server TLS key file." )
fs . BoolVar ( & cfg . ClientTLSInfo . ClientCertAuth , "client-cert-auth" , false , "Enable client cert authentication." )
2017-06-16 04:25:35 +03:00
fs . StringVar ( & cfg . ClientTLSInfo . CRLFile , "client-crl-file" , "" , "Path to the client certificate revocation list file." )
2016-07-10 21:06:08 +03:00
fs . StringVar ( & cfg . ClientTLSInfo . TrustedCAFile , "trusted-ca-file" , "" , "Path to the client server TLS trusted CA key file." )
2016-05-05 10:47:38 +03:00
fs . BoolVar ( & cfg . ClientAutoTLS , "auto-tls" , false , "Client TLS using generated certificates" )
2016-07-10 21:06:08 +03:00
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." )
2016-05-05 10:47:38 +03:00
fs . BoolVar ( & cfg . PeerAutoTLS , "peer-auto-tls" , false , "Peer TLS using generated certificates" )
2017-06-16 04:25:35 +03:00
fs . StringVar ( & cfg . PeerTLSInfo . CRLFile , "peer-crl-file" , "" , "Path to the peer certificate revocation list file." )
2017-09-22 12:22:09 +03:00
fs . StringVar ( & cfg . PeerTLSInfo . AllowedCN , "peer-cert-allowed-cn" , "" , "Allowed CN for inter peer authentication." )
2014-12-20 01:47:07 +03:00
2015-04-28 21:00:23 +03:00
// logging
2016-05-05 10:47:38 +03:00
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')." )
2016-11-03 22:30:25 +03:00
fs . StringVar ( & cfg . logOutput , "log-output" , "default" , "Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd." )
2015-04-28 21:00:23 +03:00
2014-12-20 01:47:07 +03:00
// unsafe
2016-05-05 10:47:38 +03:00
fs . BoolVar ( & cfg . ForceNewCluster , "force-new-cluster" , false , "Force to create a new one member cluster." )
2014-12-20 01:47:07 +03:00
// version
2015-10-25 16:31:37 +03:00
fs . BoolVar ( & cfg . printVersion , "version" , false , "Print the version and exit." )
2014-12-20 01:47:07 +03:00
2017-09-26 20:06:45 +03:00
fs . StringVar ( & cfg . AutoCompactionRetention , "auto-compaction-retention" , "0" , "Auto compaction retention for mvcc key value store. 0 means disable auto compaction." )
fs . StringVar ( & cfg . AutoCompactionMode , "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." )
2015-08-08 15:58:29 +03:00
2016-01-07 10:15:27 +03:00
// pprof profiler via HTTP
2016-07-10 21:06:08 +03:00
fs . BoolVar ( & cfg . EnablePprof , "enable-pprof" , false , "Enable runtime profiling data via HTTP server. Address is at client URL + \"/debug/pprof/\"" )
2016-01-07 10:15:27 +03:00
2016-12-17 04:04:20 +03:00
// additional metrics
fs . StringVar ( & cfg . Metrics , "metrics" , cfg . Metrics , "Set level of detail for exported metrics, specify 'extensive' to include histogram metrics" )
2016-07-21 08:13:57 +03:00
// auth
fs . StringVar ( & cfg . AuthToken , "auth-token" , cfg . AuthToken , "Specify auth token specific options." )
2017-08-19 12:40:35 +03:00
// experimental
fs . DurationVar ( & cfg . ExperimentalCorruptCheckTime , "experimental-corrupt-check-time" , cfg . ExperimentalCorruptCheckTime , "Duration of time between cluster corruption check passes." )
2014-12-20 01:47:07 +03:00
// ignored
for _ , f := range cfg . ignored {
fs . Var ( & flags . IgnoredFlag { Name : f } , f , "" )
}
return cfg
}
2016-07-10 21:06:08 +03:00
func ( cfg * config ) parse ( arguments [ ] string ) error {
2014-12-20 21:31:53 +03:00
perr := cfg . FlagSet . Parse ( arguments )
2014-12-20 01:47:07 +03:00
switch perr {
case nil :
case flag . ErrHelp :
2015-07-22 04:01:19 +03:00
fmt . Println ( flagsline )
2014-12-20 01:47:07 +03:00
os . Exit ( 0 )
default :
os . Exit ( 2 )
}
2015-03-21 03:01:53 +03:00
if len ( cfg . FlagSet . Args ( ) ) != 0 {
return fmt . Errorf ( "'%s' is not a valid flag" , cfg . FlagSet . Arg ( 0 ) )
}
2014-12-20 01:47:07 +03:00
if cfg . printVersion {
2015-05-09 02:49:12 +03:00
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 )
2014-12-20 01:47:07 +03:00
os . Exit ( 0 )
}
2016-05-05 10:47:38 +03:00
var err error
if cfg . configFile != "" {
plog . Infof ( "Loading server configuration from %q" , cfg . configFile )
2016-07-10 21:06:08 +03:00
err = cfg . configFromFile ( cfg . configFile )
2016-05-05 10:47:38 +03:00
} else {
err = cfg . configFromCmdLine ( )
}
return err
}
func ( cfg * config ) configFromCmdLine ( ) error {
2016-03-25 21:40:24 +03:00
err := flags . SetFlagsFromEnv ( "ETCD" , cfg . FlagSet )
2014-12-20 01:47:07 +03:00
if err != nil {
2015-06-11 02:19:06 +03:00
plog . Fatalf ( "%v" , err )
2014-12-20 01:47:07 +03:00
}
2016-07-10 21:06:08 +03:00
cfg . LPUrls = flags . URLsFromFlag ( cfg . FlagSet , "listen-peer-urls" )
cfg . APUrls = flags . URLsFromFlag ( cfg . FlagSet , "initial-advertise-peer-urls" )
cfg . LCUrls = flags . URLsFromFlag ( cfg . FlagSet , "listen-client-urls" )
cfg . ACUrls = flags . URLsFromFlag ( cfg . FlagSet , "advertise-client-urls" )
2017-07-12 20:06:38 +03:00
if len ( cfg . ListenMetricsUrlsJSON ) > 0 {
u , err := types . NewURLs ( strings . Split ( cfg . ListenMetricsUrlsJSON , "," ) )
if err != nil {
plog . Fatalf ( "unexpected error setting up listen-metrics-urls: %v" , err )
}
cfg . ListenMetricsUrls = [ ] url . URL ( u )
}
2016-07-10 21:06:08 +03:00
cfg . ClusterState = cfg . clusterState . String ( )
cfg . Fallback = cfg . fallback . String ( )
cfg . Proxy = cfg . proxy . String ( )
// disable default advertise-client-urls if lcurls is set
missingAC := flags . IsSet ( cfg . FlagSet , "listen-client-urls" ) && ! flags . IsSet ( cfg . FlagSet , "advertise-client-urls" )
if ! cfg . mayBeProxy ( ) && missingAC {
cfg . ACUrls = nil
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
// disable default initial-cluster if discovery is set
if ( cfg . Durl != "" || cfg . DNSCluster != "" ) && ! flags . IsSet ( cfg . FlagSet , "initial-cluster" ) {
cfg . InitialCluster = ""
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
return cfg . validate ( )
}
2016-05-05 10:47:38 +03:00
2016-07-10 21:06:08 +03:00
func ( cfg * config ) configFromFile ( path string ) error {
eCfg , err := embed . ConfigFromFile ( path )
if err != nil {
return err
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
cfg . Config = * eCfg
2016-05-05 10:47:38 +03:00
2016-07-10 21:06:08 +03:00
// load extra config information
b , rerr := ioutil . ReadFile ( path )
if rerr != nil {
return rerr
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
if yerr := yaml . Unmarshal ( b , & cfg . configProxy ) ; yerr != nil {
return yerr
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
if cfg . FallbackJSON != "" {
if err := cfg . fallback . Set ( cfg . FallbackJSON ) ; err != nil {
2016-05-05 10:47:38 +03:00
plog . Panicf ( "unexpected error setting up discovery-fallback flag: %v" , err )
}
2016-07-10 21:06:08 +03:00
cfg . Fallback = cfg . fallback . String ( )
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
if cfg . ProxyJSON != "" {
if err := cfg . proxy . Set ( cfg . ProxyJSON ) ; err != nil {
2016-05-05 10:47:38 +03:00
plog . Panicf ( "unexpected error setting up proxyFlag: %v" , err )
}
2016-07-10 21:06:08 +03:00
cfg . Proxy = cfg . proxy . String ( )
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
return nil
2016-05-05 10:47:38 +03:00
}
2016-07-10 21:06:08 +03:00
func ( cfg * config ) mayBeProxy ( ) bool {
mayFallbackToProxy := cfg . Durl != "" && cfg . Fallback == fallbackFlagProxy
return cfg . Proxy != proxyFlagOff || mayFallbackToProxy
2014-12-20 01:47:07 +03:00
}
2016-07-10 21:06:08 +03:00
func ( cfg * config ) validate ( ) error {
err := cfg . Config . Validate ( )
// TODO(yichengq): check this for joining through discovery service case
if err == embed . ErrUnsetAdvertiseClientURLsFlag && cfg . mayBeProxy ( ) {
return nil
2015-03-19 02:59:41 +03:00
}
2016-07-10 21:06:08 +03:00
return err
2015-03-19 02:59:41 +03:00
}
2014-12-20 01:47:07 +03:00
func ( cfg config ) isProxy ( ) bool { return cfg . proxy . String ( ) != proxyFlagOff }
func ( cfg config ) isReadonlyProxy ( ) bool { return cfg . proxy . String ( ) == proxyFlagReadonly }
func ( cfg config ) shouldFallbackToProxy ( ) bool { return cfg . fallback . String ( ) == fallbackFlagProxy }