1// Copyright 2015 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 15package etcdserver 16 17import ( 18 "fmt" 19 "path/filepath" 20 "sort" 21 "strings" 22 "time" 23 24 "golang.org/x/net/context" 25 26 "github.com/coreos/etcd/pkg/netutil" 27 "github.com/coreos/etcd/pkg/transport" 28 "github.com/coreos/etcd/pkg/types" 29) 30 31// ServerConfig holds the configuration of etcd as taken from the command line or discovery. 32type ServerConfig struct { 33 Name string 34 DiscoveryURL string 35 DiscoveryProxy string 36 ClientURLs types.URLs 37 PeerURLs types.URLs 38 DataDir string 39 // DedicatedWALDir config will make the etcd to write the WAL to the WALDir 40 // rather than the dataDir/member/wal. 41 DedicatedWALDir string 42 SnapCount uint64 43 MaxSnapFiles uint 44 MaxWALFiles uint 45 InitialPeerURLsMap types.URLsMap 46 InitialClusterToken string 47 NewCluster bool 48 ForceNewCluster bool 49 PeerTLSInfo transport.TLSInfo 50 51 TickMs uint 52 ElectionTicks int 53 54 // InitialElectionTickAdvance is true, then local member fast-forwards 55 // election ticks to speed up "initial" leader election trigger. This 56 // benefits the case of larger election ticks. For instance, cross 57 // datacenter deployment may require longer election timeout of 10-second. 58 // If true, local node does not need wait up to 10-second. Instead, 59 // forwards its election ticks to 8-second, and have only 2-second left 60 // before leader election. 61 // 62 // Major assumptions are that: 63 // - cluster has no active leader thus advancing ticks enables faster 64 // leader election, or 65 // - cluster already has an established leader, and rejoining follower 66 // is likely to receive heartbeats from the leader after tick advance 67 // and before election timeout. 68 // 69 // However, when network from leader to rejoining follower is congested, 70 // and the follower does not receive leader heartbeat within left election 71 // ticks, disruptive election has to happen thus affecting cluster 72 // availabilities. 73 // 74 // Disabling this would slow down initial bootstrap process for cross 75 // datacenter deployments. Make your own tradeoffs by configuring 76 // --initial-election-tick-advance at the cost of slow initial bootstrap. 77 // 78 // If single-node, it advances ticks regardless. 79 // 80 // See https://github.com/coreos/etcd/issues/9333 for more detail. 81 InitialElectionTickAdvance bool 82 83 BootstrapTimeout time.Duration 84 85 AutoCompactionRetention int 86 QuotaBackendBytes int64 87 88 StrictReconfigCheck bool 89 90 // ClientCertAuthEnabled is true when cert has been signed by the client CA. 91 ClientCertAuthEnabled bool 92} 93 94// VerifyBootstrap sanity-checks the initial config for bootstrap case 95// and returns an error for things that should never happen. 96func (c *ServerConfig) VerifyBootstrap() error { 97 if err := c.hasLocalMember(); err != nil { 98 return err 99 } 100 if err := c.advertiseMatchesCluster(); err != nil { 101 return err 102 } 103 if checkDuplicateURL(c.InitialPeerURLsMap) { 104 return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap) 105 } 106 if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" { 107 return fmt.Errorf("initial cluster unset and no discovery URL found") 108 } 109 return nil 110} 111 112// VerifyJoinExisting sanity-checks the initial config for join existing cluster 113// case and returns an error for things that should never happen. 114func (c *ServerConfig) VerifyJoinExisting() error { 115 // The member has announced its peer urls to the cluster before starting; no need to 116 // set the configuration again. 117 if err := c.hasLocalMember(); err != nil { 118 return err 119 } 120 if checkDuplicateURL(c.InitialPeerURLsMap) { 121 return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap) 122 } 123 if c.DiscoveryURL != "" { 124 return fmt.Errorf("discovery URL should not be set when joining existing initial cluster") 125 } 126 return nil 127} 128 129// hasLocalMember checks that the cluster at least contains the local server. 130func (c *ServerConfig) hasLocalMember() error { 131 if urls := c.InitialPeerURLsMap[c.Name]; urls == nil { 132 return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name) 133 } 134 return nil 135} 136 137// advertiseMatchesCluster confirms peer URLs match those in the cluster peer list. 138func (c *ServerConfig) advertiseMatchesCluster() error { 139 urls, apurls := c.InitialPeerURLsMap[c.Name], c.PeerURLs.StringSlice() 140 urls.Sort() 141 sort.Strings(apurls) 142 ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) 143 defer cancel() 144 if !netutil.URLStringsEqual(ctx, apurls, urls.StringSlice()) { 145 umap := map[string]types.URLs{c.Name: c.PeerURLs} 146 return fmt.Errorf("--initial-cluster must include %s given --initial-advertise-peer-urls=%s", types.URLsMap(umap).String(), strings.Join(apurls, ",")) 147 } 148 return nil 149} 150 151func (c *ServerConfig) MemberDir() string { return filepath.Join(c.DataDir, "member") } 152 153func (c *ServerConfig) WALDir() string { 154 if c.DedicatedWALDir != "" { 155 return c.DedicatedWALDir 156 } 157 return filepath.Join(c.MemberDir(), "wal") 158} 159 160func (c *ServerConfig) SnapDir() string { return filepath.Join(c.MemberDir(), "snap") } 161 162func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" } 163 164// ReqTimeout returns timeout for request to finish. 165func (c *ServerConfig) ReqTimeout() time.Duration { 166 // 5s for queue waiting, computation and disk IO delay 167 // + 2 * election timeout for possible leader election 168 return 5*time.Second + 2*time.Duration(c.ElectionTicks)*time.Duration(c.TickMs)*time.Millisecond 169} 170 171func (c *ServerConfig) electionTimeout() time.Duration { 172 return time.Duration(c.ElectionTicks) * time.Duration(c.TickMs) * time.Millisecond 173} 174 175func (c *ServerConfig) peerDialTimeout() time.Duration { 176 // 1s for queue wait and system delay 177 // + one RTT, which is smaller than 1/5 election timeout 178 return time.Second + time.Duration(c.ElectionTicks)*time.Duration(c.TickMs)*time.Millisecond/5 179} 180 181func (c *ServerConfig) PrintWithInitial() { c.print(true) } 182 183func (c *ServerConfig) Print() { c.print(false) } 184 185func (c *ServerConfig) print(initial bool) { 186 plog.Infof("name = %s", c.Name) 187 if c.ForceNewCluster { 188 plog.Infof("force new cluster") 189 } 190 plog.Infof("data dir = %s", c.DataDir) 191 plog.Infof("member dir = %s", c.MemberDir()) 192 if c.DedicatedWALDir != "" { 193 plog.Infof("dedicated WAL dir = %s", c.DedicatedWALDir) 194 } 195 plog.Infof("heartbeat = %dms", c.TickMs) 196 plog.Infof("election = %dms", c.ElectionTicks*int(c.TickMs)) 197 plog.Infof("snapshot count = %d", c.SnapCount) 198 if len(c.DiscoveryURL) != 0 { 199 plog.Infof("discovery URL= %s", c.DiscoveryURL) 200 if len(c.DiscoveryProxy) != 0 { 201 plog.Infof("discovery proxy = %s", c.DiscoveryProxy) 202 } 203 } 204 plog.Infof("advertise client URLs = %s", c.ClientURLs) 205 if initial { 206 plog.Infof("initial advertise peer URLs = %s", c.PeerURLs) 207 plog.Infof("initial cluster = %s", c.InitialPeerURLsMap) 208 } 209} 210 211func checkDuplicateURL(urlsmap types.URLsMap) bool { 212 um := make(map[string]bool) 213 for _, urls := range urlsmap { 214 for _, url := range urls { 215 u := url.String() 216 if um[u] { 217 return true 218 } 219 um[u] = true 220 } 221 } 222 return false 223} 224 225func (c *ServerConfig) bootstrapTimeout() time.Duration { 226 if c.BootstrapTimeout != 0 { 227 return c.BootstrapTimeout 228 } 229 return time.Second 230} 231