etcd/etcdmain/etcd.go

520 lines
16 KiB
Go
Raw Normal View History

// Copyright 2015 CoreOS, Inc.
//
// 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.
package etcdmain
import (
2015-01-31 02:18:26 +03:00
"encoding/json"
"fmt"
2015-01-31 02:18:26 +03:00
"io/ioutil"
"net"
"net/http"
"os"
2015-01-31 02:18:26 +03:00
"path"
"reflect"
"runtime"
"time"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-systemd/daemon"
systemdutil "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-systemd/util"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
2015-08-21 06:02:35 +03:00
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/netutil"
2015-08-08 15:58:29 +03:00
"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
2014-11-05 00:09:24 +03:00
"github.com/coreos/etcd/discovery"
"github.com/coreos/etcd/etcdserver"
2015-08-08 15:58:29 +03:00
"github.com/coreos/etcd/etcdserver/api/v3rpc"
"github.com/coreos/etcd/etcdserver/etcdhttp"
2015-08-08 15:58:29 +03:00
"github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/pkg/cors"
2015-03-18 00:43:20 +03:00
"github.com/coreos/etcd/pkg/fileutil"
"github.com/coreos/etcd/pkg/osutil"
etcdmain: close client conns when it exceeds limit This solves the problem that etcd may fatal because its critical path cannot get file descriptor resource when the number of clients is too big. The PR lets the client listener close client connections immediately after they are accepted when the file descriptor usage in the process reaches some pre-set limit, so it ensures that the internal critical path could always get file descriptor when it needs. When there are tons to clients connecting to the server, the original behavior is like this: ``` 2015/08/4 16:42:08 etcdserver: cannot monitor file descriptor usage (open /proc/self/fd: too many open files) 2015/08/4 16:42:33 etcdserver: failed to purge snap file open default2.etcd/member/snap: too many open files [halted] ``` Current behavior is like this: ``` 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:26 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:27 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 etcdserver: 80% of the file descriptor limit is used [used = 873, limit = 1024] ``` It is available at linux system today because pkg/runtime only has linux support.
2015-08-04 21:36:24 +03:00
runtimeutil "github.com/coreos/etcd/pkg/runtime"
"github.com/coreos/etcd/pkg/transport"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/proxy"
"github.com/coreos/etcd/rafthttp"
"github.com/coreos/etcd/version"
)
2015-03-18 00:43:20 +03:00
type dirType string
var plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdmain")
const (
// the owner can make/remove files inside the directory
privateDirMode = 0700
etcdmain: close client conns when it exceeds limit This solves the problem that etcd may fatal because its critical path cannot get file descriptor resource when the number of clients is too big. The PR lets the client listener close client connections immediately after they are accepted when the file descriptor usage in the process reaches some pre-set limit, so it ensures that the internal critical path could always get file descriptor when it needs. When there are tons to clients connecting to the server, the original behavior is like this: ``` 2015/08/4 16:42:08 etcdserver: cannot monitor file descriptor usage (open /proc/self/fd: too many open files) 2015/08/4 16:42:33 etcdserver: failed to purge snap file open default2.etcd/member/snap: too many open files [halted] ``` Current behavior is like this: ``` 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:26 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:27 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 etcdserver: 80% of the file descriptor limit is used [used = 873, limit = 1024] ``` It is available at linux system today because pkg/runtime only has linux support.
2015-08-04 21:36:24 +03:00
// internal fd usage includes disk usage and transport usage.
// To read/write snapshot, snap pkg needs 1. In normal case, wal pkg needs
// at most 2 to read/lock/write WALs. One case that it needs to 2 is to
// read all logs after some snapshot index, which locates at the end of
// the second last and the head of the last. For purging, it needs to read
// directory, so it needs 1. For fd monitor, it needs 1.
// For transport, rafthttp builds two long-polling connections and at most
// four temporary connections with each member. There are at most 9 members
// in a cluster, so it should reserve 96.
// For the safety, we set the total reserved number to 150.
reservedInternalFDNum = 150
)
2015-03-18 00:43:20 +03:00
var (
dirMember = dirType("member")
dirProxy = dirType("proxy")
dirEmpty = dirType("empty")
)
func Main() {
2014-12-20 01:47:07 +03:00
cfg := NewConfig()
err := cfg.Parse(os.Args[1:])
if err != nil {
plog.Errorf("error verifying flags, %v. See 'etcd --help'.", err)
switch err {
case errUnsetAdvertiseClientURLsFlag:
plog.Errorf("When listening on specific address(es), this etcd process must advertise accessible url(s) to each connected client.")
}
os.Exit(1)
}
setupLogging(cfg)
2014-11-14 10:03:34 +03:00
var stopped <-chan struct{}
2014-12-20 01:47:07 +03:00
plog.Infof("etcd Version: %s\n", version.Version)
plog.Infof("Git SHA: %s\n", version.GitSHA)
plog.Infof("Go Version: %s\n", runtime.Version())
plog.Infof("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
GoMaxProcs := runtime.GOMAXPROCS(0)
plog.Infof("setting maximum number of CPUs to %d, total number of available CPUs is %d", GoMaxProcs, runtime.NumCPU())
// TODO: check whether fields are set instead of whether fields have default value
if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
cfg.initialCluster = initialClusterFromName(cfg.name)
}
2015-03-18 00:43:20 +03:00
if cfg.dir == "" {
cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.dir)
2015-03-18 00:43:20 +03:00
}
which := identifyDataDirOrDie(cfg.dir)
if which != dirEmpty {
plog.Noticef("the server is already initialized as %v before, starting as etcd %v...", which, which)
switch which {
case dirMember:
stopped, err = startEtcd(cfg)
case dirProxy:
err = startProxy(cfg)
default:
plog.Panicf("unhandled dir type %v", which)
}
} else {
shouldProxy := cfg.isProxy()
if !shouldProxy {
stopped, err = startEtcd(cfg)
if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
plog.Noticef("discovery cluster full, falling back to %s", fallbackFlagProxy)
shouldProxy = true
}
}
if shouldProxy {
err = startProxy(cfg)
2014-11-05 00:09:24 +03:00
}
}
2014-11-05 22:31:13 +03:00
if err != nil {
switch err {
case discovery.ErrDuplicateID:
plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.name, cfg.durl)
plog.Errorf("But etcd could not find valid cluster configuration in the given data dir (%s).", cfg.dir)
plog.Infof("Please check the given data dir path if the previous bootstrap succeeded")
plog.Infof("or use a new discovery token if the previous bootstrap failed.")
os.Exit(1)
case discovery.ErrDuplicateName:
plog.Errorf("member with duplicated name has registered with discovery service token(%s).", cfg.durl)
plog.Errorf("please check (cURL) the discovery token for more information.")
plog.Errorf("please do not reuse the discovery token and generate a new one to bootstrap the cluster.")
default:
plog.Fatalf("%v", err)
}
}
2014-12-20 01:47:07 +03:00
osutil.HandleInterrupts()
if systemdutil.IsRunningSystemd() {
// At this point, the initialization of etcd is done.
// The listeners are listening on the TCP ports and ready
// for accepting connections.
// The http server is probably ready for serving incoming
// connections. If it is not, the connection might be pending
// for less than one second.
err := daemon.SdNotify("READY=1")
if err != nil {
plog.Errorf("failed to notify systemd for readiness")
}
}
2014-11-14 10:03:34 +03:00
<-stopped
osutil.Exit(0)
}
// startEtcd launches the etcd server and HTTP handlers for client/server communication.
2014-12-20 01:47:07 +03:00
func startEtcd(cfg *config) (<-chan struct{}, error) {
urlsmap, token, err := getPeerURLsMapAndToken(cfg, "etcd")
if err != nil {
2014-11-14 10:03:34 +03:00
return nil, fmt.Errorf("error setting up initial cluster: %v", err)
}
pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, peerDialTimeout(cfg.ElectionMs), rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
if err != nil {
2014-11-14 10:03:34 +03:00
return nil, err
}
2014-12-20 01:47:07 +03:00
if !cfg.peerTLSInfo.Empty() {
plog.Infof("peerTLS: %s", cfg.peerTLSInfo)
}
plns := make([]net.Listener, 0)
2014-12-20 01:47:07 +03:00
for _, u := range cfg.lpurls {
if u.Scheme == "http" && !cfg.peerTLSInfo.Empty() {
plog.Warningf("The scheme of peer url %s is http while peer key/cert files are presented. Ignored peer key/cert files.", u.String())
}
2014-11-05 00:09:24 +03:00
var l net.Listener
2014-12-20 01:47:07 +03:00
l, err = transport.NewTimeoutListener(u.Host, u.Scheme, cfg.peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
if err != nil {
2014-11-14 10:03:34 +03:00
return nil, err
}
urlStr := u.String()
plog.Info("listening for peers on ", urlStr)
2014-11-05 00:09:24 +03:00
defer func() {
if err != nil {
l.Close()
plog.Info("stopping listening for peers on ", urlStr)
2014-11-05 00:09:24 +03:00
}
}()
plns = append(plns, l)
}
2014-12-20 01:47:07 +03:00
if !cfg.clientTLSInfo.Empty() {
plog.Infof("clientTLS: %s", cfg.clientTLSInfo)
}
clns := make([]net.Listener, 0)
2014-12-20 01:47:07 +03:00
for _, u := range cfg.lcurls {
if u.Scheme == "http" && !cfg.clientTLSInfo.Empty() {
plog.Warningf("The scheme of client url %s is http while client key/cert files are presented. Ignored client key/cert files.", u.String())
}
2014-11-05 00:09:24 +03:00
var l net.Listener
l, err = transport.NewKeepAliveListener(u.Host, u.Scheme, cfg.clientTLSInfo)
if err != nil {
2014-11-14 10:03:34 +03:00
return nil, err
}
etcdmain: close client conns when it exceeds limit This solves the problem that etcd may fatal because its critical path cannot get file descriptor resource when the number of clients is too big. The PR lets the client listener close client connections immediately after they are accepted when the file descriptor usage in the process reaches some pre-set limit, so it ensures that the internal critical path could always get file descriptor when it needs. When there are tons to clients connecting to the server, the original behavior is like this: ``` 2015/08/4 16:42:08 etcdserver: cannot monitor file descriptor usage (open /proc/self/fd: too many open files) 2015/08/4 16:42:33 etcdserver: failed to purge snap file open default2.etcd/member/snap: too many open files [halted] ``` Current behavior is like this: ``` 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:26 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:27 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 etcdserver: 80% of the file descriptor limit is used [used = 873, limit = 1024] ``` It is available at linux system today because pkg/runtime only has linux support.
2015-08-04 21:36:24 +03:00
if fdLimit, err := runtimeutil.FDLimit(); err == nil {
if fdLimit <= reservedInternalFDNum {
plog.Fatalf("file descriptor limit[%d] of etcd process is too low, and should be set higher than %d to ensure internal usage", fdLimit, reservedInternalFDNum)
}
2015-08-21 06:02:35 +03:00
l = netutil.LimitListener(l, int(fdLimit-reservedInternalFDNum))
etcdmain: close client conns when it exceeds limit This solves the problem that etcd may fatal because its critical path cannot get file descriptor resource when the number of clients is too big. The PR lets the client listener close client connections immediately after they are accepted when the file descriptor usage in the process reaches some pre-set limit, so it ensures that the internal critical path could always get file descriptor when it needs. When there are tons to clients connecting to the server, the original behavior is like this: ``` 2015/08/4 16:42:08 etcdserver: cannot monitor file descriptor usage (open /proc/self/fd: too many open files) 2015/08/4 16:42:33 etcdserver: failed to purge snap file open default2.etcd/member/snap: too many open files [halted] ``` Current behavior is like this: ``` 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:25 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:26 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:27 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 transport: accept error: closing connection, exceed file descriptor usage limitation (fd limit=874) 2015/08/6 19:05:28 etcdserver: 80% of the file descriptor limit is used [used = 873, limit = 1024] ``` It is available at linux system today because pkg/runtime only has linux support.
2015-08-04 21:36:24 +03:00
}
urlStr := u.String()
plog.Info("listening for client requests on ", urlStr)
2014-11-05 00:09:24 +03:00
defer func() {
if err != nil {
l.Close()
plog.Info("stopping listening for client requests on ", urlStr)
2014-11-05 00:09:24 +03:00
}
}()
clns = append(clns, l)
}
2015-08-08 15:58:29 +03:00
var v3l net.Listener
if cfg.v3demo {
2015-09-13 08:52:51 +03:00
v3l, err = net.Listen("tcp", cfg.gRPCAddr)
2015-08-08 15:58:29 +03:00
if err != nil {
plog.Fatal(err)
}
2015-09-13 08:52:51 +03:00
plog.Infof("listening for client rpc on %s", cfg.gRPCAddr)
2015-08-08 15:58:29 +03:00
}
2014-12-20 01:47:07 +03:00
srvcfg := &etcdserver.ServerConfig{
Name: cfg.name,
ClientURLs: cfg.acurls,
PeerURLs: cfg.apurls,
DataDir: cfg.dir,
2015-09-01 01:01:18 +03:00
DedicatedWALDir: cfg.walDir,
SnapCount: cfg.snapCount,
MaxSnapFiles: cfg.maxSnapFiles,
MaxWALFiles: cfg.maxWalFiles,
InitialPeerURLsMap: urlsmap,
InitialClusterToken: token,
DiscoveryURL: cfg.durl,
DiscoveryProxy: cfg.dproxy,
NewCluster: cfg.isNewCluster(),
ForceNewCluster: cfg.forceNewCluster,
Transport: pt,
TickMs: cfg.TickMs,
ElectionTicks: cfg.electionTicks(),
2015-08-08 15:58:29 +03:00
V3demo: cfg.v3demo,
2015-09-10 09:45:34 +03:00
StrictReconfigCheck: cfg.strictReconfigCheck,
}
2014-11-05 00:09:24 +03:00
var s *etcdserver.EtcdServer
2014-12-20 01:47:07 +03:00
s, err = etcdserver.NewServer(srvcfg)
2014-11-05 00:09:24 +03:00
if err != nil {
2014-11-14 10:03:34 +03:00
return nil, err
2014-11-05 00:09:24 +03:00
}
s.Start()
osutil.RegisterInterruptHandler(s.Stop)
2014-12-20 01:47:07 +03:00
if cfg.corsInfo.String() != "" {
plog.Infof("cors = %s", cfg.corsInfo)
}
ch := &cors.CORSHandler{
Handler: etcdhttp.NewClientHandler(s, srvcfg.ReqTimeout()),
2014-12-20 01:47:07 +03:00
Info: cfg.corsInfo,
}
ph := etcdhttp.NewPeerHandler(s.Cluster(), s.RaftHandler())
// Start the peer server in a goroutine
for _, l := range plns {
go func(l net.Listener) {
plog.Fatal(serveHTTP(l, ph, 5*time.Minute))
}(l)
}
// Start a client server goroutine for each listen address
for _, l := range clns {
go func(l net.Listener) {
// read timeout does not work with http close notify
// TODO: https://github.com/golang/go/issues/9524
plog.Fatal(serveHTTP(l, ch, 0))
}(l)
}
2015-08-08 15:58:29 +03:00
if cfg.v3demo {
// set up v3 demo rpc
grpcServer := grpc.NewServer()
etcdserverpb.RegisterEtcdServer(grpcServer, v3rpc.New(s))
go plog.Fatal(grpcServer.Serve(v3l))
}
2014-11-14 10:03:34 +03:00
return s.StopNotify(), nil
}
// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
2014-12-20 01:47:07 +03:00
func startProxy(cfg *config) error {
urlsmap, _, err := getPeerURLsMapAndToken(cfg, "proxy")
if err != nil {
2014-11-05 00:09:24 +03:00
return fmt.Errorf("error setting up initial cluster: %v", err)
}
pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, time.Duration(cfg.proxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyWriteTimeoutMs)*time.Millisecond)
if err != nil {
2014-11-05 00:09:24 +03:00
return err
}
pt.MaxIdleConnsPerHost = proxy.DefaultMaxIdleConnsPerHost
tr, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, time.Duration(cfg.proxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.proxyWriteTimeoutMs)*time.Millisecond)
if err != nil {
return err
}
cfg.dir = path.Join(cfg.dir, "proxy")
2015-01-31 02:18:26 +03:00
err = os.MkdirAll(cfg.dir, 0700)
if err != nil {
return err
}
var peerURLs []string
clusterfile := path.Join(cfg.dir, "cluster")
b, err := ioutil.ReadFile(clusterfile)
switch {
case err == nil:
if cfg.durl != "" {
2015-08-07 01:53:24 +03:00
plog.Warningf("discovery token ignored since the proxy has already been initialized. Valid cluster file found at %q", clusterfile)
}
2015-01-31 02:18:26 +03:00
urls := struct{ PeerURLs []string }{}
err := json.Unmarshal(b, &urls)
if err != nil {
return err
}
peerURLs = urls.PeerURLs
2015-08-07 01:53:24 +03:00
plog.Infof("proxy: using peer urls %v from cluster file %q", peerURLs, clusterfile)
2015-01-31 02:18:26 +03:00
case os.IsNotExist(err):
if cfg.durl != "" {
s, err := discovery.GetCluster(cfg.durl, cfg.dproxy)
if err != nil {
return err
}
if urlsmap, err = types.NewURLsMap(s); err != nil {
return err
}
}
peerURLs = urlsmap.URLs()
plog.Infof("proxy: using peer urls %v ", peerURLs)
2015-01-31 02:18:26 +03:00
default:
return err
}
clientURLs := []string{}
uf := func() []string {
gcls, err := etcdserver.GetClusterFromRemotePeers(peerURLs, tr)
// TODO: remove the 2nd check when we fix GetClusterFromPeers
// GetClusterFromPeers should not return nil error with an invalid empty cluster
if err != nil {
plog.Warningf("proxy: %v", err)
return []string{}
}
if len(gcls.Members()) == 0 {
return clientURLs
}
clientURLs = gcls.ClientURLs()
2015-01-31 02:18:26 +03:00
urls := struct{ PeerURLs []string }{gcls.PeerURLs()}
2015-01-31 02:18:26 +03:00
b, err := json.Marshal(urls)
if err != nil {
plog.Warningf("proxy: error on marshal peer urls %s", err)
return clientURLs
2015-01-31 02:18:26 +03:00
}
err = ioutil.WriteFile(clusterfile+".bak", b, 0600)
if err != nil {
plog.Warningf("proxy: error on writing urls %s", err)
return clientURLs
2015-01-31 02:18:26 +03:00
}
err = os.Rename(clusterfile+".bak", clusterfile)
if err != nil {
plog.Warningf("proxy: error on updating clusterfile %s", err)
return clientURLs
2015-01-31 02:18:26 +03:00
}
if !reflect.DeepEqual(gcls.PeerURLs(), peerURLs) {
plog.Noticef("proxy: updated peer urls in cluster file from %v to %v", peerURLs, gcls.PeerURLs())
2015-01-31 02:18:26 +03:00
}
peerURLs = gcls.PeerURLs()
2015-01-31 02:18:26 +03:00
return clientURLs
}
ph := proxy.NewHandler(pt, uf, time.Duration(cfg.proxyFailureWaitMs)*time.Millisecond, time.Duration(cfg.proxyRefreshIntervalMs)*time.Millisecond)
ph = &cors.CORSHandler{
Handler: ph,
2014-12-20 01:47:07 +03:00
Info: cfg.corsInfo,
}
2014-12-20 01:47:07 +03:00
if cfg.isReadonlyProxy() {
ph = proxy.NewReadonlyHandler(ph)
}
// Start a proxy server goroutine for each listen address
2014-12-20 01:47:07 +03:00
for _, u := range cfg.lcurls {
l, err := transport.NewListener(u.Host, u.Scheme, cfg.clientTLSInfo)
if err != nil {
2014-11-05 00:09:24 +03:00
return err
}
host := u.String()
go func() {
plog.Info("proxy: listening for client requests on ", host)
2015-06-17 16:32:13 +03:00
mux := http.NewServeMux()
mux.Handle("/metrics", prometheus.Handler())
mux.Handle("/", ph)
plog.Fatal(http.Serve(l, mux))
}()
}
2014-11-05 00:09:24 +03:00
return nil
}
// getPeerURLsMapAndToken sets up an initial peer URLsMap and cluster token for bootstrap or discovery.
func getPeerURLsMapAndToken(cfg *config, which string) (urlsmap types.URLsMap, token string, err error) {
switch {
2014-12-20 01:47:07 +03:00
case cfg.durl != "":
urlsmap = types.URLsMap{}
// If using discovery, generate a temporary cluster based on
// self's advertised peer URLs
urlsmap[cfg.name] = cfg.apurls
token = cfg.durl
2014-12-20 01:47:07 +03:00
case cfg.dnsCluster != "":
var clusterStr string
clusterStr, token, err = discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
2014-12-16 03:26:42 +03:00
if err != nil {
return nil, "", err
2014-12-16 03:26:42 +03:00
}
urlsmap, err = types.NewURLsMap(clusterStr)
// only etcd member must belong to the discovered cluster.
// proxy does not need to belong to the discovered cluster.
if which == "etcd" {
if _, ok := urlsmap[cfg.name]; !ok {
return nil, "", fmt.Errorf("cannot find local etcd member %q in SRV records", cfg.name)
}
}
default:
// We're statically configured, and cluster has appropriately been set.
urlsmap, err = types.NewURLsMap(cfg.initialCluster)
token = cfg.initialClusterToken
}
return urlsmap, token, err
}
2015-03-18 00:43:20 +03:00
// identifyDataDirOrDie returns the type of the data dir.
// Dies if the datadir is invalid.
func identifyDataDirOrDie(dir string) dirType {
names, err := fileutil.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return dirEmpty
}
plog.Fatalf("error listing data dir: %s", dir)
2015-03-18 00:43:20 +03:00
}
var m, p bool
for _, name := range names {
switch dirType(name) {
case dirMember:
m = true
case dirProxy:
p = true
default:
plog.Warningf("found invalid file/dir %s under data dir %s (Ignore this if you are upgrading etcd)", name, dir)
2015-03-18 00:43:20 +03:00
}
}
if m && p {
plog.Fatal("invalid datadir. Both member and proxy directories exist.")
2015-03-18 00:43:20 +03:00
}
if m {
return dirMember
}
if p {
return dirProxy
}
return dirEmpty
}
func setupLogging(cfg *config) {
capnslog.SetGlobalLogLevel(capnslog.INFO)
if cfg.debug {
capnslog.SetGlobalLogLevel(capnslog.DEBUG)
}
if cfg.logPkgLevels != "" {
repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
settings, err := repoLog.ParseLogLevelConfig(cfg.logPkgLevels)
if err != nil {
plog.Warningf("couldn't parse log level string: %s, continuing with default levels", err.Error())
return
}
repoLog.SetLogLevel(settings)
}
}
func peerDialTimeout(electionMs uint) time.Duration {
// 1s for queue wait and system delay
// + one RTT, which is smaller than 1/5 election timeout
return time.Second + time.Duration(electionMs)*time.Millisecond/5
}