...

Source file src/go.etcd.io/etcd/server/v3/embed/config.go

Documentation: go.etcd.io/etcd/server/v3/embed

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package embed
    16  
    17  import (
    18  	"crypto/tls"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"math"
    22  	"net"
    23  	"net/http"
    24  	"net/url"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  	"sync"
    29  	"time"
    30  
    31  	"go.etcd.io/etcd/client/pkg/v3/logutil"
    32  	"go.etcd.io/etcd/client/pkg/v3/srv"
    33  	"go.etcd.io/etcd/client/pkg/v3/tlsutil"
    34  	"go.etcd.io/etcd/client/pkg/v3/transport"
    35  	"go.etcd.io/etcd/client/pkg/v3/types"
    36  	"go.etcd.io/etcd/pkg/v3/flags"
    37  	"go.etcd.io/etcd/pkg/v3/netutil"
    38  	"go.etcd.io/etcd/server/v3/config"
    39  	"go.etcd.io/etcd/server/v3/etcdserver"
    40  	"go.etcd.io/etcd/server/v3/etcdserver/api/v3compactor"
    41  
    42  	bolt "go.etcd.io/bbolt"
    43  	"go.uber.org/multierr"
    44  	"go.uber.org/zap"
    45  	"golang.org/x/crypto/bcrypt"
    46  	"google.golang.org/grpc"
    47  	"sigs.k8s.io/yaml"
    48  )
    49  
    50  const (
    51  	ClusterStateFlagNew      = "new"
    52  	ClusterStateFlagExisting = "existing"
    53  
    54  	DefaultName                  = "default"
    55  	DefaultMaxSnapshots          = 5
    56  	DefaultMaxWALs               = 5
    57  	DefaultMaxTxnOps             = uint(128)
    58  	DefaultWarningApplyDuration  = 100 * time.Millisecond
    59  	DefaultMaxRequestBytes       = 1.5 * 1024 * 1024
    60  	DefaultMaxConcurrentStreams  = math.MaxUint32
    61  	DefaultGRPCKeepAliveMinTime  = 5 * time.Second
    62  	DefaultGRPCKeepAliveInterval = 2 * time.Hour
    63  	DefaultGRPCKeepAliveTimeout  = 20 * time.Second
    64  	DefaultDowngradeCheckTime    = 5 * time.Second
    65  
    66  	DefaultListenPeerURLs   = "http://localhost:2380"
    67  	DefaultListenClientURLs = "http://localhost:2379"
    68  
    69  	DefaultLogOutput = "default"
    70  	JournalLogOutput = "systemd/journal"
    71  	StdErrLogOutput  = "stderr"
    72  	StdOutLogOutput  = "stdout"
    73  
    74  	// DefaultLogRotationConfig is the default configuration used for log rotation.
    75  	// Log rotation is disabled by default.
    76  	// MaxSize    = 100 // MB
    77  	// MaxAge     = 0 // days (no limit)
    78  	// MaxBackups = 0 // no limit
    79  	// LocalTime  = false // use computers local time, UTC by default
    80  	// Compress   = false // compress the rotated log in gzip format
    81  	DefaultLogRotationConfig = `{"maxsize": 100, "maxage": 0, "maxbackups": 0, "localtime": false, "compress": false}`
    82  
    83  	// ExperimentalDistributedTracingAddress is the default collector address.
    84  	ExperimentalDistributedTracingAddress = "localhost:4317"
    85  	// ExperimentalDistributedTracingServiceName is the default etcd service name.
    86  	ExperimentalDistributedTracingServiceName = "etcd"
    87  
    88  	// DefaultStrictReconfigCheck is the default value for "--strict-reconfig-check" flag.
    89  	// It's enabled by default.
    90  	DefaultStrictReconfigCheck = true
    91  	// DefaultEnableV2 is the default value for "--enable-v2" flag.
    92  	// v2 API is disabled by default.
    93  	DefaultEnableV2 = false
    94  
    95  	// maxElectionMs specifies the maximum value of election timeout.
    96  	// More details are listed in ../Documentation/tuning.md#time-parameters.
    97  	maxElectionMs = 50000
    98  	// backend freelist map type
    99  	freelistArrayType = "array"
   100  )
   101  
   102  var (
   103  	ErrConflictBootstrapFlags = fmt.Errorf("multiple discovery or bootstrap flags are set. " +
   104  		"Choose one of \"initial-cluster\", \"discovery\" or \"discovery-srv\"")
   105  	ErrUnsetAdvertiseClientURLsFlag = fmt.Errorf("--advertise-client-urls is required when --listen-client-urls is set explicitly")
   106  	ErrLogRotationInvalidLogOutput  = fmt.Errorf("--log-outputs requires a single file path when --log-rotate-config-json is defined")
   107  
   108  	DefaultInitialAdvertisePeerURLs = "http://localhost:2380"
   109  	DefaultAdvertiseClientURLs      = "http://localhost:2379"
   110  
   111  	defaultHostname   string
   112  	defaultHostStatus error
   113  
   114  	// indirection for testing
   115  	getCluster = srv.GetCluster
   116  )
   117  
   118  var (
   119  	// CompactorModePeriodic is periodic compaction mode
   120  	// for "Config.AutoCompactionMode" field.
   121  	// If "AutoCompactionMode" is CompactorModePeriodic and
   122  	// "AutoCompactionRetention" is "1h", it automatically compacts
   123  	// compacts storage every hour.
   124  	CompactorModePeriodic = v3compactor.ModePeriodic
   125  
   126  	// CompactorModeRevision is revision-based compaction mode
   127  	// for "Config.AutoCompactionMode" field.
   128  	// If "AutoCompactionMode" is CompactorModeRevision and
   129  	// "AutoCompactionRetention" is "1000", it compacts log on
   130  	// revision 5000 when the current revision is 6000.
   131  	// This runs every 5-minute if enough of logs have proceeded.
   132  	CompactorModeRevision = v3compactor.ModeRevision
   133  )
   134  
   135  func init() {
   136  	defaultHostname, defaultHostStatus = netutil.GetDefaultHost()
   137  }
   138  
   139  // Config holds the arguments for configuring an etcd server.
   140  type Config struct {
   141  	Name   string `json:"name"`
   142  	Dir    string `json:"data-dir"`
   143  	WalDir string `json:"wal-dir"`
   144  
   145  	SnapshotCount uint64 `json:"snapshot-count"`
   146  
   147  	// SnapshotCatchUpEntries is the number of entries for a slow follower
   148  	// to catch-up after compacting the raft storage entries.
   149  	// We expect the follower has a millisecond level latency with the leader.
   150  	// The max throughput is around 10K. Keep a 5K entries is enough for helping
   151  	// follower to catch up.
   152  	// WARNING: only change this for tests.
   153  	// Always use "DefaultSnapshotCatchUpEntries"
   154  	SnapshotCatchUpEntries uint64
   155  
   156  	MaxSnapFiles uint `json:"max-snapshots"`
   157  	MaxWalFiles  uint `json:"max-wals"`
   158  
   159  	// TickMs is the number of milliseconds between heartbeat ticks.
   160  	// TODO: decouple tickMs and heartbeat tick (current heartbeat tick = 1).
   161  	// make ticks a cluster wide configuration.
   162  	TickMs     uint `json:"heartbeat-interval"`
   163  	ElectionMs uint `json:"election-timeout"`
   164  
   165  	// InitialElectionTickAdvance is true, then local member fast-forwards
   166  	// election ticks to speed up "initial" leader election trigger. This
   167  	// benefits the case of larger election ticks. For instance, cross
   168  	// datacenter deployment may require longer election timeout of 10-second.
   169  	// If true, local node does not need wait up to 10-second. Instead,
   170  	// forwards its election ticks to 8-second, and have only 2-second left
   171  	// before leader election.
   172  	//
   173  	// Major assumptions are that:
   174  	//  - cluster has no active leader thus advancing ticks enables faster
   175  	//    leader election, or
   176  	//  - cluster already has an established leader, and rejoining follower
   177  	//    is likely to receive heartbeats from the leader after tick advance
   178  	//    and before election timeout.
   179  	//
   180  	// However, when network from leader to rejoining follower is congested,
   181  	// and the follower does not receive leader heartbeat within left election
   182  	// ticks, disruptive election has to happen thus affecting cluster
   183  	// availabilities.
   184  	//
   185  	// Disabling this would slow down initial bootstrap process for cross
   186  	// datacenter deployments. Make your own tradeoffs by configuring
   187  	// --initial-election-tick-advance at the cost of slow initial bootstrap.
   188  	//
   189  	// If single-node, it advances ticks regardless.
   190  	//
   191  	// See https://github.com/etcd-io/etcd/issues/9333 for more detail.
   192  	InitialElectionTickAdvance bool `json:"initial-election-tick-advance"`
   193  
   194  	// BackendBatchInterval is the maximum time before commit the backend transaction.
   195  	BackendBatchInterval time.Duration `json:"backend-batch-interval"`
   196  	// BackendBatchLimit is the maximum operations before commit the backend transaction.
   197  	BackendBatchLimit int `json:"backend-batch-limit"`
   198  	// BackendFreelistType specifies the type of freelist that boltdb backend uses (array and map are supported types).
   199  	BackendFreelistType string `json:"backend-bbolt-freelist-type"`
   200  	QuotaBackendBytes   int64  `json:"quota-backend-bytes"`
   201  	MaxTxnOps           uint   `json:"max-txn-ops"`
   202  	MaxRequestBytes     uint   `json:"max-request-bytes"`
   203  
   204  	// MaxConcurrentStreams specifies the maximum number of concurrent
   205  	// streams that each client can open at a time.
   206  	MaxConcurrentStreams uint32 `json:"max-concurrent-streams"`
   207  
   208  	ListenPeerUrls, ListenClientUrls, ListenClientHttpUrls []url.URL
   209  	AdvertisePeerUrls, AdvertiseClientUrls                 []url.URL
   210  	ClientTLSInfo                                          transport.TLSInfo
   211  	ClientAutoTLS                                          bool
   212  	PeerTLSInfo                                            transport.TLSInfo
   213  	PeerAutoTLS                                            bool
   214  	// SelfSignedCertValidity specifies the validity period of the client and peer certificates
   215  	// that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS,
   216  	// the unit is year, and the default is 1
   217  	SelfSignedCertValidity uint `json:"self-signed-cert-validity"`
   218  
   219  	// CipherSuites is a list of supported TLS cipher suites between
   220  	// client/server and peers. If empty, Go auto-populates the list.
   221  	// Note that cipher suites are prioritized in the given order.
   222  	CipherSuites []string `json:"cipher-suites"`
   223  
   224  	// TlsMinVersion is the minimum accepted TLS version between client/server and peers.
   225  	TlsMinVersion string `json:"tls-min-version"`
   226  	// TlsMaxVersion is the maximum accepted TLS version between client/server and peers.
   227  	TlsMaxVersion string `json:"tls-max-version"`
   228  
   229  	ClusterState          string `json:"initial-cluster-state"`
   230  	DNSCluster            string `json:"discovery-srv"`
   231  	DNSClusterServiceName string `json:"discovery-srv-name"`
   232  	Dproxy                string `json:"discovery-proxy"`
   233  	Durl                  string `json:"discovery"`
   234  	InitialCluster        string `json:"initial-cluster"`
   235  	InitialClusterToken   string `json:"initial-cluster-token"`
   236  	StrictReconfigCheck   bool   `json:"strict-reconfig-check"`
   237  
   238  	// EnableV2 exposes the deprecated V2 API surface.
   239  	// TODO: Delete in 3.6 (https://github.com/etcd-io/etcd/issues/12913)
   240  	// Deprecated in 3.5.
   241  	EnableV2 bool `json:"enable-v2"`
   242  
   243  	// AutoCompactionMode is either 'periodic' or 'revision'.
   244  	AutoCompactionMode string `json:"auto-compaction-mode"`
   245  	// AutoCompactionRetention is either duration string with time unit
   246  	// (e.g. '5m' for 5-minute), or revision unit (e.g. '5000').
   247  	// If no time unit is provided and compaction mode is 'periodic',
   248  	// the unit defaults to hour. For example, '5' translates into 5-hour.
   249  	AutoCompactionRetention string `json:"auto-compaction-retention"`
   250  
   251  	// GRPCKeepAliveMinTime is the minimum interval that a client should
   252  	// wait before pinging server. When client pings "too fast", server
   253  	// sends goaway and closes the connection (errors: too_many_pings,
   254  	// http2.ErrCodeEnhanceYourCalm). When too slow, nothing happens.
   255  	// Server expects client pings only when there is any active streams
   256  	// (PermitWithoutStream is set false).
   257  	GRPCKeepAliveMinTime time.Duration `json:"grpc-keepalive-min-time"`
   258  	// GRPCKeepAliveInterval is the frequency of server-to-client ping
   259  	// to check if a connection is alive. Close a non-responsive connection
   260  	// after an additional duration of Timeout. 0 to disable.
   261  	GRPCKeepAliveInterval time.Duration `json:"grpc-keepalive-interval"`
   262  	// GRPCKeepAliveTimeout is the additional duration of wait
   263  	// before closing a non-responsive connection. 0 to disable.
   264  	GRPCKeepAliveTimeout time.Duration `json:"grpc-keepalive-timeout"`
   265  
   266  	// SocketOpts are socket options passed to listener config.
   267  	SocketOpts transport.SocketOpts `json:"socket-options"`
   268  
   269  	// PreVote is true to enable Raft Pre-Vote.
   270  	// If enabled, Raft runs an additional election phase
   271  	// to check whether it would get enough votes to win
   272  	// an election, thus minimizing disruptions.
   273  	PreVote bool `json:"pre-vote"`
   274  
   275  	CORS map[string]struct{}
   276  
   277  	// HostWhitelist lists acceptable hostnames from HTTP client requests.
   278  	// Client origin policy protects against "DNS Rebinding" attacks
   279  	// to insecure etcd servers. That is, any website can simply create
   280  	// an authorized DNS name, and direct DNS to "localhost" (or any
   281  	// other address). Then, all HTTP endpoints of etcd server listening
   282  	// on "localhost" becomes accessible, thus vulnerable to DNS rebinding
   283  	// attacks. See "CVE-2018-5702" for more detail.
   284  	//
   285  	// 1. If client connection is secure via HTTPS, allow any hostnames.
   286  	// 2. If client connection is not secure and "HostWhitelist" is not empty,
   287  	//    only allow HTTP requests whose Host field is listed in whitelist.
   288  	//
   289  	// Note that the client origin policy is enforced whether authentication
   290  	// is enabled or not, for tighter controls.
   291  	//
   292  	// By default, "HostWhitelist" is "*", which allows any hostnames.
   293  	// Note that when specifying hostnames, loopback addresses are not added
   294  	// automatically. To allow loopback interfaces, leave it empty or set it "*",
   295  	// or add them to whitelist manually (e.g. "localhost", "127.0.0.1", etc.).
   296  	//
   297  	// CVE-2018-5702 reference:
   298  	// - https://bugs.chromium.org/p/project-zero/issues/detail?id=1447#c2
   299  	// - https://github.com/transmission/transmission/pull/468
   300  	// - https://github.com/etcd-io/etcd/issues/9353
   301  	HostWhitelist map[string]struct{}
   302  
   303  	// UserHandlers is for registering users handlers and only used for
   304  	// embedding etcd into other applications.
   305  	// The map key is the route path for the handler, and
   306  	// you must ensure it can't be conflicted with etcd's.
   307  	UserHandlers map[string]http.Handler `json:"-"`
   308  	// ServiceRegister is for registering users' gRPC services. A simple usage example:
   309  	//	cfg := embed.NewConfig()
   310  	//	cfg.ServerRegister = func(s *grpc.Server) {
   311  	//		pb.RegisterFooServer(s, &fooServer{})
   312  	//		pb.RegisterBarServer(s, &barServer{})
   313  	//	}
   314  	//	embed.StartEtcd(cfg)
   315  	ServiceRegister func(*grpc.Server) `json:"-"`
   316  
   317  	AuthToken  string `json:"auth-token"`
   318  	BcryptCost uint   `json:"bcrypt-cost"`
   319  
   320  	// AuthTokenTTL specifies the TTL in seconds of the simple token
   321  	AuthTokenTTL uint `json:"auth-token-ttl"`
   322  
   323  	ExperimentalInitialCorruptCheck     bool          `json:"experimental-initial-corrupt-check"`
   324  	ExperimentalCorruptCheckTime        time.Duration `json:"experimental-corrupt-check-time"`
   325  	ExperimentalCompactHashCheckEnabled bool          `json:"experimental-compact-hash-check-enabled"`
   326  	ExperimentalCompactHashCheckTime    time.Duration `json:"experimental-compact-hash-check-time"`
   327  	// ExperimentalEnableV2V3 configures URLs that expose deprecated V2 API working on V3 store.
   328  	// Deprecated in v3.5.
   329  	// TODO: Delete in v3.6 (https://github.com/etcd-io/etcd/issues/12913)
   330  	ExperimentalEnableV2V3 string `json:"experimental-enable-v2v3"`
   331  	// ExperimentalEnableLeaseCheckpoint enables leader to send regular checkpoints to other members to prevent reset of remaining TTL on leader change.
   332  	ExperimentalEnableLeaseCheckpoint bool `json:"experimental-enable-lease-checkpoint"`
   333  	// ExperimentalEnableLeaseCheckpointPersist enables persisting remainingTTL to prevent indefinite auto-renewal of long lived leases. Always enabled in v3.6. Should be used to ensure smooth upgrade from v3.5 clusters with this feature enabled.
   334  	// Requires experimental-enable-lease-checkpoint to be enabled.
   335  	// Deprecated in v3.6.
   336  	// TODO: Delete in v3.7
   337  	ExperimentalEnableLeaseCheckpointPersist bool          `json:"experimental-enable-lease-checkpoint-persist"`
   338  	ExperimentalCompactionBatchLimit         int           `json:"experimental-compaction-batch-limit"`
   339  	ExperimentalWatchProgressNotifyInterval  time.Duration `json:"experimental-watch-progress-notify-interval"`
   340  	// ExperimentalWarningApplyDuration is the time duration after which a warning is generated if applying request
   341  	// takes more time than this value.
   342  	ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration"`
   343  	// ExperimentalBootstrapDefragThresholdMegabytes is the minimum number of megabytes needed to be freed for etcd server to
   344  	// consider running defrag during bootstrap. Needs to be set to non-zero value to take effect.
   345  	ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes"`
   346  
   347  	// ForceNewCluster starts a new cluster even if previously started; unsafe.
   348  	ForceNewCluster bool `json:"force-new-cluster"`
   349  
   350  	EnablePprof           bool   `json:"enable-pprof"`
   351  	Metrics               string `json:"metrics"`
   352  	ListenMetricsUrls     []url.URL
   353  	ListenMetricsUrlsJSON string `json:"listen-metrics-urls"`
   354  
   355  	// ExperimentalEnableDistributedTracing indicates if experimental tracing using OpenTelemetry is enabled.
   356  	ExperimentalEnableDistributedTracing bool `json:"experimental-enable-distributed-tracing"`
   357  	// ExperimentalDistributedTracingAddress is the address of the OpenTelemetry Collector.
   358  	// Can only be set if ExperimentalEnableDistributedTracing is true.
   359  	ExperimentalDistributedTracingAddress string `json:"experimental-distributed-tracing-address"`
   360  	// ExperimentalDistributedTracingServiceName is the name of the service.
   361  	// Can only be used if ExperimentalEnableDistributedTracing is true.
   362  	ExperimentalDistributedTracingServiceName string `json:"experimental-distributed-tracing-service-name"`
   363  	// ExperimentalDistributedTracingServiceInstanceID is the ID key of the service.
   364  	// This ID must be unique, as helps to distinguish instances of the same service
   365  	// that exist at the same time.
   366  	// Can only be used if ExperimentalEnableDistributedTracing is true.
   367  	ExperimentalDistributedTracingServiceInstanceID string `json:"experimental-distributed-tracing-instance-id"`
   368  	// ExperimentalDistributedTracingSamplingRatePerMillion is the number of samples to collect per million spans.
   369  	// Defaults to 0.
   370  	ExperimentalDistributedTracingSamplingRatePerMillion int `json:"experimental-distributed-tracing-sampling-rate"`
   371  
   372  	// Logger is logger options: currently only supports "zap".
   373  	// "capnslog" is removed in v3.5.
   374  	Logger string `json:"logger"`
   375  	// LogLevel configures log level. Only supports debug, info, warn, error, panic, or fatal. Default 'info'.
   376  	LogLevel string `json:"log-level"`
   377  	// LogOutputs is either:
   378  	//  - "default" as os.Stderr,
   379  	//  - "stderr" as os.Stderr,
   380  	//  - "stdout" as os.Stdout,
   381  	//  - file path to append server logs to.
   382  	// It can be multiple when "Logger" is zap.
   383  	LogOutputs []string `json:"log-outputs"`
   384  	// EnableLogRotation enables log rotation of a single LogOutputs file target.
   385  	EnableLogRotation bool `json:"enable-log-rotation"`
   386  	// LogRotationConfigJSON is a passthrough allowing a log rotation JSON config to be passed directly.
   387  	LogRotationConfigJSON string `json:"log-rotation-config-json"`
   388  	// ZapLoggerBuilder is used to build the zap logger.
   389  	ZapLoggerBuilder func(*Config) error
   390  
   391  	// logger logs server-side operations. The default is nil,
   392  	// and "setupLogging" must be called before starting server.
   393  	// Do not set logger directly.
   394  	loggerMu *sync.RWMutex
   395  	logger   *zap.Logger
   396  	// EnableGRPCGateway enables grpc gateway.
   397  	// The gateway translates a RESTful HTTP API into gRPC.
   398  	EnableGRPCGateway bool `json:"enable-grpc-gateway"`
   399  
   400  	// UnsafeNoFsync disables all uses of fsync.
   401  	// Setting this is unsafe and will cause data loss.
   402  	UnsafeNoFsync bool `json:"unsafe-no-fsync"`
   403  
   404  	ExperimentalDowngradeCheckTime time.Duration `json:"experimental-downgrade-check-time"`
   405  
   406  	// ExperimentalMemoryMlock enables mlocking of etcd owned memory pages.
   407  	// The setting improves etcd tail latency in environments were:
   408  	//   - memory pressure might lead to swapping pages to disk
   409  	//   - disk latency might be unstable
   410  	// Currently all etcd memory gets mlocked, but in future the flag can
   411  	// be refined to mlock in-use area of bbolt only.
   412  	ExperimentalMemoryMlock bool `json:"experimental-memory-mlock"`
   413  
   414  	// ExperimentalTxnModeWriteWithSharedBuffer enables write transaction to use a shared buffer in its readonly check operations.
   415  	ExperimentalTxnModeWriteWithSharedBuffer bool `json:"experimental-txn-mode-write-with-shared-buffer"`
   416  
   417  	// V2Deprecation describes phase of API & Storage V2 support
   418  	V2Deprecation config.V2DeprecationEnum `json:"v2-deprecation"`
   419  }
   420  
   421  // configYAML holds the config suitable for yaml parsing
   422  type configYAML struct {
   423  	Config
   424  	configJSON
   425  }
   426  
   427  // configJSON has file options that are translated into Config options
   428  type configJSON struct {
   429  	ListenPeerUrls       string `json:"listen-peer-urls"`
   430  	ListenClientUrls     string `json:"listen-client-urls"`
   431  	ListenClientHttpUrls string `json:"listen-client-http-urls"`
   432  	AdvertisePeerUrls    string `json:"initial-advertise-peer-urls"`
   433  	AdvertiseClientUrls  string `json:"advertise-client-urls"`
   434  
   435  	CORSJSON          string `json:"cors"`
   436  	HostWhitelistJSON string `json:"host-whitelist"`
   437  
   438  	ClientSecurityJSON securityConfig `json:"client-transport-security"`
   439  	PeerSecurityJSON   securityConfig `json:"peer-transport-security"`
   440  }
   441  
   442  type securityConfig struct {
   443  	CertFile       string `json:"cert-file"`
   444  	KeyFile        string `json:"key-file"`
   445  	ClientCertFile string `json:"client-cert-file"`
   446  	ClientKeyFile  string `json:"client-key-file"`
   447  	CertAuth       bool   `json:"client-cert-auth"`
   448  	TrustedCAFile  string `json:"trusted-ca-file"`
   449  	AutoTLS        bool   `json:"auto-tls"`
   450  }
   451  
   452  // NewConfig creates a new Config populated with default values.
   453  func NewConfig() *Config {
   454  	lpurl, _ := url.Parse(DefaultListenPeerURLs)
   455  	apurl, _ := url.Parse(DefaultInitialAdvertisePeerURLs)
   456  	lcurl, _ := url.Parse(DefaultListenClientURLs)
   457  	acurl, _ := url.Parse(DefaultAdvertiseClientURLs)
   458  	cfg := &Config{
   459  		MaxSnapFiles: DefaultMaxSnapshots,
   460  		MaxWalFiles:  DefaultMaxWALs,
   461  
   462  		Name: DefaultName,
   463  
   464  		SnapshotCount:          etcdserver.DefaultSnapshotCount,
   465  		SnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries,
   466  
   467  		MaxTxnOps:                        DefaultMaxTxnOps,
   468  		MaxRequestBytes:                  DefaultMaxRequestBytes,
   469  		MaxConcurrentStreams:             DefaultMaxConcurrentStreams,
   470  		ExperimentalWarningApplyDuration: DefaultWarningApplyDuration,
   471  
   472  		GRPCKeepAliveMinTime:  DefaultGRPCKeepAliveMinTime,
   473  		GRPCKeepAliveInterval: DefaultGRPCKeepAliveInterval,
   474  		GRPCKeepAliveTimeout:  DefaultGRPCKeepAliveTimeout,
   475  
   476  		SocketOpts: transport.SocketOpts{
   477  			ReusePort:    false,
   478  			ReuseAddress: false,
   479  		},
   480  
   481  		TickMs:                     100,
   482  		ElectionMs:                 1000,
   483  		InitialElectionTickAdvance: true,
   484  
   485  		ListenPeerUrls:      []url.URL{*lpurl},
   486  		ListenClientUrls:    []url.URL{*lcurl},
   487  		AdvertisePeerUrls:   []url.URL{*apurl},
   488  		AdvertiseClientUrls: []url.URL{*acurl},
   489  
   490  		ClusterState:        ClusterStateFlagNew,
   491  		InitialClusterToken: "etcd-cluster",
   492  
   493  		StrictReconfigCheck: DefaultStrictReconfigCheck,
   494  		Metrics:             "basic",
   495  		EnableV2:            DefaultEnableV2,
   496  
   497  		CORS:          map[string]struct{}{"*": {}},
   498  		HostWhitelist: map[string]struct{}{"*": {}},
   499  
   500  		AuthToken:    "simple",
   501  		BcryptCost:   uint(bcrypt.DefaultCost),
   502  		AuthTokenTTL: 300,
   503  
   504  		PreVote: true,
   505  
   506  		loggerMu:              new(sync.RWMutex),
   507  		logger:                nil,
   508  		Logger:                "zap",
   509  		LogOutputs:            []string{DefaultLogOutput},
   510  		LogLevel:              logutil.DefaultLogLevel,
   511  		EnableLogRotation:     false,
   512  		LogRotationConfigJSON: DefaultLogRotationConfig,
   513  		EnableGRPCGateway:     true,
   514  
   515  		ExperimentalDowngradeCheckTime:           DefaultDowngradeCheckTime,
   516  		ExperimentalMemoryMlock:                  false,
   517  		ExperimentalTxnModeWriteWithSharedBuffer: true,
   518  
   519  		ExperimentalCompactHashCheckEnabled: false,
   520  		ExperimentalCompactHashCheckTime:    time.Minute,
   521  
   522  		V2Deprecation: config.V2_DEPR_DEFAULT,
   523  	}
   524  	cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)
   525  	return cfg
   526  }
   527  
   528  func ConfigFromFile(path string) (*Config, error) {
   529  	cfg := &configYAML{Config: *NewConfig()}
   530  	if err := cfg.configFromFile(path); err != nil {
   531  		return nil, err
   532  	}
   533  	return &cfg.Config, nil
   534  }
   535  
   536  func (cfg *configYAML) configFromFile(path string) error {
   537  	b, err := ioutil.ReadFile(path)
   538  	if err != nil {
   539  		return err
   540  	}
   541  
   542  	defaultInitialCluster := cfg.InitialCluster
   543  
   544  	err = yaml.Unmarshal(b, cfg)
   545  	if err != nil {
   546  		return err
   547  	}
   548  
   549  	if cfg.configJSON.ListenPeerUrls != "" {
   550  		u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenPeerUrls, ","))
   551  		if err != nil {
   552  			fmt.Fprintf(os.Stderr, "unexpected error setting up listen-peer-urls: %v\n", err)
   553  			os.Exit(1)
   554  		}
   555  		cfg.Config.ListenPeerUrls = u
   556  	}
   557  
   558  	if cfg.configJSON.ListenClientUrls != "" {
   559  		u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenClientUrls, ","))
   560  		if err != nil {
   561  			fmt.Fprintf(os.Stderr, "unexpected error setting up listen-client-urls: %v\n", err)
   562  			os.Exit(1)
   563  		}
   564  		cfg.Config.ListenClientUrls = u
   565  	}
   566  
   567  	if cfg.configJSON.ListenClientHttpUrls != "" {
   568  		u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenClientHttpUrls, ","))
   569  		if err != nil {
   570  			fmt.Fprintf(os.Stderr, "unexpected error setting up listen-client-http-urls: %v\n", err)
   571  			os.Exit(1)
   572  		}
   573  		cfg.Config.ListenClientHttpUrls = u
   574  	}
   575  
   576  	if cfg.configJSON.AdvertisePeerUrls != "" {
   577  		u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertisePeerUrls, ","))
   578  		if err != nil {
   579  			fmt.Fprintf(os.Stderr, "unexpected error setting up initial-advertise-peer-urls: %v\n", err)
   580  			os.Exit(1)
   581  		}
   582  		cfg.Config.AdvertisePeerUrls = u
   583  	}
   584  
   585  	if cfg.configJSON.AdvertiseClientUrls != "" {
   586  		u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertiseClientUrls, ","))
   587  		if err != nil {
   588  			fmt.Fprintf(os.Stderr, "unexpected error setting up advertise-peer-urls: %v\n", err)
   589  			os.Exit(1)
   590  		}
   591  		cfg.Config.AdvertiseClientUrls = u
   592  	}
   593  
   594  	if cfg.ListenMetricsUrlsJSON != "" {
   595  		u, err := types.NewURLs(strings.Split(cfg.ListenMetricsUrlsJSON, ","))
   596  		if err != nil {
   597  			fmt.Fprintf(os.Stderr, "unexpected error setting up listen-metrics-urls: %v\n", err)
   598  			os.Exit(1)
   599  		}
   600  		cfg.ListenMetricsUrls = []url.URL(u)
   601  	}
   602  
   603  	if cfg.CORSJSON != "" {
   604  		uv := flags.NewUniqueURLsWithExceptions(cfg.CORSJSON, "*")
   605  		cfg.CORS = uv.Values
   606  	}
   607  
   608  	if cfg.HostWhitelistJSON != "" {
   609  		uv := flags.NewUniqueStringsValue(cfg.HostWhitelistJSON)
   610  		cfg.HostWhitelist = uv.Values
   611  	}
   612  
   613  	// If a discovery flag is set, clear default initial cluster set by InitialClusterFromName
   614  	if (cfg.Durl != "" || cfg.DNSCluster != "") && cfg.InitialCluster == defaultInitialCluster {
   615  		cfg.InitialCluster = ""
   616  	}
   617  	if cfg.ClusterState == "" {
   618  		cfg.ClusterState = ClusterStateFlagNew
   619  	}
   620  
   621  	copySecurityDetails := func(tls *transport.TLSInfo, ysc *securityConfig) {
   622  		tls.CertFile = ysc.CertFile
   623  		tls.KeyFile = ysc.KeyFile
   624  		tls.ClientCertFile = ysc.ClientCertFile
   625  		tls.ClientKeyFile = ysc.ClientKeyFile
   626  		tls.ClientCertAuth = ysc.CertAuth
   627  		tls.TrustedCAFile = ysc.TrustedCAFile
   628  	}
   629  	copySecurityDetails(&cfg.ClientTLSInfo, &cfg.ClientSecurityJSON)
   630  	copySecurityDetails(&cfg.PeerTLSInfo, &cfg.PeerSecurityJSON)
   631  	cfg.ClientAutoTLS = cfg.ClientSecurityJSON.AutoTLS
   632  	cfg.PeerAutoTLS = cfg.PeerSecurityJSON.AutoTLS
   633  	if cfg.SelfSignedCertValidity == 0 {
   634  		cfg.SelfSignedCertValidity = 1
   635  	}
   636  	return cfg.Validate()
   637  }
   638  
   639  func updateCipherSuites(tls *transport.TLSInfo, ss []string) error {
   640  	if len(tls.CipherSuites) > 0 && len(ss) > 0 {
   641  		return fmt.Errorf("TLSInfo.CipherSuites is already specified (given %v)", ss)
   642  	}
   643  	if len(ss) > 0 {
   644  		cs, err := tlsutil.GetCipherSuites(ss)
   645  		if err != nil {
   646  			return err
   647  		}
   648  		tls.CipherSuites = cs
   649  	}
   650  	return nil
   651  }
   652  
   653  func updateMinMaxVersions(info *transport.TLSInfo, min, max string) {
   654  	// Validate() has been called to check the user input, so it should never fail.
   655  	var err error
   656  	if info.MinVersion, err = tlsutil.GetTLSVersion(min); err != nil {
   657  		panic(err)
   658  	}
   659  	if info.MaxVersion, err = tlsutil.GetTLSVersion(max); err != nil {
   660  		panic(err)
   661  	}
   662  }
   663  
   664  // Validate ensures that '*embed.Config' fields are properly configured.
   665  func (cfg *Config) Validate() error {
   666  	if err := cfg.setupLogging(); err != nil {
   667  		return err
   668  	}
   669  	if err := checkBindURLs(cfg.ListenPeerUrls); err != nil {
   670  		return err
   671  	}
   672  	if err := checkBindURLs(cfg.ListenClientUrls); err != nil {
   673  		return err
   674  	}
   675  	if err := checkBindURLs(cfg.ListenClientHttpUrls); err != nil {
   676  		return err
   677  	}
   678  	if len(cfg.ListenClientHttpUrls) == 0 {
   679  		cfg.logger.Warn("Running http and grpc server on single port. This is not recommended for production.")
   680  	}
   681  	if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil {
   682  		return err
   683  	}
   684  	if err := checkHostURLs(cfg.AdvertisePeerUrls); err != nil {
   685  		addrs := cfg.getAdvertisePeerUrls()
   686  		return fmt.Errorf(`--initial-advertise-peer-urls %q must be "host:port" (%v)`, strings.Join(addrs, ","), err)
   687  	}
   688  	if err := checkHostURLs(cfg.AdvertiseClientUrls); err != nil {
   689  		addrs := cfg.getAdvertiseClientUrls()
   690  		return fmt.Errorf(`--advertise-client-urls %q must be "host:port" (%v)`, strings.Join(addrs, ","), err)
   691  	}
   692  	// Check if conflicting flags are passed.
   693  	nSet := 0
   694  	for _, v := range []bool{cfg.Durl != "", cfg.InitialCluster != "", cfg.DNSCluster != ""} {
   695  		if v {
   696  			nSet++
   697  		}
   698  	}
   699  
   700  	if cfg.ClusterState != ClusterStateFlagNew && cfg.ClusterState != ClusterStateFlagExisting {
   701  		return fmt.Errorf("unexpected clusterState %q", cfg.ClusterState)
   702  	}
   703  
   704  	if nSet > 1 {
   705  		return ErrConflictBootstrapFlags
   706  	}
   707  
   708  	if cfg.TickMs == 0 {
   709  		return fmt.Errorf("--heartbeat-interval must be >0 (set to %dms)", cfg.TickMs)
   710  	}
   711  	if cfg.ElectionMs == 0 {
   712  		return fmt.Errorf("--election-timeout must be >0 (set to %dms)", cfg.ElectionMs)
   713  	}
   714  	if 5*cfg.TickMs > cfg.ElectionMs {
   715  		return fmt.Errorf("--election-timeout[%vms] should be at least as 5 times as --heartbeat-interval[%vms]", cfg.ElectionMs, cfg.TickMs)
   716  	}
   717  	if cfg.ElectionMs > maxElectionMs {
   718  		return fmt.Errorf("--election-timeout[%vms] is too long, and should be set less than %vms", cfg.ElectionMs, maxElectionMs)
   719  	}
   720  
   721  	// check this last since proxying in etcdmain may make this OK
   722  	if cfg.ListenClientUrls != nil && cfg.AdvertiseClientUrls == nil {
   723  		return ErrUnsetAdvertiseClientURLsFlag
   724  	}
   725  
   726  	switch cfg.AutoCompactionMode {
   727  	case "":
   728  	case CompactorModeRevision, CompactorModePeriodic:
   729  	default:
   730  		return fmt.Errorf("unknown auto-compaction-mode %q", cfg.AutoCompactionMode)
   731  	}
   732  
   733  	if !cfg.ExperimentalEnableLeaseCheckpointPersist && cfg.ExperimentalEnableLeaseCheckpoint {
   734  		cfg.logger.Warn("Detected that checkpointing is enabled without persistence. Consider enabling experimental-enable-lease-checkpoint-persist")
   735  	}
   736  
   737  	if cfg.ExperimentalEnableLeaseCheckpointPersist && !cfg.ExperimentalEnableLeaseCheckpoint {
   738  		return fmt.Errorf("setting experimental-enable-lease-checkpoint-persist requires experimental-enable-lease-checkpoint")
   739  	}
   740  
   741  	if cfg.ExperimentalCompactHashCheckTime <= 0 {
   742  		return fmt.Errorf("--experimental-compact-hash-check-time must be >0 (set to %v)", cfg.ExperimentalCompactHashCheckTime)
   743  	}
   744  
   745  	minVersion, err := tlsutil.GetTLSVersion(cfg.TlsMinVersion)
   746  	if err != nil {
   747  		return err
   748  	}
   749  	maxVersion, err := tlsutil.GetTLSVersion(cfg.TlsMaxVersion)
   750  	if err != nil {
   751  		return err
   752  	}
   753  
   754  	// maxVersion == 0 means that Go selects the highest available version.
   755  	if maxVersion != 0 && minVersion > maxVersion {
   756  		return fmt.Errorf("min version (%s) is greater than max version (%s)", cfg.TlsMinVersion, cfg.TlsMaxVersion)
   757  	}
   758  
   759  	// Check if user attempted to configure ciphers for TLS1.3 only: Go does not support that currently.
   760  	if minVersion == tls.VersionTLS13 && len(cfg.CipherSuites) > 0 {
   761  		return fmt.Errorf("cipher suites cannot be configured when only TLS1.3 is enabled")
   762  	}
   763  
   764  	// Validate distributed tracing configuration but only if enabled.
   765  	if cfg.ExperimentalEnableDistributedTracing {
   766  		if err := validateTracingConfig(cfg.ExperimentalDistributedTracingSamplingRatePerMillion); err != nil {
   767  			return fmt.Errorf("distributed tracing configurition is not valid: (%v)", err)
   768  		}
   769  	}
   770  
   771  	return nil
   772  }
   773  
   774  // PeerURLsMapAndToken sets up an initial peer URLsMap and cluster token for bootstrap or discovery.
   775  func (cfg *Config) PeerURLsMapAndToken(which string) (urlsmap types.URLsMap, token string, err error) {
   776  	token = cfg.InitialClusterToken
   777  	switch {
   778  	case cfg.Durl != "":
   779  		urlsmap = types.URLsMap{}
   780  		// If using discovery, generate a temporary cluster based on
   781  		// self's advertised peer URLs
   782  		urlsmap[cfg.Name] = cfg.AdvertisePeerUrls
   783  		token = cfg.Durl
   784  
   785  	case cfg.DNSCluster != "":
   786  		clusterStrs, cerr := cfg.GetDNSClusterNames()
   787  		lg := cfg.logger
   788  		if cerr != nil {
   789  			lg.Warn("failed to resolve during SRV discovery", zap.Error(cerr))
   790  		}
   791  		if len(clusterStrs) == 0 {
   792  			return nil, "", cerr
   793  		}
   794  		for _, s := range clusterStrs {
   795  			lg.Info("got bootstrap from DNS for etcd-server", zap.String("node", s))
   796  		}
   797  		clusterStr := strings.Join(clusterStrs, ",")
   798  		if strings.Contains(clusterStr, "https://") && cfg.PeerTLSInfo.TrustedCAFile == "" {
   799  			cfg.PeerTLSInfo.ServerName = cfg.DNSCluster
   800  		}
   801  		urlsmap, err = types.NewURLsMap(clusterStr)
   802  		// only etcd member must belong to the discovered cluster.
   803  		// proxy does not need to belong to the discovered cluster.
   804  		if which == "etcd" {
   805  			if _, ok := urlsmap[cfg.Name]; !ok {
   806  				return nil, "", fmt.Errorf("cannot find local etcd member %q in SRV records", cfg.Name)
   807  			}
   808  		}
   809  
   810  	default:
   811  		// We're statically configured, and cluster has appropriately been set.
   812  		urlsmap, err = types.NewURLsMap(cfg.InitialCluster)
   813  	}
   814  	return urlsmap, token, err
   815  }
   816  
   817  // GetDNSClusterNames uses DNS SRV records to get a list of initial nodes for cluster bootstrapping.
   818  // This function will return a list of one or more nodes, as well as any errors encountered while
   819  // performing service discovery.
   820  // Note: Because this checks multiple sets of SRV records, discovery should only be considered to have
   821  // failed if the returned node list is empty.
   822  func (cfg *Config) GetDNSClusterNames() ([]string, error) {
   823  	var (
   824  		clusterStrs       []string
   825  		cerr              error
   826  		serviceNameSuffix string
   827  	)
   828  	if cfg.DNSClusterServiceName != "" {
   829  		serviceNameSuffix = "-" + cfg.DNSClusterServiceName
   830  	}
   831  
   832  	lg := cfg.GetLogger()
   833  
   834  	// Use both etcd-server-ssl and etcd-server for discovery.
   835  	// Combine the results if both are available.
   836  	clusterStrs, cerr = getCluster("https", "etcd-server-ssl"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.AdvertisePeerUrls)
   837  	if cerr != nil {
   838  		clusterStrs = make([]string, 0)
   839  	}
   840  	lg.Info(
   841  		"get cluster for etcd-server-ssl SRV",
   842  		zap.String("service-scheme", "https"),
   843  		zap.String("service-name", "etcd-server-ssl"+serviceNameSuffix),
   844  		zap.String("server-name", cfg.Name),
   845  		zap.String("discovery-srv", cfg.DNSCluster),
   846  		zap.Strings("advertise-peer-urls", cfg.getAdvertisePeerUrls()),
   847  		zap.Strings("found-cluster", clusterStrs),
   848  		zap.Error(cerr),
   849  	)
   850  
   851  	defaultHTTPClusterStrs, httpCerr := getCluster("http", "etcd-server"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.AdvertisePeerUrls)
   852  	if httpCerr == nil {
   853  		clusterStrs = append(clusterStrs, defaultHTTPClusterStrs...)
   854  	}
   855  	lg.Info(
   856  		"get cluster for etcd-server SRV",
   857  		zap.String("service-scheme", "http"),
   858  		zap.String("service-name", "etcd-server"+serviceNameSuffix),
   859  		zap.String("server-name", cfg.Name),
   860  		zap.String("discovery-srv", cfg.DNSCluster),
   861  		zap.Strings("advertise-peer-urls", cfg.getAdvertisePeerUrls()),
   862  		zap.Strings("found-cluster", clusterStrs),
   863  		zap.Error(httpCerr),
   864  	)
   865  
   866  	return clusterStrs, multierr.Combine(cerr, httpCerr)
   867  }
   868  
   869  func (cfg Config) InitialClusterFromName(name string) (ret string) {
   870  	if len(cfg.AdvertisePeerUrls) == 0 {
   871  		return ""
   872  	}
   873  	n := name
   874  	if name == "" {
   875  		n = DefaultName
   876  	}
   877  	for i := range cfg.AdvertisePeerUrls {
   878  		ret = ret + "," + n + "=" + cfg.AdvertisePeerUrls[i].String()
   879  	}
   880  	return ret[1:]
   881  }
   882  
   883  func (cfg Config) IsNewCluster() bool { return cfg.ClusterState == ClusterStateFlagNew }
   884  func (cfg Config) ElectionTicks() int { return int(cfg.ElectionMs / cfg.TickMs) }
   885  
   886  func (cfg Config) V2DeprecationEffective() config.V2DeprecationEnum {
   887  	if cfg.V2Deprecation == "" {
   888  		return config.V2_DEPR_DEFAULT
   889  	}
   890  	return cfg.V2Deprecation
   891  }
   892  
   893  func (cfg Config) defaultPeerHost() bool {
   894  	return len(cfg.AdvertisePeerUrls) == 1 && cfg.AdvertisePeerUrls[0].String() == DefaultInitialAdvertisePeerURLs
   895  }
   896  
   897  func (cfg Config) defaultClientHost() bool {
   898  	return len(cfg.AdvertiseClientUrls) == 1 && cfg.AdvertiseClientUrls[0].String() == DefaultAdvertiseClientURLs
   899  }
   900  
   901  func (cfg *Config) ClientSelfCert() (err error) {
   902  	if !cfg.ClientAutoTLS {
   903  		return nil
   904  	}
   905  	if !cfg.ClientTLSInfo.Empty() {
   906  		cfg.logger.Warn("ignoring client auto TLS since certs given")
   907  		return nil
   908  	}
   909  	chosts := make([]string, 0, len(cfg.ListenClientUrls)+len(cfg.ListenClientHttpUrls))
   910  	for _, u := range cfg.ListenClientUrls {
   911  		chosts = append(chosts, u.Host)
   912  	}
   913  	for _, u := range cfg.ListenClientHttpUrls {
   914  		chosts = append(chosts, u.Host)
   915  	}
   916  	cfg.ClientTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "client"), chosts, cfg.SelfSignedCertValidity)
   917  	if err != nil {
   918  		return err
   919  	}
   920  	return updateCipherSuites(&cfg.ClientTLSInfo, cfg.CipherSuites)
   921  }
   922  
   923  func (cfg *Config) PeerSelfCert() (err error) {
   924  	if !cfg.PeerAutoTLS {
   925  		return nil
   926  	}
   927  	if !cfg.PeerTLSInfo.Empty() {
   928  		cfg.logger.Warn("ignoring peer auto TLS since certs given")
   929  		return nil
   930  	}
   931  	phosts := make([]string, len(cfg.ListenPeerUrls))
   932  	for i, u := range cfg.ListenPeerUrls {
   933  		phosts[i] = u.Host
   934  	}
   935  	cfg.PeerTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "peer"), phosts, cfg.SelfSignedCertValidity)
   936  	if err != nil {
   937  		return err
   938  	}
   939  	return updateCipherSuites(&cfg.PeerTLSInfo, cfg.CipherSuites)
   940  }
   941  
   942  // UpdateDefaultClusterFromName updates cluster advertise URLs with, if available, default host,
   943  // if advertise URLs are default values(localhost:2379,2380) AND if listen URL is 0.0.0.0.
   944  // e.g. advertise peer URL localhost:2380 or listen peer URL 0.0.0.0:2380
   945  // then the advertise peer host would be updated with machine's default host,
   946  // while keeping the listen URL's port.
   947  // User can work around this by explicitly setting URL with 127.0.0.1.
   948  // It returns the default hostname, if used, and the error, if any, from getting the machine's default host.
   949  // TODO: check whether fields are set instead of whether fields have default value
   950  func (cfg *Config) UpdateDefaultClusterFromName(defaultInitialCluster string) (string, error) {
   951  	if defaultHostname == "" || defaultHostStatus != nil {
   952  		// update 'initial-cluster' when only the name is specified (e.g. 'etcd --name=abc')
   953  		if cfg.Name != DefaultName && cfg.InitialCluster == defaultInitialCluster {
   954  			cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)
   955  		}
   956  		return "", defaultHostStatus
   957  	}
   958  
   959  	used := false
   960  	pip, pport := cfg.ListenPeerUrls[0].Hostname(), cfg.ListenPeerUrls[0].Port()
   961  	if cfg.defaultPeerHost() && pip == "0.0.0.0" {
   962  		cfg.AdvertisePeerUrls[0] = url.URL{Scheme: cfg.AdvertisePeerUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, pport)}
   963  		used = true
   964  	}
   965  	// update 'initial-cluster' when only the name is specified (e.g. 'etcd --name=abc')
   966  	if cfg.Name != DefaultName && cfg.InitialCluster == defaultInitialCluster {
   967  		cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)
   968  	}
   969  
   970  	cip, cport := cfg.ListenClientUrls[0].Hostname(), cfg.ListenClientUrls[0].Port()
   971  	if cfg.defaultClientHost() && cip == "0.0.0.0" {
   972  		cfg.AdvertiseClientUrls[0] = url.URL{Scheme: cfg.AdvertiseClientUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, cport)}
   973  		used = true
   974  	}
   975  	dhost := defaultHostname
   976  	if !used {
   977  		dhost = ""
   978  	}
   979  	return dhost, defaultHostStatus
   980  }
   981  
   982  // checkBindURLs returns an error if any URL uses a domain name.
   983  func checkBindURLs(urls []url.URL) error {
   984  	for _, url := range urls {
   985  		if url.Scheme == "unix" || url.Scheme == "unixs" {
   986  			continue
   987  		}
   988  		host, _, err := net.SplitHostPort(url.Host)
   989  		if err != nil {
   990  			return err
   991  		}
   992  		if host == "localhost" {
   993  			// special case for local address
   994  			// TODO: support /etc/hosts ?
   995  			continue
   996  		}
   997  		if net.ParseIP(host) == nil {
   998  			return fmt.Errorf("expected IP in URL for binding (%s)", url.String())
   999  		}
  1000  	}
  1001  	return nil
  1002  }
  1003  
  1004  func checkHostURLs(urls []url.URL) error {
  1005  	for _, url := range urls {
  1006  		host, _, err := net.SplitHostPort(url.Host)
  1007  		if err != nil {
  1008  			return err
  1009  		}
  1010  		if host == "" {
  1011  			return fmt.Errorf("unexpected empty host (%s)", url.String())
  1012  		}
  1013  	}
  1014  	return nil
  1015  }
  1016  
  1017  func (cfg *Config) getAdvertisePeerUrls() (ss []string) {
  1018  	ss = make([]string, len(cfg.AdvertisePeerUrls))
  1019  	for i := range cfg.AdvertisePeerUrls {
  1020  		ss[i] = cfg.AdvertisePeerUrls[i].String()
  1021  	}
  1022  	return ss
  1023  }
  1024  
  1025  func (cfg *Config) getListenPeerUrls() (ss []string) {
  1026  	ss = make([]string, len(cfg.ListenPeerUrls))
  1027  	for i := range cfg.ListenPeerUrls {
  1028  		ss[i] = cfg.ListenPeerUrls[i].String()
  1029  	}
  1030  	return ss
  1031  }
  1032  
  1033  func (cfg *Config) getAdvertiseClientUrls() (ss []string) {
  1034  	ss = make([]string, len(cfg.AdvertiseClientUrls))
  1035  	for i := range cfg.AdvertiseClientUrls {
  1036  		ss[i] = cfg.AdvertiseClientUrls[i].String()
  1037  	}
  1038  	return ss
  1039  }
  1040  
  1041  func (cfg *Config) getListenClientUrls() (ss []string) {
  1042  	ss = make([]string, len(cfg.ListenClientUrls))
  1043  	for i := range cfg.ListenClientUrls {
  1044  		ss[i] = cfg.ListenClientUrls[i].String()
  1045  	}
  1046  	return ss
  1047  }
  1048  
  1049  func (cfg *Config) getListenClientHttpUrls() (ss []string) {
  1050  	ss = make([]string, len(cfg.ListenClientHttpUrls))
  1051  	for i := range cfg.ListenClientHttpUrls {
  1052  		ss[i] = cfg.ListenClientHttpUrls[i].String()
  1053  	}
  1054  	return ss
  1055  }
  1056  
  1057  func (cfg *Config) getMetricsURLs() (ss []string) {
  1058  	ss = make([]string, len(cfg.ListenMetricsUrls))
  1059  	for i := range cfg.ListenMetricsUrls {
  1060  		ss[i] = cfg.ListenMetricsUrls[i].String()
  1061  	}
  1062  	return ss
  1063  }
  1064  
  1065  func parseBackendFreelistType(freelistType string) bolt.FreelistType {
  1066  	if freelistType == freelistArrayType {
  1067  		return bolt.FreelistArrayType
  1068  	}
  1069  
  1070  	return bolt.FreelistMapType
  1071  }
  1072  

View as plain text