瀏覽代碼

Merge pull request #1417 from jonboulle/master

RFC: move main logic to etcd subpackage
Jonathan Boulle 11 年之前
父節點
當前提交
83ca16188c
共有 5 個文件被更改,包括 353 次插入301 次删除
  1. 19 0
      etcdmain/doc.go
  2. 320 0
      etcdmain/etcd.go
  3. 1 1
      etcdmain/etcd_test.go
  4. 11 297
      main.go
  5. 2 3
      test

+ 19 - 0
etcdmain/doc.go

@@ -0,0 +1,19 @@
+/*
+   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 etcd contains the main entry point for the etcd binary. */
+
+package etcdmain

+ 320 - 0
etcdmain/etcd.go

@@ -0,0 +1,320 @@
+/*
+   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 (
+	"flag"
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+	"strings"
+
+	"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/flags"
+	"github.com/coreos/etcd/pkg/transport"
+	"github.com/coreos/etcd/pkg/types"
+	"github.com/coreos/etcd/proxy"
+	"github.com/coreos/etcd/version"
+)
+
+const (
+	// the owner can make/remove files inside the directory
+	privateDirMode = 0700
+)
+
+var (
+	fs           = flag.NewFlagSet("etcd", flag.ContinueOnError)
+	name         = fs.String("name", "default", "Unique human-readable name for this node")
+	dir          = fs.String("data-dir", "", "Path to the data directory")
+	durl         = fs.String("discovery", "", "Discovery service used to bootstrap the cluster")
+	snapCount    = fs.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
+	printVersion = fs.Bool("version", false, "Print the version and exit")
+
+	initialCluster     = fs.String("initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
+	initialClusterName = fs.String("initial-cluster-name", "etcd", "Initial name for the etcd cluster during bootstrap")
+	clusterState       = new(etcdserver.ClusterState)
+
+	corsInfo  = &cors.CORSInfo{}
+	proxyFlag = new(flags.Proxy)
+
+	clientTLSInfo = transport.TLSInfo{}
+	peerTLSInfo   = transport.TLSInfo{}
+
+	ignored = []string{
+		"cluster-active-size",
+		"cluster-remove-delay",
+		"cluster-sync-interval",
+		"config",
+		"force",
+		"max-result-buffer",
+		"max-retry-attempts",
+		"peer-heartbeat-interval",
+		"peer-election-timeout",
+		"retry-interval",
+		"snapshot",
+		"v",
+		"vv",
+	}
+)
+
+func init() {
+	fs.Var(clusterState, "initial-cluster-state", "Initial cluster configuration for bootstrapping")
+	if err := clusterState.Set(etcdserver.ClusterStateValueNew); err != nil {
+		// Should never happen.
+		log.Panicf("unexpected error setting up clusterState: %v", err)
+	}
+
+	fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
+	fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
+	fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of URLs to listen on for peer traffic")
+	fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of URLs to listen on for client traffic")
+
+	fs.Var(corsInfo, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
+
+	fs.Var(proxyFlag, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(flags.ProxyValues, ", ")))
+	if err := proxyFlag.Set(flags.ProxyValueOff); err != nil {
+		// Should never happen.
+		log.Panicf("unexpected error setting up proxyFlag: %v", err)
+	}
+
+	fs.StringVar(&clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.")
+	fs.StringVar(&clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
+	fs.StringVar(&clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
+
+	fs.StringVar(&peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.")
+	fs.StringVar(&peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
+	fs.StringVar(&peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
+
+	// backwards-compatibility with v0.4.6
+	fs.Var(&flags.IPAddressPort{}, "addr", "DEPRECATED: Use -advertise-client-urls instead.")
+	fs.Var(&flags.IPAddressPort{}, "bind-addr", "DEPRECATED: Use -listen-client-urls instead.")
+	fs.Var(&flags.IPAddressPort{}, "peer-addr", "DEPRECATED: Use -initial-advertise-peer-urls instead.")
+	fs.Var(&flags.IPAddressPort{}, "peer-bind-addr", "DEPRECATED: Use -listen-peer-urls instead.")
+
+	for _, f := range ignored {
+		fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
+	}
+
+	fs.Var(&flags.DeprecatedFlag{Name: "peers"}, "peers", "DEPRECATED: Use -initial-cluster instead")
+	fs.Var(&flags.DeprecatedFlag{Name: "peers-file"}, "peers-file", "DEPRECATED: Use -initial-cluster instead")
+}
+
+func Main() {
+	fs.Usage = flags.UsageWithIgnoredFlagsFunc(fs, ignored)
+	err := fs.Parse(os.Args[1:])
+	switch err {
+	case nil:
+	case flag.ErrHelp:
+		os.Exit(0)
+	default:
+		os.Exit(2)
+	}
+
+	if *printVersion {
+		fmt.Println("etcd version", version.Version)
+		os.Exit(0)
+	}
+
+	flags.SetFlagsFromEnv(fs)
+
+	if string(*proxyFlag) == flags.ProxyValueOff {
+		startEtcd()
+	} else {
+		startProxy()
+	}
+
+	// Block indefinitely
+	<-make(chan struct{})
+}
+
+// startEtcd launches the etcd server and HTTP handlers for client/server communication.
+func startEtcd() {
+	cls, err := setupCluster()
+	if err != nil {
+		log.Fatalf("etcd: error setting up initial cluster: %v", err)
+	}
+
+	if *dir == "" {
+		*dir = fmt.Sprintf("%v.etcd", *name)
+		log.Printf("etcd: no data-dir provided, using default data-dir ./%s", *dir)
+	}
+	if err := os.MkdirAll(*dir, privateDirMode); err != nil {
+		log.Fatalf("etcd: cannot create data directory: %v", err)
+	}
+	if err := fileutil.IsDirWriteable(*dir); err != nil {
+		log.Fatalf("etcd: cannot write to data directory: %v", err)
+	}
+
+	pt, err := transport.NewTransport(peerTLSInfo)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	acurls, err := flags.URLsFromFlags(fs, "advertise-client-urls", "addr", clientTLSInfo)
+	if err != nil {
+		log.Fatal(err.Error())
+	}
+	cfg := &etcdserver.ServerConfig{
+		Name:         *name,
+		ClientURLs:   acurls,
+		DataDir:      *dir,
+		SnapCount:    *snapCount,
+		Cluster:      cls,
+		DiscoveryURL: *durl,
+		ClusterState: *clusterState,
+		Transport:    pt,
+	}
+	s := etcdserver.NewServer(cfg)
+	s.Start()
+
+	ch := &cors.CORSHandler{
+		Handler: etcdhttp.NewClientHandler(s),
+		Info:    corsInfo,
+	}
+	ph := etcdhttp.NewPeerHandler(s)
+
+	lpurls, err := flags.URLsFromFlags(fs, "listen-peer-urls", "peer-bind-addr", peerTLSInfo)
+	if err != nil {
+		log.Fatal(err.Error())
+	}
+
+	for _, u := range lpurls {
+		l, err := transport.NewListener(u.Host, peerTLSInfo)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		// Start the peer server in a goroutine
+		urlStr := u.String()
+		go func() {
+			log.Print("etcd: listening for peers on ", urlStr)
+			log.Fatal(http.Serve(l, ph))
+		}()
+	}
+
+	lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo)
+	if err != nil {
+		log.Fatal(err.Error())
+	}
+
+	// Start a client server goroutine for each listen address
+	for _, u := range lcurls {
+		l, err := transport.NewListener(u.Host, clientTLSInfo)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		urlStr := u.String()
+		go func() {
+			log.Print("etcd: listening for client requests on ", urlStr)
+			log.Fatal(http.Serve(l, ch))
+		}()
+	}
+}
+
+// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
+func startProxy() {
+	cls, err := setupCluster()
+	if err != nil {
+		log.Fatalf("etcd: error setting up initial cluster: %v", err)
+	}
+
+	pt, err := transport.NewTransport(clientTLSInfo)
+	if err != nil {
+		log.Fatal(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 {
+			log.Printf("etcd: %v", err)
+			return []string{}
+		}
+		return cls.ClientURLs()
+	}
+	ph := proxy.NewHandler(pt, uf)
+	ph = &cors.CORSHandler{
+		Handler: ph,
+		Info:    corsInfo,
+	}
+
+	if string(*proxyFlag) == flags.ProxyValueReadonly {
+		ph = proxy.NewReadonlyHandler(ph)
+	}
+
+	lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo)
+	if err != nil {
+		log.Fatal(err.Error())
+	}
+	// Start a proxy server goroutine for each listen address
+	for _, u := range lcurls {
+		l, err := transport.NewListener(u.Host, clientTLSInfo)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		host := u.Host
+		go func() {
+			log.Print("etcd: proxy listening for client requests on ", host)
+			log.Fatal(http.Serve(l, ph))
+		}()
+	}
+}
+
+// setupCluster sets up the cluster definition for bootstrap or discovery.
+func setupCluster() (*etcdserver.Cluster, error) {
+	set := make(map[string]bool)
+	fs.Visit(func(f *flag.Flag) {
+		set[f.Name] = true
+	})
+	if set["discovery"] && set["initial-cluster"] {
+		return nil, fmt.Errorf("both discovery and bootstrap-config are set")
+	}
+	apurls, err := flags.URLsFromFlags(fs, "initial-advertise-peer-urls", "addr", peerTLSInfo)
+	if err != nil {
+		return nil, err
+	}
+
+	var cls *etcdserver.Cluster
+	switch {
+	case set["discovery"]:
+		clusterStr := genClusterString(*name, apurls)
+		cls, err = etcdserver.NewClusterFromString(*durl, clusterStr)
+	case set["initial-cluster"]:
+		fallthrough
+	default:
+		// We're statically configured, and cluster has appropriately been set.
+		// Try to configure by indexing the static cluster by name.
+		cls, err = etcdserver.NewClusterFromString(*initialClusterName, *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, ",")
+}

+ 1 - 1
main_test.go → etcdmain/etcd_test.go

@@ -14,7 +14,7 @@
    limitations under the License.
 */
 
