etcd/etcdmain/etcd.go

278 lines
7.3 KiB
Go
Raw Normal View History

/*
Copyright 2014 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 (
"fmt"
"log"
"net"
"net/http"
"os"
"strings"
"time"
2014-11-05 00:09:24 +03:00
"github.com/coreos/etcd/discovery"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/etcdhttp"
"github.com/coreos/etcd/pkg/cors"
"github.com/coreos/etcd/pkg/fileutil"
"github.com/coreos/etcd/pkg/transport"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/proxy"
"github.com/coreos/etcd/rafthttp"
)
const (
// the owner can make/remove files inside the directory
privateDirMode = 0700
)
func Main() {
2014-12-20 01:47:07 +03:00
cfg := NewConfig()
err := cfg.Parse(os.Args[1:])
if err != nil {
2014-12-20 01:47:07 +03:00
log.Printf("etcd: error verifying flags, %v", err)
os.Exit(2)
}
2014-11-14 10:03:34 +03:00
var stopped <-chan struct{}
2014-12-20 01:47:07 +03:00
shouldProxy := cfg.isProxy()
2014-11-05 22:31:13 +03:00
if !shouldProxy {
2014-12-20 01:47:07 +03:00
stopped, err = startEtcd(cfg)
if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
2014-11-05 22:31:13 +03:00
log.Printf("etcd: discovery cluster full, falling back to %s", fallbackFlagProxy)
shouldProxy = true
2014-11-05 00:09:24 +03:00
}
}
2014-11-05 22:31:13 +03:00
if shouldProxy {
2014-12-20 01:47:07 +03:00
err = startProxy(cfg)
2014-11-05 22:31:13 +03:00
}
if err != nil {
switch err {
case discovery.ErrDuplicateID:
log.Fatalf("etcd: member %s has previously registered with discovery service (%s), but the data-dir (%s) on disk cannot be found.",
2014-12-20 01:47:07 +03:00
cfg.name, cfg.durl, cfg.dir)
default:
log.Fatalf("etcd: %v", err)
}
}
2014-12-20 01:47:07 +03:00
2014-11-14 10:03:34 +03:00
<-stopped
}
// 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) {
cls, err := setupCluster(cfg)
if err != nil {
2014-11-14 10:03:34 +03:00
return nil, fmt.Errorf("error setting up initial cluster: %v", err)
}
2014-12-20 01:47:07 +03:00
if cfg.dir == "" {
cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
log.Printf("no data-dir provided, using default data-dir ./%s", cfg.dir)
}
2014-12-20 01:47:07 +03:00
if err := os.MkdirAll(cfg.dir, privateDirMode); err != nil {
2014-11-14 10:03:34 +03:00
return nil, fmt.Errorf("cannot create data directory: %v", err)
}
2014-12-20 01:47:07 +03:00
if err := fileutil.IsDirWriteable(cfg.dir); err != nil {
2014-11-14 10:03:34 +03:00
return nil, fmt.Errorf("cannot write to data directory: %v", err)
}
2014-12-20 01:47:07 +03:00
pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, 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() {
log.Printf("etcd: peerTLS: %s", cfg.peerTLSInfo)
}
plns := make([]net.Listener, 0)
2014-12-20 01:47:07 +03:00
for _, u := range cfg.lpurls {
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()
log.Print("etcd: listening for peers on ", urlStr)
2014-11-05 00:09:24 +03:00
defer func() {
if err != nil {
l.Close()
log.Print("etcd: stopping listening for peers on ", urlStr)
}
}()
plns = append(plns, l)
}
2014-12-20 01:47:07 +03:00
if !cfg.clientTLSInfo.Empty() {
log.Printf("etcd: clientTLS: %s", cfg.clientTLSInfo)
}
clns := make([]net.Listener, 0)
2014-12-20 01:47:07 +03:00
for _, u := range cfg.lcurls {
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
}
urlStr := u.String()
log.Print("etcd: listening for client requests on ", urlStr)
2014-11-05 00:09:24 +03:00
defer func() {
if err != nil {
l.Close()
log.Print("etcd: stopping listening for client requests on ", urlStr)
}
}()
clns = append(clns, l)
}
2014-12-20 01:47:07 +03:00
srvcfg := &etcdserver.ServerConfig{
Name: cfg.name,
ClientURLs: cfg.acurls,
PeerURLs: cfg.apurls,
DataDir: cfg.dir,
SnapCount: cfg.snapCount,
MaxSnapFiles: cfg.maxSnapFiles,
MaxWALFiles: cfg.maxWalFiles,
Cluster: cls,
2014-12-20 01:47:07 +03:00
DiscoveryURL: cfg.durl,
DiscoveryProxy: cfg.dproxy,
NewCluster: cfg.isNewCluster(),
ForceNewCluster: cfg.forceNewCluster,
Transport: pt,
}
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()
2014-12-20 01:47:07 +03:00
if cfg.corsInfo.String() != "" {
log.Printf("etcd: cors = %s", cfg.corsInfo)
}
ch := &cors.CORSHandler{
Handler: etcdhttp.NewClientHandler(s),
2014-12-20 01:47:07 +03:00
Info: cfg.corsInfo,
}
ph := etcdhttp.NewPeerHandler(s)
// Start the peer server in a goroutine
for _, l := range plns {
go func(l net.Listener) {
log.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) {
log.Fatal(serveHTTP(l, ch, 30*time.Second))
}(l)
}
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 {
cls, err := setupCluster(cfg)
if err != nil {
2014-11-05 00:09:24 +03:00
return fmt.Errorf("error setting up initial cluster: %v", err)
}
2014-12-20 01:47:07 +03:00
if cfg.durl != "" {
s, err := discovery.GetCluster(cfg.durl, cfg.dproxy)
2014-11-05 00:09:24 +03:00
if err != nil {
return err
}
2014-12-20 01:47:07 +03:00
if cls, err = etcdserver.NewClusterFromString(cfg.durl, s); err != nil {
2014-11-05 00:09:24 +03:00
return err
}
}
2014-12-20 01:47:07 +03:00
pt, err := transport.NewTransport(cfg.clientTLSInfo)
if err != nil {
2014-11-05 00:09:24 +03:00
return err
}
// TODO(jonboulle): update peerURLs dynamically (i.e. when updating
// clientURLs) instead of just using the initial fixed list here
peerURLs := cls.PeerURLs()
uf := func() []string {
cls, err := etcdserver.GetClusterFromPeers(peerURLs)
if err != nil {
2014-11-05 00:09:24 +03:00
log.Printf("proxy: %v", err)
return []string{}
}
return cls.ClientURLs()
}
ph := proxy.NewHandler(pt, uf)
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.Host
go func() {
2014-11-05 00:09:24 +03:00
log.Print("proxy: listening for client requests on ", host)
log.Fatal(http.Serve(l, ph))
}()
}
2014-11-05 00:09:24 +03:00
return nil
}
// setupCluster sets up an initial cluster definition for bootstrap or discovery.
2014-12-20 01:47:07 +03:00
func setupCluster(cfg *config) (*etcdserver.Cluster, error) {
var cls *etcdserver.Cluster
var err error
switch {
2014-12-20 01:47:07 +03:00
case cfg.durl != "":
// If using discovery, generate a temporary cluster based on
// self's advertised peer URLs
2014-12-20 01:47:07 +03:00
clusterStr := genClusterString(cfg.name, cfg.apurls)
cls, err = etcdserver.NewClusterFromString(cfg.durl, clusterStr)
case cfg.dnsCluster != "":
2014-12-25 08:35:11 +03:00
clusterStr, clusterToken, 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
}
cls, err = etcdserver.NewClusterFromString(clusterToken, clusterStr)
default:
// We're statically configured, and cluster has appropriately been set.
2014-12-20 01:47:07 +03:00
cls, err = etcdserver.NewClusterFromString(cfg.initialClusterToken, cfg.initialCluster)
}
return cls, err
}
func genClusterString(name string, urls types.URLs) string {
addrs := make([]string, 0)
for _, u := range urls {
addrs = append(addrs, fmt.Sprintf("%v=%v", name, u.String()))
}
return strings.Join(addrs, ",")
}