github.com/ghodss/etcd@v0.3.1-0.20140417172404-cc329bfa55cb/etcd/etcd.go (about) 1 /* 2 Copyright 2013 CoreOS Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package etcd 18 19 import ( 20 "net" 21 "net/http" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strings" 26 "time" 27 28 goetcd "github.com/coreos/etcd/third_party/github.com/coreos/go-etcd/etcd" 29 golog "github.com/coreos/etcd/third_party/github.com/coreos/go-log/log" 30 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 31 32 "github.com/coreos/etcd/config" 33 ehttp "github.com/coreos/etcd/http" 34 "github.com/coreos/etcd/log" 35 "github.com/coreos/etcd/metrics" 36 "github.com/coreos/etcd/server" 37 "github.com/coreos/etcd/store" 38 ) 39 40 type Etcd struct { 41 Config *config.Config // etcd config 42 Store store.Store // data store 43 Registry *server.Registry // stores URL information for nodes 44 Server *server.Server // http server, runs on 4001 by default 45 PeerServer *server.PeerServer // peer server, runs on 7001 by default 46 listener net.Listener // Listener for Server 47 peerListener net.Listener // Listener for PeerServer 48 readyC chan bool // To signal when server is ready to accept connections 49 } 50 51 // New returns a new Etcd instance. 52 func New(c *config.Config) *Etcd { 53 if c == nil { 54 c = config.New() 55 } 56 return &Etcd{ 57 Config: c, 58 readyC: make(chan bool), 59 } 60 } 61 62 // Run the etcd instance. 63 func (e *Etcd) Run() { 64 // Enable options. 65 if e.Config.VeryVeryVerbose { 66 log.Verbose = true 67 raft.SetLogLevel(raft.Trace) 68 goetcd.SetLogger( 69 golog.New( 70 "go-etcd", 71 false, 72 golog.CombinedSink( 73 os.Stdout, 74 "[%s] %s %-9s | %s\n", 75 []string{"prefix", "time", "priority", "message"}, 76 ), 77 ), 78 ) 79 } else if e.Config.VeryVerbose { 80 log.Verbose = true 81 raft.SetLogLevel(raft.Debug) 82 } else if e.Config.Verbose { 83 log.Verbose = true 84 } 85 86 if e.Config.CPUProfileFile != "" { 87 profile(e.Config.CPUProfileFile) 88 } 89 90 if e.Config.DataDir == "" { 91 log.Fatal("The data dir was not set and could not be guessed from machine name") 92 } 93 94 // Create data directory if it doesn't already exist. 95 if err := os.MkdirAll(e.Config.DataDir, 0744); err != nil { 96 log.Fatalf("Unable to create path: %s", err) 97 } 98 99 // Warn people if they have an info file 100 info := filepath.Join(e.Config.DataDir, "info") 101 if _, err := os.Stat(info); err == nil { 102 log.Warnf("All cached configuration is now ignored. The file %s can be removed.", info) 103 } 104 105 var mbName string 106 if e.Config.Trace() { 107 mbName = e.Config.MetricsBucketName() 108 runtime.SetBlockProfileRate(1) 109 } 110 111 mb := metrics.NewBucket(mbName) 112 113 if e.Config.GraphiteHost != "" { 114 err := mb.Publish(e.Config.GraphiteHost) 115 if err != nil { 116 panic(err) 117 } 118 } 119 120 // Retrieve CORS configuration 121 corsInfo, err := ehttp.NewCORSInfo(e.Config.CorsOrigins) 122 if err != nil { 123 log.Fatal("CORS:", err) 124 } 125 126 // Create etcd key-value store and registry. 127 e.Store = store.New() 128 e.Registry = server.NewRegistry(e.Store) 129 130 // Create stats objects 131 followersStats := server.NewRaftFollowersStats(e.Config.Name) 132 serverStats := server.NewRaftServerStats(e.Config.Name) 133 134 // Calculate all of our timeouts 135 heartbeatInterval := time.Duration(e.Config.Peer.HeartbeatInterval) * time.Millisecond 136 electionTimeout := time.Duration(e.Config.Peer.ElectionTimeout) * time.Millisecond 137 dialTimeout := (3 * heartbeatInterval) + electionTimeout 138 responseHeaderTimeout := (3 * heartbeatInterval) + electionTimeout 139 140 // Create peer server 141 psConfig := server.PeerServerConfig{ 142 Name: e.Config.Name, 143 Scheme: e.Config.PeerTLSInfo().Scheme(), 144 URL: e.Config.Peer.Addr, 145 SnapshotCount: e.Config.SnapshotCount, 146 RetryTimes: e.Config.MaxRetryAttempts, 147 RetryInterval: e.Config.RetryInterval, 148 } 149 e.PeerServer = server.NewPeerServer(psConfig, e.Registry, e.Store, &mb, followersStats, serverStats) 150 151 // Create raft transporter and server 152 raftTransporter := server.NewTransporter(followersStats, serverStats, e.Registry, heartbeatInterval, dialTimeout, responseHeaderTimeout) 153 if psConfig.Scheme == "https" { 154 raftClientTLSConfig, err := e.Config.PeerTLSInfo().ClientConfig() 155 if err != nil { 156 log.Fatal("raft client TLS error: ", err) 157 } 158 raftTransporter.SetTLSConfig(*raftClientTLSConfig) 159 } 160 raftServer, err := raft.NewServer(e.Config.Name, e.Config.DataDir, raftTransporter, e.Store, e.PeerServer, "") 161 if err != nil { 162 log.Fatal(err) 163 } 164 raftServer.SetElectionTimeout(electionTimeout) 165 raftServer.SetHeartbeatInterval(heartbeatInterval) 166 e.PeerServer.SetRaftServer(raftServer) 167 168 // Create etcd server 169 e.Server = server.New(e.Config.Name, e.Config.Addr, e.PeerServer, e.Registry, e.Store, &mb) 170 171 if e.Config.Trace() { 172 e.Server.EnableTracing() 173 } 174 175 e.PeerServer.SetServer(e.Server) 176 177 // Generating config could be slow. 178 // Put it here to make listen happen immediately after peer-server starting. 179 peerTLSConfig := server.TLSServerConfig(e.Config.PeerTLSInfo()) 180 etcdTLSConfig := server.TLSServerConfig(e.Config.EtcdTLSInfo()) 181 182 log.Infof("etcd server [name %s, listen on %s, advertised url %s]", e.Server.Name, e.Config.BindAddr, e.Server.URL()) 183 e.listener = server.NewListener(e.Config.EtcdTLSInfo().Scheme(), e.Config.BindAddr, etcdTLSConfig) 184 185 // An error string equivalent to net.errClosing for using with 186 // http.Serve() during server shutdown. Need to re-declare 187 // here because it is not exported by "net" package. 188 const errClosing = "use of closed network connection" 189 190 peerServerClosed := make(chan bool) 191 go func() { 192 // Starting peer server should be followed close by listening on its port 193 // If not, it may leave many requests unaccepted, or cannot receive heartbeat from the cluster. 194 // One severe problem caused if failing receiving heartbeats is when the second node joins one-node cluster, 195 // the cluster could be out of work as long as the two nodes cannot transfer messages. 196 e.PeerServer.Start(e.Config.Snapshot, e.Config.Discovery, e.Config.Peers) 197 198 log.Infof("peer server [name %s, listen on %s, advertised url %s]", e.PeerServer.Config.Name, e.Config.Peer.BindAddr, e.PeerServer.Config.URL) 199 e.peerListener = server.NewListener(psConfig.Scheme, e.Config.Peer.BindAddr, peerTLSConfig) 200 201 close(e.readyC) // etcd server is ready to accept connections, notify waiters. 202 203 sHTTP := &ehttp.CORSHandler{e.PeerServer.HTTPHandler(), corsInfo} 204 if err := http.Serve(e.peerListener, sHTTP); err != nil { 205 if !strings.Contains(err.Error(), errClosing) { 206 log.Fatal(err) 207 } 208 } 209 close(peerServerClosed) 210 }() 211 212 sHTTP := &ehttp.CORSHandler{e.Server.HTTPHandler(), corsInfo} 213 if err := http.Serve(e.listener, sHTTP); err != nil { 214 if !strings.Contains(err.Error(), errClosing) { 215 log.Fatal(err) 216 } 217 } 218 219 <-peerServerClosed 220 log.Infof("etcd instance is stopped [name %s]", e.Config.Name) 221 } 222 223 // Stop the etcd instance. 224 // 225 // TODO Shutdown gracefully. 226 func (e *Etcd) Stop() { 227 e.PeerServer.Stop() 228 e.peerListener.Close() 229 e.listener.Close() 230 } 231 232 // ReadyNotify returns a channel that is going to be closed 233 // when the etcd instance is ready to accept connections. 234 func (e *Etcd) ReadyNotify() <-chan bool { 235 return e.readyC 236 }