-package main
+package etcdmain
 
 import (
 	"testing"

+ 11 - 297
main.go

@@ -14,307 +14,21 @@
    limitations under the License.
 */
 
+// Package main is a simple wrapper of the real etcd entrypoint package
+// (located at github.com/coreos/etcd/etcdmain) to ensure that etcd is still
+// "go getable"; e.g. `go get github.com/coreos/etcd` works as expected and
+// builds a binary in $GOBIN/etcd
+//
+// This package should NOT be extended or modified in any way; to modify the
+// etcd binary, work in the `github.com/coreos/etcd/etcdmain` package.
+//
+
 package main
 
 import (
-	"flag"
-	"fmt"
-	"log"
-	"net/http"
-	"os"
-	"strings"
-
-	"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/flags"
-	"github.com/coreos/etcd/pkg/transport"
-	"github.com/coreos/etcd/pkg/types"
-	"github.com/coreos/etcd/proxy"
-	"github.com/coreos/etcd/version"
-)
-
-const (
-	// the owner can make/remove files inside the directory
-	privateDirMode = 0700
-)
-
-var (
-	fs           = flag.NewFlagSet("etcd", flag.ContinueOnError)
-	name         = fs.String("name", "default", "Unique human-readable name for this node")
-	dir          = fs.String("data-dir", "", "Path to the data directory")
-	durl         = fs.String("discovery", "", "Discovery service used to bootstrap the cluster")
-	snapCount    = fs.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
-	printVersion = fs.Bool("version", false, "Print the version and exit")
-
-	initialCluster     = fs.String("initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
-	initialClusterName = fs.String("initial-cluster-name", "etcd", "Initial name for the etcd cluster during bootstrap")
-	clusterState       = new(etcdserver.ClusterState)
-
-	corsInfo  = &cors.CORSInfo{}
-	proxyFlag = new(flags.Proxy)
-
-	clientTLSInfo = transport.TLSInfo{}
-	peerTLSInfo   = transport.TLSInfo{}
-
-	ignored = []string{
-		"cluster-active-size",
-		"cluster-remove-delay",
-		"cluster-sync-interval",
-		"config",
-		"force",
-		"max-result-buffer",
-		"max-retry-attempts",
-		"peer-heartbeat-interval",
-		"peer-election-timeout",
-		"retry-interval",
-		"snapshot",
-		"v",
-		"vv",
-	}
+	"github.com/coreos/etcd/etcdmain"
 )
 
-func init() {
-	fs.Var(clusterState, "initial-cluster-state", "Initial cluster configuration for bootstrapping")
-	if err := clusterState.Set(etcdserver.ClusterStateValueNew); err != nil {
-		// Should never happen.
-		log.Panicf("unexpected error setting up clusterState: %v", err)
-	}
-
-	fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
-	fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
-	fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of URLs to listen on for peer traffic")
-	fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of URLs to listen on for client traffic")
-
-	fs.Var(corsInfo, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
-
-	fs.Var(proxyFlag, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(flags.ProxyValues, ", ")))
-	if err := proxyFlag.Set(flags.ProxyValueOff); err != nil {
-		// Should never happen.
-		log.Panicf("unexpected error setting up proxyFlag: %v", err)
-	}
-
-	fs.StringVar(&clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.")
-	fs.StringVar(&clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
-	fs.StringVar(&clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
-
-	fs.StringVar(&peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.")
-	fs.StringVar(&peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
-	fs.StringVar(&peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
-
-	// backwards-compatibility with v0.4.6
-	fs.Var(&flags.IPAddressPort{}, "addr", "DEPRECATED: Use -advertise-client-urls instead.")
-	fs.Var(&flags.IPAddressPort{}, "bind-addr", "DEPRECATED: Use -listen-client-urls instead.")
-	fs.Var(&flags.IPAddressPort{}, "peer-addr", "DEPRECATED: Use -initial-advertise-peer-urls instead.")
-	fs.Var(&flags.IPAddressPort{}, "peer-bind-addr", "DEPRECATED: Use -listen-peer-urls instead.")
-
-	for _, f := range ignored {
-		fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
-	}
-
-	fs.Var(&flags.DeprecatedFlag{Name: "peers"}, "peers", "DEPRECATED: Use -initial-cluster instead")
-	fs.Var(&flags.DeprecatedFlag{Name: "peers-file"}, "peers-file", "DEPRECATED: Use -initial-cluster instead")
-}
-
 func main() {
-	fs.Usage = flags.UsageWithIgnoredFlagsFunc(fs, ignored)
-	err := fs.Parse(os.Args[1:])
-	switch err {
-	case nil:
-	case flag.ErrHelp:
-		os.Exit(0)
-	default:
-		os.Exit(2)
-	}
-
-	if *printVersion {
-		fmt.Println("etcd version", version.Version)
-		os.Exit(0)
-	}
-
-	flags.SetFlagsFromEnv(fs)
-
-	if string(*proxyFlag) == flags.ProxyValueOff {
-		startEtcd()
-	} else {
-		startProxy()
-	}
-
-	// Block indefinitely
-	<-make(chan struct{})
-}
-
-// startEtcd launches the etcd server and HTTP handlers for client/server communication.
-func startEtcd() {
-	cls, err := setupCluster()
-	if err != nil {
-		log.Fatalf("etcd: error setting up initial cluster: %v", err)
-	}
-
-	if *dir == "" {
-		*dir = fmt.Sprintf("%v.etcd", *name)
-		log.Printf("etcd: no data-dir provided, using default data-dir ./%s", *dir)
-	}
-	if err := os.MkdirAll(*dir, privateDirMode); err != nil {
-		log.Fatalf("etcd: cannot create data directory: %v", err)
-	}
-	if err := fileutil.IsDirWriteable(*dir); err != nil {
-		log.Fatalf("etcd: cannot write to data directory: %v", err)
-	}
-
-	pt, err := transport.NewTransport(peerTLSInfo)
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	acurls, err := flags.URLsFromFlags(fs, "advertise-client-urls", "addr", clientTLSInfo)
-	if err != nil {
-		log.Fatal(err.Error())
-	}
-	cfg := &etcdserver.ServerConfig{
-		Name:         *name,
-		ClientURLs:   acurls,
-		DataDir:      *dir,
-		SnapCount:    *snapCount,
-		Cluster:      cls,
-		DiscoveryURL: *durl,
-		ClusterState: *clusterState,
-		Transport:    pt,
-	}
-	s := etcdserver.NewServer(cfg)
-	s.Start()
-
-	ch := &cors.CORSHandler{
-		Handler: etcdhttp.NewClientHandler(s),
-		Info:    corsInfo,
-	}
-	ph := etcdhttp.NewPeerHandler(s)
-
-	lpurls, err := flags.URLsFromFlags(fs, "listen-peer-urls", "peer-bind-addr", peerTLSInfo)
-	if err != nil {
-		log.Fatal(err.Error())
-	}
-
-	for _, u := range lpurls {
-		l, err := transport.NewListener(u.Host, peerTLSInfo)
-		if err != nil {
-			log.Fatal(err)
-		}
-
-		// Start the peer server in a goroutine
-		urlStr := u.String()
-		go func() {
-			log.Print("etcd: listening for peers on ", urlStr)
-			log.Fatal(http.Serve(l, ph))
-		}()
-	}
-
-	lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo)
-	if err != nil {
-		log.Fatal(err.Error())
-	}
-
-	// Start a client server goroutine for each listen address
-	for _, u := range lcurls {
-		l, err := transport.NewListener(u.Host, clientTLSInfo)
-		if err != nil {
-			log.Fatal(err)
-		}
-
-		urlStr := u.String()
-		go func() {
-			log.Print("etcd: listening for client requests on ", urlStr)
-			log.Fatal(http.Serve(l, ch))
-		}()
-	}
-}
-
-// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
-func startProxy() {
-	cls, err := setupCluster()
-	if err != nil {
-		log.Fatalf("etcd: error setting up initial cluster: %v", err)
-	}
-
-	pt, err := transport.NewTransport(clientTLSInfo)
-	if err != nil {
-		log.Fatal(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 {
-			log.Printf("etcd: %v", err)
-			return []string{}
-		}
-		return cls.ClientURLs()
-	}
-	ph := proxy.NewHandler(pt, uf)
-	ph = &cors.CORSHandler{
-		Handler: ph,
-		Info:    corsInfo,
-	}
-
-	if string(*proxyFlag) == flags.ProxyValueReadonly {
-		ph = proxy.NewReadonlyHandler(ph)
-	}
-
-	lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo)
-	if err != nil {
-		log.Fatal(err.Error())
-	}
-	// Start a proxy server goroutine for each listen address
-	for _, u := range lcurls {
-		l, err := transport.NewListener(u.Host, clientTLSInfo)
-		if err != nil {
-			log.Fatal(err)
-		}
-
-		host := u.Host
-		go func() {
-			log.Print("etcd: proxy listening for client requests on ", host)
-			log.Fatal(http.Serve(l, ph))
-		}()
-	}
-}
-
-// setupCluster sets up the cluster definition for bootstrap or discovery.
-func setupCluster() (*etcdserver.Cluster, error) {
-	set := make(map[string]bool)
-	fs.Visit(func(f *flag.Flag) {
-		set[f.Name] = true
-	})
-	if set["discovery"] && set["initial-cluster"] {
-		return nil, fmt.Errorf("both discovery and bootstrap-config are set")
-	}
-	apurls, err := flags.URLsFromFlags(fs, "initial-advertise-peer-urls", "addr", peerTLSInfo)
-	if err != nil {
-		return nil, err
-	}
-
-	var cls *etcdserver.Cluster
-	switch {
-	case set["discovery"]:
-		clusterStr := genClusterString(*name, apurls)
-		cls, err = etcdserver.NewClusterFromString(*durl, clusterStr)
-	case set["initial-cluster"]:
-		fallthrough
-	default:
-		// We're statically configured, and cluster has appropriately been set.
-		// Try to configure by indexing the static cluster by name.
-		cls, err = etcdserver.NewClusterFromString(*initialClusterName, *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, ",")
+	etcdmain.Main()
 }

+ 2 - 3
test

@@ -15,13 +15,12 @@ COVER=${COVER:-"-cover"}
 source ./build
 
 # Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt.
-TESTABLE_AND_FORMATTABLE="client discovery etcdctl/command etcdserver etcdserver/etcdhttp etcdserver/etcdhttp/httptypes etcdserver/etcdserverpb integration pkg/flags pkg/strutil pkg/transport proxy raft snap store wait wal"
-TESTABLE="$TESTABLE_AND_FORMATTABLE ./"
+TESTABLE_AND_FORMATTABLE="client discovery etcdctl/command etcdmain etcdserver etcdserver/etcdhttp etcdserver/etcdhttp/httptypes etcdserver/etcdserverpb integration pkg/flags pkg/strutil pkg/transport proxy raft snap store wait wal"
 FORMATTABLE="$TESTABLE_AND_FORMATTABLE *.go etcdctl/"
 
 # user has not provided PKG override
 if [ -z "$PKG" ]; then
-	TEST=$TESTABLE
+	TEST=$TESTABLE_AND_FORMATTABLE
 	FMT=$FORMATTABLE
 
 # user has provided PKG override