func (cfg *config) configFromCmdLine() error { err := flags.SetFlagsFromEnv("ETCD", cfg.FlagSet) if err != nil { plog.Fatalf("%v", err) } flags.SetBindAddrFromAddr(cfg.FlagSet, "peer-bind-addr", "peer-addr") flags.SetBindAddrFromAddr(cfg.FlagSet, "bind-addr", "addr") cfg.lpurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-peer-urls", "peer-bind-addr", cfg.peerTLSInfo) if err != nil { return err } cfg.apurls, err = flags.URLsFromFlags(cfg.FlagSet, "initial-advertise-peer-urls", "peer-addr", cfg.peerTLSInfo) if err != nil { return err } cfg.lcurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-client-urls", "bind-addr", cfg.clientTLSInfo) if err != nil { return err } cfg.acurls, err = flags.URLsFromFlags(cfg.FlagSet, "advertise-client-urls", "addr", cfg.clientTLSInfo) if err != nil { return err } return cfg.validateConfig(func(field string) bool { return flags.IsSet(cfg.FlagSet, field) }) }
func (cfg *config) Parse(arguments []string) error { perr := cfg.FlagSet.Parse(arguments) switch perr { case nil: case flag.ErrHelp: os.Exit(0) default: os.Exit(2) } if cfg.printVersion { fmt.Println("etcd version", version.Version) os.Exit(0) } err := flags.SetFlagsFromEnv(cfg.FlagSet) if err != nil { log.Fatalf("etcd: %v", err) } set := make(map[string]bool) cfg.FlagSet.Visit(func(f *flag.Flag) { set[f.Name] = true }) nSet := 0 for _, v := range []bool{set["discovery"], set["initial-cluster"], set["discovery-srv"]} { if v { nSet += 1 } } if nSet > 1 { return ErrConflictBootstrapFlags } cfg.lpurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-peer-urls", "peer-bind-addr", cfg.peerTLSInfo) if err != nil { return err } cfg.apurls, err = flags.URLsFromFlags(cfg.FlagSet, "initial-advertise-peer-urls", "peer-addr", cfg.peerTLSInfo) if err != nil { return err } cfg.lcurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-client-urls", "bind-addr", cfg.clientTLSInfo) if err != nil { return err } cfg.acurls, err = flags.URLsFromFlags(cfg.FlagSet, "advertise-client-urls", "addr", cfg.clientTLSInfo) if err != nil { return err } if err := cfg.resolveUrls(); err != nil { return errors.New("cannot resolve DNS hostnames.") } return nil }
// 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 }
// 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)) }() } }
func (cfg *config) Parse(arguments []string) error { perr := cfg.FlagSet.Parse(arguments) switch perr { case nil: case flag.ErrHelp: fmt.Println(flagsline) os.Exit(0) default: os.Exit(2) } if len(cfg.FlagSet.Args()) != 0 { return fmt.Errorf("'%s' is not a valid flag", cfg.FlagSet.Arg(0)) } if cfg.printVersion { fmt.Printf("etcd Version: %s\n", version.Version) fmt.Printf("Git SHA: %s\n", version.GitSHA) fmt.Printf("Go Version: %s\n", runtime.Version()) fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) os.Exit(0) } err := flags.SetFlagsFromEnv("ETCD", cfg.FlagSet) if err != nil { plog.Fatalf("%v", err) } set := make(map[string]bool) cfg.FlagSet.Visit(func(f *flag.Flag) { set[f.Name] = true }) nSet := 0 for _, v := range []bool{set["discovery"], set["initial-cluster"], set["discovery-srv"]} { if v { nSet += 1 } } if nSet > 1 { return ErrConflictBootstrapFlags } flags.SetBindAddrFromAddr(cfg.FlagSet, "peer-bind-addr", "peer-addr") flags.SetBindAddrFromAddr(cfg.FlagSet, "bind-addr", "addr") cfg.lpurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-peer-urls", "peer-bind-addr", cfg.peerTLSInfo) if err != nil { return err } cfg.apurls, err = flags.URLsFromFlags(cfg.FlagSet, "initial-advertise-peer-urls", "peer-addr", cfg.peerTLSInfo) if err != nil { return err } cfg.lcurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-client-urls", "bind-addr", cfg.clientTLSInfo) if err != nil { return err } cfg.acurls, err = flags.URLsFromFlags(cfg.FlagSet, "advertise-client-urls", "addr", cfg.clientTLSInfo) if err != nil { return err } // when etcd runs in member mode user needs to set --advertise-client-urls if --listen-client-urls is set. // TODO(yichengq): check this for joining through discovery service case mayFallbackToProxy := flags.IsSet(cfg.FlagSet, "discovery") && cfg.fallback.String() == fallbackFlagProxy mayBeProxy := cfg.proxy.String() != proxyFlagOff || mayFallbackToProxy if !mayBeProxy { if flags.IsSet(cfg.FlagSet, "listen-client-urls") && !flags.IsSet(cfg.FlagSet, "advertise-client-urls") { return errUnsetAdvertiseClientURLsFlag } } if 5*cfg.TickMs > cfg.ElectionMs { return fmt.Errorf("--election-timeout[%vms] should be at least as 5 times as --heartbeat-interval[%vms]", cfg.ElectionMs, cfg.TickMs) } if cfg.ElectionMs > maxElectionMs { return fmt.Errorf("--election-timeout[%vms] is too long, and should be set less than %vms", cfg.ElectionMs, maxElectionMs) } return nil }
// 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() error { apurls, err := flags.URLsFromFlags(fs, "initial-advertise-peer-urls", "addr", peerTLSInfo) if err != nil { return err } cls, err := setupCluster(apurls) if err != nil { return fmt.Errorf("error setting up initial cluster: %v", err) } if *durl != "" { s, err := discovery.GetCluster(*durl, *dproxy) if err != nil { return err } if cls, err = etcdserver.NewClusterFromString(*durl, s); err != nil { return err } } pt, err := transport.NewTransport(clientTLSInfo) if err != nil { 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 { log.Printf("proxy: %v", err) return []string{} } return cls.ClientURLs() } ph := proxy.NewHandler(pt, uf) ph = &cors.CORSHandler{ Handler: ph, Info: corsInfo, } if proxyFlag.String() == proxyFlagReadonly { ph = proxy.NewReadonlyHandler(ph) } lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo) if err != nil { return err } // Start a proxy server goroutine for each listen address for _, u := range lcurls { l, err := transport.NewListener(u.Host, u.Scheme, clientTLSInfo) if err != nil { return err } host := u.Host go func() { log.Print("proxy: listening for client requests on ", host) log.Fatal(http.Serve(l, ph)) }() } return nil }
// startEtcd launches the etcd server and HTTP handlers for client/server communication. func startEtcd() (<-chan struct{}, error) { apurls, err := flags.URLsFromFlags(fs, "initial-advertise-peer-urls", "addr", peerTLSInfo) if err != nil { return nil, err } cls, err := setupCluster(apurls) if err != nil { return nil, fmt.Errorf("error setting up initial cluster: %v", err) } if *dir == "" { *dir = fmt.Sprintf("%v.etcd", *name) log.Printf("no data-dir provided, using default data-dir ./%s", *dir) } if err := os.MkdirAll(*dir, privateDirMode); err != nil { return nil, fmt.Errorf("cannot create data directory: %v", err) } if err := fileutil.IsDirWriteable(*dir); err != nil { return nil, fmt.Errorf("cannot write to data directory: %v", err) } pt, err := transport.NewTimeoutTransport(peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout) if err != nil { return nil, err } acurls, err := flags.URLsFromFlags(fs, "advertise-client-urls", "addr", clientTLSInfo) if err != nil { return nil, err } lpurls, err := flags.URLsFromFlags(fs, "listen-peer-urls", "peer-bind-addr", peerTLSInfo) if err != nil { return nil, err } if !peerTLSInfo.Empty() { log.Printf("etcd: peerTLS: %s", peerTLSInfo) } plns := make([]net.Listener, 0) for _, u := range lpurls { var l net.Listener l, err = transport.NewTimeoutListener(u.Host, u.Scheme, peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout) if err != nil { return nil, err } urlStr := u.String() log.Print("etcd: listening for peers on ", urlStr) defer func() { if err != nil { l.Close() log.Print("etcd: stopping listening for peers on ", urlStr) } }() plns = append(plns, l) } lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo) if err != nil { return nil, err } if !clientTLSInfo.Empty() { log.Printf("etcd: clientTLS: %s", clientTLSInfo) } clns := make([]net.Listener, 0) for _, u := range lcurls { var l net.Listener l, err = transport.NewListener(u.Host, u.Scheme, clientTLSInfo) if err != nil { return nil, err } urlStr := u.String() log.Print("etcd: listening for client requests on ", urlStr) defer func() { if err != nil { l.Close() log.Print("etcd: stopping listening for client requests on ", urlStr) } }() clns = append(clns, l) } cfg := &etcdserver.ServerConfig{ Name: *name, ClientURLs: acurls, PeerURLs: apurls, DataDir: *dir, SnapCount: *snapCount, MaxSnapFiles: *maxSnapFiles, MaxWALFiles: *maxWalFiles, Cluster: cls, DiscoveryURL: *durl, DiscoveryProxy: *dproxy, NewCluster: clusterStateFlag.String() == clusterStateFlagNew, ForceNewCluster: *forceNewCluster, Transport: pt, } var s *etcdserver.EtcdServer s, err = etcdserver.NewServer(cfg) if err != nil { return nil, err } s.Start() if corsInfo.String() != "" { log.Printf("etcd: cors = %s", corsInfo) } ch := &cors.CORSHandler{ Handler: etcdhttp.NewClientHandler(s), Info: corsInfo, } ph := etcdhttp.NewPeerHandler(s) // Start the peer server in a goroutine for _, l := range plns { go func(l net.Listener) { log.Fatal(http.Serve(l, ph)) }(l) } // Start a client server goroutine for each listen address for _, l := range clns { go func(l net.Listener) { log.Fatal(http.Serve(l, ch)) }(l) } return s.StopNotify(), nil }