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  }