Dependencies for the affinity router and the
affinity routing daemon.
Change-Id: Icda72c3594ef7f8f0bc0c33dc03087a4c25529ca
diff --git a/vendor/github.com/coreos/etcd/etcdmain/config.go b/vendor/github.com/coreos/etcd/etcdmain/config.go
new file mode 100644
index 0000000..2a5faa7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/config.go
@@ -0,0 +1,346 @@
+// Copyright 2015 The etcd Authors
+//
+// 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.
+
+// Every change should be reflected on help.go as well.
+
+package etcdmain
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "runtime"
+ "strings"
+
+ "github.com/coreos/etcd/embed"
+ "github.com/coreos/etcd/pkg/flags"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/version"
+
+ "github.com/ghodss/yaml"
+)
+
+var (
+ proxyFlagOff = "off"
+ proxyFlagReadonly = "readonly"
+ proxyFlagOn = "on"
+
+ fallbackFlagExit = "exit"
+ fallbackFlagProxy = "proxy"
+
+ 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",
+ // for coverage testing
+ "test.coverprofile",
+ "test.outputdir",
+ }
+)
+
+type configProxy struct {
+ ProxyFailureWaitMs uint `json:"proxy-failure-wait"`
+ ProxyRefreshIntervalMs uint `json:"proxy-refresh-interval"`
+ ProxyDialTimeoutMs uint `json:"proxy-dial-timeout"`
+ ProxyWriteTimeoutMs uint `json:"proxy-write-timeout"`
+ ProxyReadTimeoutMs uint `json:"proxy-read-timeout"`
+ Fallback string
+ Proxy string
+ ProxyJSON string `json:"proxy"`
+ FallbackJSON string `json:"discovery-fallback"`
+}
+
+// config holds the config for a command line invocation of etcd
+type config struct {
+ ec embed.Config
+ cp configProxy
+ cf configFlags
+ configFile string
+ printVersion bool
+ ignored []string
+}
+
+// configFlags has the set of flags used for command line parsing a Config
+type configFlags struct {
+ flagSet *flag.FlagSet
+ clusterState *flags.StringsFlag
+ fallback *flags.StringsFlag
+ proxy *flags.StringsFlag
+}
+
+func newConfig() *config {
+ cfg := &config{
+ ec: *embed.NewConfig(),
+ cp: configProxy{
+ Proxy: proxyFlagOff,
+ ProxyFailureWaitMs: 5000,
+ ProxyRefreshIntervalMs: 30000,
+ ProxyDialTimeoutMs: 1000,
+ ProxyWriteTimeoutMs: 5000,
+ },
+ ignored: ignored,
+ }
+ cfg.cf = configFlags{
+ flagSet: flag.NewFlagSet("etcd", flag.ContinueOnError),
+ clusterState: flags.NewStringsFlag(
+ embed.ClusterStateFlagNew,
+ embed.ClusterStateFlagExisting,
+ ),
+ fallback: flags.NewStringsFlag(
+ fallbackFlagProxy,
+ fallbackFlagExit,
+ ),
+ proxy: flags.NewStringsFlag(
+ proxyFlagOff,
+ proxyFlagReadonly,
+ proxyFlagOn,
+ ),
+ }
+
+ fs := cfg.cf.flagSet
+ fs.Usage = func() {
+ fmt.Fprintln(os.Stderr, usageline)
+ }
+
+ fs.StringVar(&cfg.configFile, "config-file", "", "Path to the server configuration file")
+
+ // member
+ fs.Var(cfg.ec.CorsInfo, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
+ fs.StringVar(&cfg.ec.Dir, "data-dir", cfg.ec.Dir, "Path to the data directory.")
+ fs.StringVar(&cfg.ec.WalDir, "wal-dir", cfg.ec.WalDir, "Path to the dedicated wal directory.")
+ fs.Var(flags.NewURLsValue(embed.DefaultListenPeerURLs), "listen-peer-urls", "List of URLs to listen on for peer traffic.")
+ fs.Var(flags.NewURLsValue(embed.DefaultListenClientURLs), "listen-client-urls", "List of URLs to listen on for client traffic.")
+ fs.StringVar(&cfg.ec.ListenMetricsUrlsJSON, "listen-metrics-urls", "", "List of URLs to listen on for metrics.")
+ fs.UintVar(&cfg.ec.MaxSnapFiles, "max-snapshots", cfg.ec.MaxSnapFiles, "Maximum number of snapshot files to retain (0 is unlimited).")
+ fs.UintVar(&cfg.ec.MaxWalFiles, "max-wals", cfg.ec.MaxWalFiles, "Maximum number of wal files to retain (0 is unlimited).")
+ fs.StringVar(&cfg.ec.Name, "name", cfg.ec.Name, "Human-readable name for this member.")
+ fs.Uint64Var(&cfg.ec.SnapCount, "snapshot-count", cfg.ec.SnapCount, "Number of committed transactions to trigger a snapshot to disk.")
+ fs.UintVar(&cfg.ec.TickMs, "heartbeat-interval", cfg.ec.TickMs, "Time (in milliseconds) of a heartbeat interval.")
+ fs.UintVar(&cfg.ec.ElectionMs, "election-timeout", cfg.ec.ElectionMs, "Time (in milliseconds) for an election to timeout.")
+ fs.BoolVar(&cfg.ec.InitialElectionTickAdvance, "initial-election-tick-advance", cfg.ec.InitialElectionTickAdvance, "Whether to fast-forward initial election ticks on boot for faster election.")
+ fs.Int64Var(&cfg.ec.QuotaBackendBytes, "quota-backend-bytes", cfg.ec.QuotaBackendBytes, "Raise alarms when backend size exceeds the given quota. 0 means use the default quota.")
+ fs.UintVar(&cfg.ec.MaxTxnOps, "max-txn-ops", cfg.ec.MaxTxnOps, "Maximum number of operations permitted in a transaction.")
+ fs.UintVar(&cfg.ec.MaxRequestBytes, "max-request-bytes", cfg.ec.MaxRequestBytes, "Maximum client request size in bytes the server will accept.")
+ fs.DurationVar(&cfg.ec.GRPCKeepAliveMinTime, "grpc-keepalive-min-time", cfg.ec.GRPCKeepAliveMinTime, "Minimum interval duration that a client should wait before pinging server.")
+ fs.DurationVar(&cfg.ec.GRPCKeepAliveInterval, "grpc-keepalive-interval", cfg.ec.GRPCKeepAliveInterval, "Frequency duration of server-to-client ping to check if a connection is alive (0 to disable).")
+ fs.DurationVar(&cfg.ec.GRPCKeepAliveTimeout, "grpc-keepalive-timeout", cfg.ec.GRPCKeepAliveTimeout, "Additional duration of wait before closing a non-responsive connection (0 to disable).")
+
+ // clustering
+ fs.Var(flags.NewURLsValue(embed.DefaultInitialAdvertisePeerURLs), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster.")
+ fs.Var(flags.NewURLsValue(embed.DefaultAdvertiseClientURLs), "advertise-client-urls", "List of this member's client URLs to advertise to the public.")
+ fs.StringVar(&cfg.ec.Durl, "discovery", cfg.ec.Durl, "Discovery URL used to bootstrap the cluster.")
+ fs.Var(cfg.cf.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %s", strings.Join(cfg.cf.fallback.Values, ", ")))
+
+ fs.StringVar(&cfg.ec.Dproxy, "discovery-proxy", cfg.ec.Dproxy, "HTTP proxy to use for traffic to discovery service.")
+ fs.StringVar(&cfg.ec.DNSCluster, "discovery-srv", cfg.ec.DNSCluster, "DNS domain used to bootstrap initial cluster.")
+ fs.StringVar(&cfg.ec.InitialCluster, "initial-cluster", cfg.ec.InitialCluster, "Initial cluster configuration for bootstrapping.")
+ fs.StringVar(&cfg.ec.InitialClusterToken, "initial-cluster-token", cfg.ec.InitialClusterToken, "Initial cluster token for the etcd cluster during bootstrap.")
+ fs.Var(cfg.cf.clusterState, "initial-cluster-state", "Initial cluster state ('new' or 'existing').")
+
+ fs.BoolVar(&cfg.ec.StrictReconfigCheck, "strict-reconfig-check", cfg.ec.StrictReconfigCheck, "Reject reconfiguration requests that would cause quorum loss.")
+ fs.BoolVar(&cfg.ec.EnableV2, "enable-v2", cfg.ec.EnableV2, "Accept etcd V2 client requests.")
+ fs.StringVar(&cfg.ec.ExperimentalEnableV2V3, "experimental-enable-v2v3", cfg.ec.ExperimentalEnableV2V3, "v3 prefix for serving emulated v2 state.")
+
+ // proxy
+ fs.Var(cfg.cf.proxy, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(cfg.cf.proxy.Values, ", ")))
+
+ fs.UintVar(&cfg.cp.ProxyFailureWaitMs, "proxy-failure-wait", cfg.cp.ProxyFailureWaitMs, "Time (in milliseconds) an endpoint will be held in a failed state.")
+ fs.UintVar(&cfg.cp.ProxyRefreshIntervalMs, "proxy-refresh-interval", cfg.cp.ProxyRefreshIntervalMs, "Time (in milliseconds) of the endpoints refresh interval.")
+ fs.UintVar(&cfg.cp.ProxyDialTimeoutMs, "proxy-dial-timeout", cfg.cp.ProxyDialTimeoutMs, "Time (in milliseconds) for a dial to timeout.")
+ fs.UintVar(&cfg.cp.ProxyWriteTimeoutMs, "proxy-write-timeout", cfg.cp.ProxyWriteTimeoutMs, "Time (in milliseconds) for a write to timeout.")
+ fs.UintVar(&cfg.cp.ProxyReadTimeoutMs, "proxy-read-timeout", cfg.cp.ProxyReadTimeoutMs, "Time (in milliseconds) for a read to timeout.")
+
+ // security
+ fs.StringVar(&cfg.ec.ClientTLSInfo.CAFile, "ca-file", "", "DEPRECATED: Path to the client server TLS CA file.")
+ fs.StringVar(&cfg.ec.ClientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
+ fs.StringVar(&cfg.ec.ClientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
+ fs.BoolVar(&cfg.ec.ClientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.")
+ fs.StringVar(&cfg.ec.ClientTLSInfo.CRLFile, "client-crl-file", "", "Path to the client certificate revocation list file.")
+ fs.StringVar(&cfg.ec.ClientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA cert file.")
+ fs.BoolVar(&cfg.ec.ClientAutoTLS, "auto-tls", false, "Client TLS using generated certificates")
+ fs.StringVar(&cfg.ec.PeerTLSInfo.CAFile, "peer-ca-file", "", "DEPRECATED: Path to the peer server TLS CA file.")
+ fs.StringVar(&cfg.ec.PeerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
+ fs.StringVar(&cfg.ec.PeerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
+ fs.BoolVar(&cfg.ec.PeerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.")
+ fs.StringVar(&cfg.ec.PeerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.")
+ fs.BoolVar(&cfg.ec.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates")
+ fs.StringVar(&cfg.ec.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer certificate revocation list file.")
+ fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedCN, "peer-cert-allowed-cn", "", "Allowed CN for inter peer authentication.")
+
+ fs.Var(flags.NewStringsValueV2(""), "cipher-suites", "Comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).")
+
+ // logging
+ fs.BoolVar(&cfg.ec.Debug, "debug", false, "Enable debug-level logging for etcd.")
+ fs.StringVar(&cfg.ec.LogPkgLevels, "log-package-levels", "", "Specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').")
+ fs.StringVar(&cfg.ec.LogOutput, "log-output", embed.DefaultLogOutput, "Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.")
+
+ // unsafe
+ fs.BoolVar(&cfg.ec.ForceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster.")
+
+ // version
+ fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.")
+
+ fs.StringVar(&cfg.ec.AutoCompactionRetention, "auto-compaction-retention", "0", "Auto compaction retention for mvcc key value store. 0 means disable auto compaction.")
+ fs.StringVar(&cfg.ec.AutoCompactionMode, "auto-compaction-mode", "periodic", "interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.")
+
+ // pprof profiler via HTTP
+ fs.BoolVar(&cfg.ec.EnablePprof, "enable-pprof", false, "Enable runtime profiling data via HTTP server. Address is at client URL + \"/debug/pprof/\"")
+
+ // additional metrics
+ fs.StringVar(&cfg.ec.Metrics, "metrics", cfg.ec.Metrics, "Set level of detail for exported metrics, specify 'extensive' to include histogram metrics")
+
+ // auth
+ fs.StringVar(&cfg.ec.AuthToken, "auth-token", cfg.ec.AuthToken, "Specify auth token specific options.")
+
+ // experimental
+ fs.BoolVar(&cfg.ec.ExperimentalInitialCorruptCheck, "experimental-initial-corrupt-check", cfg.ec.ExperimentalInitialCorruptCheck, "Enable to check data corruption before serving any client/peer traffic.")
+ fs.DurationVar(&cfg.ec.ExperimentalCorruptCheckTime, "experimental-corrupt-check-time", cfg.ec.ExperimentalCorruptCheckTime, "Duration of time between cluster corruption check passes.")
+
+ // ignored
+ for _, f := range cfg.ignored {
+ fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
+ }
+ return cfg
+}
+
+func (cfg *config) parse(arguments []string) error {
+ perr := cfg.cf.flagSet.Parse(arguments)
+ switch perr {
+ case nil:
+ case flag.ErrHelp:
+ fmt.Println(flagsline)
+ os.Exit(0)
+ default:
+ os.Exit(2)
+ }
+ if len(cfg.cf.flagSet.Args()) != 0 {
+ return fmt.Errorf("'%s' is not a valid flag", cfg.cf.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)
+ }
+
+ var err error
+ if cfg.configFile != "" {
+ plog.Infof("Loading server configuration from %q", cfg.configFile)
+ err = cfg.configFromFile(cfg.configFile)
+ } else {
+ err = cfg.configFromCmdLine()
+ }
+ return err
+}
+
+func (cfg *config) configFromCmdLine() error {
+ err := flags.SetFlagsFromEnv("ETCD", cfg.cf.flagSet)
+ if err != nil {
+ plog.Fatalf("%v", err)
+ }
+
+ cfg.ec.LPUrls = flags.URLsFromFlag(cfg.cf.flagSet, "listen-peer-urls")
+ cfg.ec.APUrls = flags.URLsFromFlag(cfg.cf.flagSet, "initial-advertise-peer-urls")
+ cfg.ec.LCUrls = flags.URLsFromFlag(cfg.cf.flagSet, "listen-client-urls")
+ cfg.ec.ACUrls = flags.URLsFromFlag(cfg.cf.flagSet, "advertise-client-urls")
+
+ if len(cfg.ec.ListenMetricsUrlsJSON) > 0 {
+ u, err := types.NewURLs(strings.Split(cfg.ec.ListenMetricsUrlsJSON, ","))
+ if err != nil {
+ plog.Fatalf("unexpected error setting up listen-metrics-urls: %v", err)
+ }
+ cfg.ec.ListenMetricsUrls = []url.URL(u)
+ }
+
+ cfg.ec.CipherSuites = flags.StringsFromFlagV2(cfg.cf.flagSet, "cipher-suites")
+
+ cfg.ec.ClusterState = cfg.cf.clusterState.String()
+ cfg.cp.Fallback = cfg.cf.fallback.String()
+ cfg.cp.Proxy = cfg.cf.proxy.String()
+
+ // disable default advertise-client-urls if lcurls is set
+ missingAC := flags.IsSet(cfg.cf.flagSet, "listen-client-urls") && !flags.IsSet(cfg.cf.flagSet, "advertise-client-urls")
+ if !cfg.mayBeProxy() && missingAC {
+ cfg.ec.ACUrls = nil
+ }
+
+ // disable default initial-cluster if discovery is set
+ if (cfg.ec.Durl != "" || cfg.ec.DNSCluster != "") && !flags.IsSet(cfg.cf.flagSet, "initial-cluster") {
+ cfg.ec.InitialCluster = ""
+ }
+
+ return cfg.validate()
+}
+
+func (cfg *config) configFromFile(path string) error {
+ eCfg, err := embed.ConfigFromFile(path)
+ if err != nil {
+ return err
+ }
+ cfg.ec = *eCfg
+
+ // load extra config information
+ b, rerr := ioutil.ReadFile(path)
+ if rerr != nil {
+ return rerr
+ }
+ if yerr := yaml.Unmarshal(b, &cfg.cp); yerr != nil {
+ return yerr
+ }
+ if cfg.cp.FallbackJSON != "" {
+ if err := cfg.cf.fallback.Set(cfg.cp.FallbackJSON); err != nil {
+ plog.Panicf("unexpected error setting up discovery-fallback flag: %v", err)
+ }
+ cfg.cp.Fallback = cfg.cf.fallback.String()
+ }
+ if cfg.cp.ProxyJSON != "" {
+ if err := cfg.cf.proxy.Set(cfg.cp.ProxyJSON); err != nil {
+ plog.Panicf("unexpected error setting up proxyFlag: %v", err)
+ }
+ cfg.cp.Proxy = cfg.cf.proxy.String()
+ }
+ return nil
+}
+
+func (cfg *config) mayBeProxy() bool {
+ mayFallbackToProxy := cfg.ec.Durl != "" && cfg.cp.Fallback == fallbackFlagProxy
+ return cfg.cp.Proxy != proxyFlagOff || mayFallbackToProxy
+}
+
+func (cfg *config) validate() error {
+ err := cfg.ec.Validate()
+ // TODO(yichengq): check this for joining through discovery service case
+ if err == embed.ErrUnsetAdvertiseClientURLsFlag && cfg.mayBeProxy() {
+ return nil
+ }
+ return err
+}
+
+func (cfg config) isProxy() bool { return cfg.cf.proxy.String() != proxyFlagOff }
+func (cfg config) isReadonlyProxy() bool { return cfg.cf.proxy.String() == proxyFlagReadonly }
+func (cfg config) shouldFallbackToProxy() bool { return cfg.cf.fallback.String() == fallbackFlagProxy }
diff --git a/vendor/github.com/coreos/etcd/etcdmain/doc.go b/vendor/github.com/coreos/etcd/etcdmain/doc.go
new file mode 100644
index 0000000..ff281aa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/doc.go
@@ -0,0 +1,16 @@
+// Copyright 2015 The etcd Authors
+//
+// 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 contains the main entry point for the etcd binary.
+package etcdmain
diff --git a/vendor/github.com/coreos/etcd/etcdmain/etcd.go b/vendor/github.com/coreos/etcd/etcdmain/etcd.go
new file mode 100644
index 0000000..87e9b25
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/etcd.go
@@ -0,0 +1,399 @@
+// Copyright 2015 The etcd Authors
+//
+// 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 (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/discovery"
+ "github.com/coreos/etcd/embed"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/api/etcdhttp"
+ "github.com/coreos/etcd/pkg/cors"
+ "github.com/coreos/etcd/pkg/fileutil"
+ pkgioutil "github.com/coreos/etcd/pkg/ioutil"
+ "github.com/coreos/etcd/pkg/osutil"
+ "github.com/coreos/etcd/pkg/transport"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/proxy/httpproxy"
+ "github.com/coreos/etcd/version"
+
+ "github.com/coreos/pkg/capnslog"
+ "google.golang.org/grpc"
+)
+
+type dirType string
+
+var plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdmain")
+
+var (
+ dirMember = dirType("member")
+ dirProxy = dirType("proxy")
+ dirEmpty = dirType("empty")
+)
+
+func startEtcdOrProxyV2() {
+ grpc.EnableTracing = false
+
+ cfg := newConfig()
+ defaultInitialCluster := cfg.ec.InitialCluster
+
+ err := cfg.parse(os.Args[1:])
+ if err != nil {
+ plog.Errorf("error verifying flags, %v. See 'etcd --help'.", err)
+ switch err {
+ case embed.ErrUnsetAdvertiseClientURLsFlag:
+ plog.Errorf("When listening on specific address(es), this etcd process must advertise accessible url(s) to each connected client.")
+ }
+ os.Exit(1)
+ }
+ cfg.ec.SetupLogging()
+
+ var stopped <-chan struct{}
+ var errc <-chan error
+
+ 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())
+
+ defaultHost, dhErr := (&cfg.ec).UpdateDefaultClusterFromName(defaultInitialCluster)
+ if defaultHost != "" {
+ plog.Infof("advertising using detected default host %q", defaultHost)
+ }
+ if dhErr != nil {
+ plog.Noticef("failed to detect default host (%v)", dhErr)
+ }
+
+ if cfg.ec.Dir == "" {
+ cfg.ec.Dir = fmt.Sprintf("%v.etcd", cfg.ec.Name)
+ plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.ec.Dir)
+ }
+
+ which := identifyDataDirOrDie(cfg.ec.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, errc, err = startEtcd(&cfg.ec)
+ case dirProxy:
+ err = startProxy(cfg)
+ default:
+ plog.Panicf("unhandled dir type %v", which)
+ }
+ } else {
+ shouldProxy := cfg.isProxy()
+ if !shouldProxy {
+ stopped, errc, err = startEtcd(&cfg.ec)
+ if derr, ok := err.(*etcdserver.DiscoveryError); ok && derr.Err == discovery.ErrFullCluster {
+ if cfg.shouldFallbackToProxy() {
+ plog.Noticef("discovery cluster full, falling back to %s", fallbackFlagProxy)
+ shouldProxy = true
+ }
+ }
+ }
+ if shouldProxy {
+ err = startProxy(cfg)
+ }
+ }
+
+ if err != nil {
+ if derr, ok := err.(*etcdserver.DiscoveryError); ok {
+ switch derr.Err {
+ case discovery.ErrDuplicateID:
+ plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.ec.Name, cfg.ec.Durl)
+ plog.Errorf("But etcd could not find valid cluster configuration in the given data dir (%s).", cfg.ec.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.")
+ case discovery.ErrDuplicateName:
+ plog.Errorf("member with duplicated name has registered with discovery service token(%s).", cfg.ec.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.Errorf("%v", err)
+ plog.Infof("discovery token %s was used, but failed to bootstrap the cluster.", cfg.ec.Durl)
+ plog.Infof("please generate a new discovery token and try to bootstrap again.")
+ }
+ os.Exit(1)
+ }
+
+ if strings.Contains(err.Error(), "include") && strings.Contains(err.Error(), "--initial-cluster") {
+ plog.Infof("%v", err)
+ if cfg.ec.InitialCluster == cfg.ec.InitialClusterFromName(cfg.ec.Name) {
+ plog.Infof("forgot to set --initial-cluster flag?")
+ }
+ if types.URLs(cfg.ec.APUrls).String() == embed.DefaultInitialAdvertisePeerURLs {
+ plog.Infof("forgot to set --initial-advertise-peer-urls flag?")
+ }
+ if cfg.ec.InitialCluster == cfg.ec.InitialClusterFromName(cfg.ec.Name) && len(cfg.ec.Durl) == 0 {
+ plog.Infof("if you want to use discovery service, please set --discovery flag.")
+ }
+ os.Exit(1)
+ }
+ plog.Fatalf("%v", err)
+ }
+
+ osutil.HandleInterrupts()
+
+ // At this point, the initialization of etcd is done.
+ // The listeners are listening on the TCP ports and ready
+ // for accepting connections. The etcd instance should be
+ // joined with the cluster and ready to serve incoming
+ // connections.
+ notifySystemd()
+
+ select {
+ case lerr := <-errc:
+ // fatal out on listener errors
+ plog.Fatal(lerr)
+ case <-stopped:
+ }
+
+ osutil.Exit(0)
+}
+
+// startEtcd runs StartEtcd in addition to hooks needed for standalone etcd.
+func startEtcd(cfg *embed.Config) (<-chan struct{}, <-chan error, error) {
+ e, err := embed.StartEtcd(cfg)
+ if err != nil {
+ return nil, nil, err
+ }
+ osutil.RegisterInterruptHandler(e.Close)
+ select {
+ case <-e.Server.ReadyNotify(): // wait for e.Server to join the cluster
+ case <-e.Server.StopNotify(): // publish aborted from 'ErrStopped'
+ }
+ return e.Server.StopNotify(), e.Err(), nil
+}
+
+// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
+func startProxy(cfg *config) error {
+ plog.Notice("proxy: this proxy supports v2 API only!")
+
+ clientTLSInfo := cfg.ec.ClientTLSInfo
+ if clientTLSInfo.Empty() {
+ // Support old proxy behavior of defaulting to PeerTLSInfo
+ // for both client and peer connections.
+ clientTLSInfo = cfg.ec.PeerTLSInfo
+ }
+ clientTLSInfo.InsecureSkipVerify = cfg.ec.ClientAutoTLS
+ cfg.ec.PeerTLSInfo.InsecureSkipVerify = cfg.ec.PeerAutoTLS
+
+ pt, err := transport.NewTimeoutTransport(clientTLSInfo, time.Duration(cfg.cp.ProxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.cp.ProxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.cp.ProxyWriteTimeoutMs)*time.Millisecond)
+ if err != nil {
+ return err
+ }
+ pt.MaxIdleConnsPerHost = httpproxy.DefaultMaxIdleConnsPerHost
+
+ if err = cfg.ec.PeerSelfCert(); err != nil {
+ plog.Fatalf("could not get certs (%v)", err)
+ }
+ tr, err := transport.NewTimeoutTransport(cfg.ec.PeerTLSInfo, time.Duration(cfg.cp.ProxyDialTimeoutMs)*time.Millisecond, time.Duration(cfg.cp.ProxyReadTimeoutMs)*time.Millisecond, time.Duration(cfg.cp.ProxyWriteTimeoutMs)*time.Millisecond)
+ if err != nil {
+ return err
+ }
+
+ cfg.ec.Dir = filepath.Join(cfg.ec.Dir, "proxy")
+ err = os.MkdirAll(cfg.ec.Dir, fileutil.PrivateDirMode)
+ if err != nil {
+ return err
+ }
+
+ var peerURLs []string
+ clusterfile := filepath.Join(cfg.ec.Dir, "cluster")
+
+ b, err := ioutil.ReadFile(clusterfile)
+ switch {
+ case err == nil:
+ if cfg.ec.Durl != "" {
+ plog.Warningf("discovery token ignored since the proxy has already been initialized. Valid cluster file found at %q", clusterfile)
+ }
+ if cfg.ec.DNSCluster != "" {
+ plog.Warningf("DNS SRV discovery ignored since the proxy has already been initialized. Valid cluster file found at %q", clusterfile)
+ }
+ urls := struct{ PeerURLs []string }{}
+ err = json.Unmarshal(b, &urls)
+ if err != nil {
+ return err
+ }
+ peerURLs = urls.PeerURLs
+ plog.Infof("proxy: using peer urls %v from cluster file %q", peerURLs, clusterfile)
+ case os.IsNotExist(err):
+ var urlsmap types.URLsMap
+ urlsmap, _, err = cfg.ec.PeerURLsMapAndToken("proxy")
+ if err != nil {
+ return fmt.Errorf("error setting up initial cluster: %v", err)
+ }
+
+ if cfg.ec.Durl != "" {
+ var s string
+ s, err = discovery.GetCluster(cfg.ec.Durl, cfg.ec.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)
+ default:
+ return err
+ }
+
+ clientURLs := []string{}
+ uf := func() []string {
+ gcls, gerr := etcdserver.GetClusterFromRemotePeers(peerURLs, tr)
+
+ if gerr != nil {
+ plog.Warningf("proxy: %v", gerr)
+ return []string{}
+ }
+
+ clientURLs = gcls.ClientURLs()
+
+ urls := struct{ PeerURLs []string }{gcls.PeerURLs()}
+ b, jerr := json.Marshal(urls)
+ if jerr != nil {
+ plog.Warningf("proxy: error on marshal peer urls %s", jerr)
+ return clientURLs
+ }
+
+ err = pkgioutil.WriteAndSyncFile(clusterfile+".bak", b, 0600)
+ if err != nil {
+ plog.Warningf("proxy: error on writing urls %s", err)
+ return clientURLs
+ }
+ err = os.Rename(clusterfile+".bak", clusterfile)
+ if err != nil {
+ plog.Warningf("proxy: error on updating clusterfile %s", err)
+ return clientURLs
+ }
+ if !reflect.DeepEqual(gcls.PeerURLs(), peerURLs) {
+ plog.Noticef("proxy: updated peer urls in cluster file from %v to %v", peerURLs, gcls.PeerURLs())
+ }
+ peerURLs = gcls.PeerURLs()
+
+ return clientURLs
+ }
+ ph := httpproxy.NewHandler(pt, uf, time.Duration(cfg.cp.ProxyFailureWaitMs)*time.Millisecond, time.Duration(cfg.cp.ProxyRefreshIntervalMs)*time.Millisecond)
+ ph = &cors.CORSHandler{
+ Handler: ph,
+ Info: cfg.ec.CorsInfo,
+ }
+
+ if cfg.isReadonlyProxy() {
+ ph = httpproxy.NewReadonlyHandler(ph)
+ }
+
+ // setup self signed certs when serving https
+ cHosts, cTLS := []string{}, false
+ for _, u := range cfg.ec.LCUrls {
+ cHosts = append(cHosts, u.Host)
+ cTLS = cTLS || u.Scheme == "https"
+ }
+ for _, u := range cfg.ec.ACUrls {
+ cHosts = append(cHosts, u.Host)
+ cTLS = cTLS || u.Scheme == "https"
+ }
+ listenerTLS := cfg.ec.ClientTLSInfo
+ if cfg.ec.ClientAutoTLS && cTLS {
+ listenerTLS, err = transport.SelfCert(filepath.Join(cfg.ec.Dir, "clientCerts"), cHosts)
+ if err != nil {
+ plog.Fatalf("proxy: could not initialize self-signed client certs (%v)", err)
+ }
+ }
+
+ // Start a proxy server goroutine for each listen address
+ for _, u := range cfg.ec.LCUrls {
+ l, err := transport.NewListener(u.Host, u.Scheme, &listenerTLS)
+ if err != nil {
+ return err
+ }
+
+ host := u.String()
+ go func() {
+ plog.Info("proxy: listening for client requests on ", host)
+ mux := http.NewServeMux()
+ etcdhttp.HandlePrometheus(mux) // v2 proxy just uses the same port
+ mux.Handle("/", ph)
+ plog.Fatal(http.Serve(l, mux))
+ }()
+ }
+ return nil
+}
+
+// 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)
+ }
+
+ 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)
+ }
+ }
+
+ if m && p {
+ plog.Fatal("invalid datadir. Both member and proxy directories exist.")
+ }
+ if m {
+ return dirMember
+ }
+ if p {
+ return dirProxy
+ }
+ return dirEmpty
+}
+
+func checkSupportArch() {
+ // TODO qualify arm64
+ if runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64le" {
+ return
+ }
+ // unsupported arch only configured via environment variable
+ // so unset here to not parse through flag
+ defer os.Unsetenv("ETCD_UNSUPPORTED_ARCH")
+ if env, ok := os.LookupEnv("ETCD_UNSUPPORTED_ARCH"); ok && env == runtime.GOARCH {
+ plog.Warningf("running etcd on unsupported architecture %q since ETCD_UNSUPPORTED_ARCH is set", env)
+ return
+ }
+ plog.Errorf("etcd on unsupported platform without ETCD_UNSUPPORTED_ARCH=%s set.", runtime.GOARCH)
+ os.Exit(1)
+}
diff --git a/vendor/github.com/coreos/etcd/etcdmain/gateway.go b/vendor/github.com/coreos/etcd/etcdmain/gateway.go
new file mode 100644
index 0000000..5487414
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/gateway.go
@@ -0,0 +1,135 @@
+// Copyright 2016 The etcd Authors
+//
+// 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"
+ "net"
+ "net/url"
+ "os"
+ "time"
+
+ "github.com/coreos/etcd/proxy/tcpproxy"
+
+ "github.com/spf13/cobra"
+)
+
+var (
+ gatewayListenAddr string
+ gatewayEndpoints []string
+ gatewayDNSCluster string
+ gatewayInsecureDiscovery bool
+ getewayRetryDelay time.Duration
+ gatewayCA string
+)
+
+var (
+ rootCmd = &cobra.Command{
+ Use: "etcd",
+ Short: "etcd server",
+ SuggestFor: []string{"etcd"},
+ }
+)
+
+func init() {
+ rootCmd.AddCommand(newGatewayCommand())
+}
+
+// newGatewayCommand returns the cobra command for "gateway".
+func newGatewayCommand() *cobra.Command {
+ lpc := &cobra.Command{
+ Use: "gateway <subcommand>",
+ Short: "gateway related command",
+ }
+ lpc.AddCommand(newGatewayStartCommand())
+
+ return lpc
+}
+
+func newGatewayStartCommand() *cobra.Command {
+ cmd := cobra.Command{
+ Use: "start",
+ Short: "start the gateway",
+ Run: startGateway,
+ }
+
+ cmd.Flags().StringVar(&gatewayListenAddr, "listen-addr", "127.0.0.1:23790", "listen address")
+ cmd.Flags().StringVar(&gatewayDNSCluster, "discovery-srv", "", "DNS domain used to bootstrap initial cluster")
+ cmd.Flags().BoolVar(&gatewayInsecureDiscovery, "insecure-discovery", false, "accept insecure SRV records")
+ cmd.Flags().StringVar(&gatewayCA, "trusted-ca-file", "", "path to the client server TLS CA file.")
+
+ cmd.Flags().StringSliceVar(&gatewayEndpoints, "endpoints", []string{"127.0.0.1:2379"}, "comma separated etcd cluster endpoints")
+
+ cmd.Flags().DurationVar(&getewayRetryDelay, "retry-delay", time.Minute, "duration of delay before retrying failed endpoints")
+
+ return &cmd
+}
+
+func stripSchema(eps []string) []string {
+ var endpoints []string
+
+ for _, ep := range eps {
+
+ if u, err := url.Parse(ep); err == nil && u.Host != "" {
+ ep = u.Host
+ }
+
+ endpoints = append(endpoints, ep)
+ }
+
+ return endpoints
+}
+
+func startGateway(cmd *cobra.Command, args []string) {
+ srvs := discoverEndpoints(gatewayDNSCluster, gatewayCA, gatewayInsecureDiscovery)
+ if len(srvs.Endpoints) == 0 {
+ // no endpoints discovered, fall back to provided endpoints
+ srvs.Endpoints = gatewayEndpoints
+ }
+ // Strip the schema from the endpoints because we start just a TCP proxy
+ srvs.Endpoints = stripSchema(srvs.Endpoints)
+ if len(srvs.SRVs) == 0 {
+ for _, ep := range srvs.Endpoints {
+ h, p, err := net.SplitHostPort(ep)
+ if err != nil {
+ plog.Fatalf("error parsing endpoint %q", ep)
+ }
+ var port uint16
+ fmt.Sscanf(p, "%d", &port)
+ srvs.SRVs = append(srvs.SRVs, &net.SRV{Target: h, Port: port})
+ }
+ }
+
+ if len(srvs.Endpoints) == 0 {
+ plog.Fatalf("no endpoints found")
+ }
+
+ l, err := net.Listen("tcp", gatewayListenAddr)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ tp := tcpproxy.TCPProxy{
+ Listener: l,
+ Endpoints: srvs.SRVs,
+ MonitorInterval: getewayRetryDelay,
+ }
+
+ // At this point, etcd gateway listener is initialized
+ notifySystemd()
+
+ tp.Run()
+}
diff --git a/vendor/github.com/coreos/etcd/etcdmain/grpc_proxy.go b/vendor/github.com/coreos/etcd/etcdmain/grpc_proxy.go
new file mode 100644
index 0000000..6a8e39f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/grpc_proxy.go
@@ -0,0 +1,399 @@
+// Copyright 2016 The etcd Authors
+//
+// 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 (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "math"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/coreos/etcd/clientv3"
+ "github.com/coreos/etcd/clientv3/leasing"
+ "github.com/coreos/etcd/clientv3/namespace"
+ "github.com/coreos/etcd/clientv3/ordering"
+ "github.com/coreos/etcd/etcdserver/api/etcdhttp"
+ "github.com/coreos/etcd/etcdserver/api/v3election/v3electionpb"
+ "github.com/coreos/etcd/etcdserver/api/v3lock/v3lockpb"
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/pkg/debugutil"
+ "github.com/coreos/etcd/pkg/transport"
+ "github.com/coreos/etcd/proxy/grpcproxy"
+
+ "github.com/coreos/pkg/capnslog"
+ grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
+ "github.com/soheilhy/cmux"
+ "github.com/spf13/cobra"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/grpclog"
+)
+
+var (
+ grpcProxyListenAddr string
+ grpcProxyMetricsListenAddr string
+ grpcProxyEndpoints []string
+ grpcProxyDNSCluster string
+ grpcProxyInsecureDiscovery bool
+ grpcProxyDataDir string
+ grpcMaxCallSendMsgSize int
+ grpcMaxCallRecvMsgSize int
+
+ // tls for connecting to etcd
+
+ grpcProxyCA string
+ grpcProxyCert string
+ grpcProxyKey string
+ grpcProxyInsecureSkipTLSVerify bool
+
+ // tls for clients connecting to proxy
+
+ grpcProxyListenCA string
+ grpcProxyListenCert string
+ grpcProxyListenKey string
+ grpcProxyListenAutoTLS bool
+ grpcProxyListenCRL string
+
+ grpcProxyAdvertiseClientURL string
+ grpcProxyResolverPrefix string
+ grpcProxyResolverTTL int
+
+ grpcProxyNamespace string
+ grpcProxyLeasing string
+
+ grpcProxyEnablePprof bool
+ grpcProxyEnableOrdering bool
+
+ grpcProxyDebug bool
+)
+
+const defaultGRPCMaxCallSendMsgSize = 1.5 * 1024 * 1024
+
+func init() {
+ rootCmd.AddCommand(newGRPCProxyCommand())
+}
+
+// newGRPCProxyCommand returns the cobra command for "grpc-proxy".
+func newGRPCProxyCommand() *cobra.Command {
+ lpc := &cobra.Command{
+ Use: "grpc-proxy <subcommand>",
+ Short: "grpc-proxy related command",
+ }
+ lpc.AddCommand(newGRPCProxyStartCommand())
+
+ return lpc
+}
+
+func newGRPCProxyStartCommand() *cobra.Command {
+ cmd := cobra.Command{
+ Use: "start",
+ Short: "start the grpc proxy",
+ Run: startGRPCProxy,
+ }
+
+ cmd.Flags().StringVar(&grpcProxyListenAddr, "listen-addr", "127.0.0.1:23790", "listen address")
+ cmd.Flags().StringVar(&grpcProxyDNSCluster, "discovery-srv", "", "DNS domain used to bootstrap initial cluster")
+ cmd.Flags().StringVar(&grpcProxyMetricsListenAddr, "metrics-addr", "", "listen for /metrics requests on an additional interface")
+ cmd.Flags().BoolVar(&grpcProxyInsecureDiscovery, "insecure-discovery", false, "accept insecure SRV records")
+ cmd.Flags().StringSliceVar(&grpcProxyEndpoints, "endpoints", []string{"127.0.0.1:2379"}, "comma separated etcd cluster endpoints")
+ cmd.Flags().StringVar(&grpcProxyAdvertiseClientURL, "advertise-client-url", "127.0.0.1:23790", "advertise address to register (must be reachable by client)")
+ cmd.Flags().StringVar(&grpcProxyResolverPrefix, "resolver-prefix", "", "prefix to use for registering proxy (must be shared with other grpc-proxy members)")
+ cmd.Flags().IntVar(&grpcProxyResolverTTL, "resolver-ttl", 0, "specify TTL, in seconds, when registering proxy endpoints")
+ cmd.Flags().StringVar(&grpcProxyNamespace, "namespace", "", "string to prefix to all keys for namespacing requests")
+ cmd.Flags().BoolVar(&grpcProxyEnablePprof, "enable-pprof", false, `Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"`)
+ cmd.Flags().StringVar(&grpcProxyDataDir, "data-dir", "default.proxy", "Data directory for persistent data")
+ cmd.Flags().IntVar(&grpcMaxCallSendMsgSize, "max-send-bytes", defaultGRPCMaxCallSendMsgSize, "message send limits in bytes (default value is 1.5 MiB)")
+ cmd.Flags().IntVar(&grpcMaxCallRecvMsgSize, "max-recv-bytes", math.MaxInt32, "message receive limits in bytes (default value is math.MaxInt32)")
+
+ // client TLS for connecting to server
+ cmd.Flags().StringVar(&grpcProxyCert, "cert", "", "identify secure connections with etcd servers using this TLS certificate file")
+ cmd.Flags().StringVar(&grpcProxyKey, "key", "", "identify secure connections with etcd servers using this TLS key file")
+ cmd.Flags().StringVar(&grpcProxyCA, "cacert", "", "verify certificates of TLS-enabled secure etcd servers using this CA bundle")
+ cmd.Flags().BoolVar(&grpcProxyInsecureSkipTLSVerify, "insecure-skip-tls-verify", false, "skip authentication of etcd server TLS certificates")
+
+ // client TLS for connecting to proxy
+ cmd.Flags().StringVar(&grpcProxyListenCert, "cert-file", "", "identify secure connections to the proxy using this TLS certificate file")
+ cmd.Flags().StringVar(&grpcProxyListenKey, "key-file", "", "identify secure connections to the proxy using this TLS key file")
+ cmd.Flags().StringVar(&grpcProxyListenCA, "trusted-ca-file", "", "verify certificates of TLS-enabled secure proxy using this CA bundle")
+ cmd.Flags().BoolVar(&grpcProxyListenAutoTLS, "auto-tls", false, "proxy TLS using generated certificates")
+ cmd.Flags().StringVar(&grpcProxyListenCRL, "client-crl-file", "", "proxy client certificate revocation list file.")
+
+ // experimental flags
+ cmd.Flags().BoolVar(&grpcProxyEnableOrdering, "experimental-serializable-ordering", false, "Ensure serializable reads have monotonically increasing store revisions across endpoints.")
+ cmd.Flags().StringVar(&grpcProxyLeasing, "experimental-leasing-prefix", "", "leasing metadata prefix for disconnected linearized reads.")
+
+ cmd.Flags().BoolVar(&grpcProxyDebug, "debug", false, "Enable debug-level logging for grpc-proxy.")
+
+ return &cmd
+}
+
+func startGRPCProxy(cmd *cobra.Command, args []string) {
+ checkArgs()
+
+ capnslog.SetGlobalLogLevel(capnslog.INFO)
+ if grpcProxyDebug {
+ capnslog.SetGlobalLogLevel(capnslog.DEBUG)
+ grpc.EnableTracing = true
+ // enable info, warning, error
+ grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
+ } else {
+ // only discard info
+ grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, os.Stderr, os.Stderr))
+ }
+
+ tlsinfo := newTLS(grpcProxyListenCA, grpcProxyListenCert, grpcProxyListenKey)
+ if tlsinfo == nil && grpcProxyListenAutoTLS {
+ host := []string{"https://" + grpcProxyListenAddr}
+ dir := filepath.Join(grpcProxyDataDir, "fixtures", "proxy")
+ autoTLS, err := transport.SelfCert(dir, host)
+ if err != nil {
+ plog.Fatal(err)
+ }
+ tlsinfo = &autoTLS
+ }
+ if tlsinfo != nil {
+ plog.Infof("ServerTLS: %s", tlsinfo)
+ }
+ m := mustListenCMux(tlsinfo)
+
+ grpcl := m.Match(cmux.HTTP2())
+ defer func() {
+ grpcl.Close()
+ plog.Infof("stopping listening for grpc-proxy client requests on %s", grpcProxyListenAddr)
+ }()
+
+ client := mustNewClient()
+
+ srvhttp, httpl := mustHTTPListener(m, tlsinfo, client)
+ errc := make(chan error)
+ go func() { errc <- newGRPCProxyServer(client).Serve(grpcl) }()
+ go func() { errc <- srvhttp.Serve(httpl) }()
+ go func() { errc <- m.Serve() }()
+ if len(grpcProxyMetricsListenAddr) > 0 {
+ mhttpl := mustMetricsListener(tlsinfo)
+ go func() {
+ mux := http.NewServeMux()
+ etcdhttp.HandlePrometheus(mux)
+ grpcproxy.HandleHealth(mux, client)
+ plog.Fatal(http.Serve(mhttpl, mux))
+ }()
+ }
+
+ // grpc-proxy is initialized, ready to serve
+ notifySystemd()
+
+ fmt.Fprintln(os.Stderr, <-errc)
+ os.Exit(1)
+}
+
+func checkArgs() {
+ if grpcProxyResolverPrefix != "" && grpcProxyResolverTTL < 1 {
+ fmt.Fprintln(os.Stderr, fmt.Errorf("invalid resolver-ttl %d", grpcProxyResolverTTL))
+ os.Exit(1)
+ }
+ if grpcProxyResolverPrefix == "" && grpcProxyResolverTTL > 0 {
+ fmt.Fprintln(os.Stderr, fmt.Errorf("invalid resolver-prefix %q", grpcProxyResolverPrefix))
+ os.Exit(1)
+ }
+ if grpcProxyResolverPrefix != "" && grpcProxyResolverTTL > 0 && grpcProxyAdvertiseClientURL == "" {
+ fmt.Fprintln(os.Stderr, fmt.Errorf("invalid advertise-client-url %q", grpcProxyAdvertiseClientURL))
+ os.Exit(1)
+ }
+}
+
+func mustNewClient() *clientv3.Client {
+ srvs := discoverEndpoints(grpcProxyDNSCluster, grpcProxyCA, grpcProxyInsecureDiscovery)
+ eps := srvs.Endpoints
+ if len(eps) == 0 {
+ eps = grpcProxyEndpoints
+ }
+ cfg, err := newClientCfg(eps)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ cfg.DialOptions = append(cfg.DialOptions,
+ grpc.WithUnaryInterceptor(grpcproxy.AuthUnaryClientInterceptor))
+ cfg.DialOptions = append(cfg.DialOptions,
+ grpc.WithStreamInterceptor(grpcproxy.AuthStreamClientInterceptor))
+ client, err := clientv3.New(*cfg)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ return client
+}
+
+func newClientCfg(eps []string) (*clientv3.Config, error) {
+ // set tls if any one tls option set
+ cfg := clientv3.Config{
+ Endpoints: eps,
+ DialTimeout: 5 * time.Second,
+ }
+
+ if grpcMaxCallSendMsgSize > 0 {
+ cfg.MaxCallSendMsgSize = grpcMaxCallSendMsgSize
+ }
+ if grpcMaxCallRecvMsgSize > 0 {
+ cfg.MaxCallRecvMsgSize = grpcMaxCallRecvMsgSize
+ }
+
+ tls := newTLS(grpcProxyCA, grpcProxyCert, grpcProxyKey)
+ if tls == nil && grpcProxyInsecureSkipTLSVerify {
+ tls = &transport.TLSInfo{}
+ }
+ if tls != nil {
+ clientTLS, err := tls.ClientConfig()
+ if err != nil {
+ return nil, err
+ }
+ clientTLS.InsecureSkipVerify = grpcProxyInsecureSkipTLSVerify
+ cfg.TLS = clientTLS
+ plog.Infof("ClientTLS: %s", tls)
+ }
+ return &cfg, nil
+}
+
+func newTLS(ca, cert, key string) *transport.TLSInfo {
+ if ca == "" && cert == "" && key == "" {
+ return nil
+ }
+ return &transport.TLSInfo{CAFile: ca, CertFile: cert, KeyFile: key}
+}
+
+func mustListenCMux(tlsinfo *transport.TLSInfo) cmux.CMux {
+ l, err := net.Listen("tcp", grpcProxyListenAddr)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+
+ if l, err = transport.NewKeepAliveListener(l, "tcp", nil); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ if tlsinfo != nil {
+ tlsinfo.CRLFile = grpcProxyListenCRL
+ if l, err = transport.NewTLSListener(l, tlsinfo); err != nil {
+ plog.Fatal(err)
+ }
+ }
+
+ plog.Infof("listening for grpc-proxy client requests on %s", grpcProxyListenAddr)
+ return cmux.New(l)
+}
+
+func newGRPCProxyServer(client *clientv3.Client) *grpc.Server {
+ if grpcProxyEnableOrdering {
+ vf := ordering.NewOrderViolationSwitchEndpointClosure(*client)
+ client.KV = ordering.NewKV(client.KV, vf)
+ plog.Infof("waiting for linearized read from cluster to recover ordering")
+ for {
+ _, err := client.KV.Get(context.TODO(), "_", clientv3.WithKeysOnly())
+ if err == nil {
+ break
+ }
+ plog.Warningf("ordering recovery failed, retrying in 1s (%v)", err)
+ time.Sleep(time.Second)
+ }
+ }
+
+ if len(grpcProxyNamespace) > 0 {
+ client.KV = namespace.NewKV(client.KV, grpcProxyNamespace)
+ client.Watcher = namespace.NewWatcher(client.Watcher, grpcProxyNamespace)
+ client.Lease = namespace.NewLease(client.Lease, grpcProxyNamespace)
+ }
+
+ if len(grpcProxyLeasing) > 0 {
+ client.KV, _, _ = leasing.NewKV(client, grpcProxyLeasing)
+ }
+
+ kvp, _ := grpcproxy.NewKvProxy(client)
+ watchp, _ := grpcproxy.NewWatchProxy(client)
+ if grpcProxyResolverPrefix != "" {
+ grpcproxy.Register(client, grpcProxyResolverPrefix, grpcProxyAdvertiseClientURL, grpcProxyResolverTTL)
+ }
+ clusterp, _ := grpcproxy.NewClusterProxy(client, grpcProxyAdvertiseClientURL, grpcProxyResolverPrefix)
+ leasep, _ := grpcproxy.NewLeaseProxy(client)
+ mainp := grpcproxy.NewMaintenanceProxy(client)
+ authp := grpcproxy.NewAuthProxy(client)
+ electionp := grpcproxy.NewElectionProxy(client)
+ lockp := grpcproxy.NewLockProxy(client)
+
+ server := grpc.NewServer(
+ grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
+ grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
+ grpc.MaxConcurrentStreams(math.MaxUint32),
+ )
+
+ pb.RegisterKVServer(server, kvp)
+ pb.RegisterWatchServer(server, watchp)
+ pb.RegisterClusterServer(server, clusterp)
+ pb.RegisterLeaseServer(server, leasep)
+ pb.RegisterMaintenanceServer(server, mainp)
+ pb.RegisterAuthServer(server, authp)
+ v3electionpb.RegisterElectionServer(server, electionp)
+ v3lockpb.RegisterLockServer(server, lockp)
+
+ // set zero values for metrics registered for this grpc server
+ grpc_prometheus.Register(server)
+
+ return server
+}
+
+func mustHTTPListener(m cmux.CMux, tlsinfo *transport.TLSInfo, c *clientv3.Client) (*http.Server, net.Listener) {
+ httpmux := http.NewServeMux()
+ httpmux.HandleFunc("/", http.NotFound)
+ etcdhttp.HandlePrometheus(httpmux)
+ grpcproxy.HandleHealth(httpmux, c)
+ if grpcProxyEnablePprof {
+ for p, h := range debugutil.PProfHandlers() {
+ httpmux.Handle(p, h)
+ }
+ plog.Infof("pprof is enabled under %s", debugutil.HTTPPrefixPProf)
+ }
+ srvhttp := &http.Server{Handler: httpmux}
+
+ if tlsinfo == nil {
+ return srvhttp, m.Match(cmux.HTTP1())
+ }
+
+ srvTLS, err := tlsinfo.ServerConfig()
+ if err != nil {
+ plog.Fatalf("could not setup TLS (%v)", err)
+ }
+ srvhttp.TLSConfig = srvTLS
+ return srvhttp, m.Match(cmux.Any())
+}
+
+func mustMetricsListener(tlsinfo *transport.TLSInfo) net.Listener {
+ murl, err := url.Parse(grpcProxyMetricsListenAddr)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cannot parse %q", grpcProxyMetricsListenAddr)
+ os.Exit(1)
+ }
+ ml, err := transport.NewListener(murl.Host, murl.Scheme, tlsinfo)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ plog.Info("grpc-proxy: listening for metrics on ", murl.String())
+ return ml
+}
diff --git a/vendor/github.com/coreos/etcd/etcdmain/help.go b/vendor/github.com/coreos/etcd/etcdmain/help.go
new file mode 100644
index 0000000..c64dab3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/help.go
@@ -0,0 +1,203 @@
+// Copyright 2015 The etcd Authors
+//
+// 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 (
+ "strconv"
+
+ "github.com/coreos/etcd/embed"
+)
+
+var (
+ usageline = `usage: etcd [flags]
+ start an etcd server
+
+ etcd --version
+ show the version of etcd
+
+ etcd -h | --help
+ show the help information about etcd
+
+ etcd --config-file
+ path to the server configuration file
+
+ etcd gateway
+ run the stateless pass-through etcd TCP connection forwarding proxy
+
+ etcd grpc-proxy
+ run the stateless etcd v3 gRPC L7 reverse proxy
+ `
+ flagsline = `
+member flags:
+
+ --name 'default'
+ human-readable name for this member.
+ --data-dir '${name}.etcd'
+ path to the data directory.
+ --wal-dir ''
+ path to the dedicated wal directory.
+ --snapshot-count '100000'
+ number of committed transactions to trigger a snapshot to disk.
+ --heartbeat-interval '100'
+ time (in milliseconds) of a heartbeat interval.
+ --election-timeout '1000'
+ time (in milliseconds) for an election to timeout. See tuning documentation for details.
+ --initial-election-tick-advance 'true'
+ whether to fast-forward initial election ticks on boot for faster election.
+ --listen-peer-urls 'http://localhost:2380'
+ list of URLs to listen on for peer traffic.
+ --listen-client-urls 'http://localhost:2379'
+ list of URLs to listen on for client traffic.
+ --max-snapshots '` + strconv.Itoa(embed.DefaultMaxSnapshots) + `'
+ maximum number of snapshot files to retain (0 is unlimited).
+ --max-wals '` + strconv.Itoa(embed.DefaultMaxWALs) + `'
+ maximum number of wal files to retain (0 is unlimited).
+ --cors ''
+ comma-separated whitelist of origins for CORS (cross-origin resource sharing).
+ --quota-backend-bytes '0'
+ raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
+ --max-txn-ops '128'
+ maximum number of operations permitted in a transaction.
+ --max-request-bytes '1572864'
+ maximum client request size in bytes the server will accept.
+ --grpc-keepalive-min-time '5s'
+ minimum duration interval that a client should wait before pinging server.
+ --grpc-keepalive-interval '2h'
+ frequency duration of server-to-client ping to check if a connection is alive (0 to disable).
+ --grpc-keepalive-timeout '20s'
+ additional duration of wait before closing a non-responsive connection (0 to disable).
+
+clustering flags:
+
+ --initial-advertise-peer-urls 'http://localhost:2380'
+ list of this member's peer URLs to advertise to the rest of the cluster.
+ --initial-cluster 'default=http://localhost:2380'
+ initial cluster configuration for bootstrapping.
+ --initial-cluster-state 'new'
+ initial cluster state ('new' or 'existing').
+ --initial-cluster-token 'etcd-cluster'
+ initial cluster token for the etcd cluster during bootstrap.
+ Specifying this can protect you from unintended cross-cluster interaction when running multiple clusters.
+ --advertise-client-urls 'http://localhost:2379'
+ list of this member's client URLs to advertise to the public.
+ The client URLs advertised should be accessible to machines that talk to etcd cluster. etcd client libraries parse these URLs to connect to the cluster.
+ --discovery ''
+ discovery URL used to bootstrap the cluster.
+ --discovery-fallback 'proxy'
+ expected behavior ('exit' or 'proxy') when discovery services fails.
+ "proxy" supports v2 API only.
+ --discovery-proxy ''
+ HTTP proxy to use for traffic to discovery service.
+ --discovery-srv ''
+ dns srv domain used to bootstrap the cluster.
+ --strict-reconfig-check '` + strconv.FormatBool(embed.DefaultStrictReconfigCheck) + `'
+ reject reconfiguration requests that would cause quorum loss.
+ --auto-compaction-retention '0'
+ auto compaction retention length. 0 means disable auto compaction.
+ --auto-compaction-mode 'periodic'
+ interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.
+ --enable-v2 '` + strconv.FormatBool(embed.DefaultEnableV2) + `'
+ Accept etcd V2 client requests.
+
+proxy flags:
+ "proxy" supports v2 API only.
+
+ --proxy 'off'
+ proxy mode setting ('off', 'readonly' or 'on').
+ --proxy-failure-wait 5000
+ time (in milliseconds) an endpoint will be held in a failed state.
+ --proxy-refresh-interval 30000
+ time (in milliseconds) of the endpoints refresh interval.
+ --proxy-dial-timeout 1000
+ time (in milliseconds) for a dial to timeout.
+ --proxy-write-timeout 5000
+ time (in milliseconds) for a write to timeout.
+ --proxy-read-timeout 0
+ time (in milliseconds) for a read to timeout.
+
+
+security flags:
+
+ --ca-file '' [DEPRECATED]
+ path to the client server TLS CA file. '-ca-file ca.crt' could be replaced by '-trusted-ca-file ca.crt -client-cert-auth' and etcd will perform the same.
+ --cert-file ''
+ path to the client server TLS cert file.
+ --key-file ''
+ path to the client server TLS key file.
+ --client-cert-auth 'false'
+ enable client cert authentication.
+ --client-crl-file ''
+ path to the client certificate revocation list file.
+ --trusted-ca-file ''
+ path to the client server TLS trusted CA cert file.
+ --auto-tls 'false'
+ client TLS using generated certificates.
+ --peer-ca-file '' [DEPRECATED]
+ path to the peer server TLS CA file. '-peer-ca-file ca.crt' could be replaced by '-peer-trusted-ca-file ca.crt -peer-client-cert-auth' and etcd will perform the same.
+ --peer-cert-file ''
+ path to the peer server TLS cert file.
+ --peer-key-file ''
+ path to the peer server TLS key file.
+ --peer-client-cert-auth 'false'
+ enable peer client cert authentication.
+ --peer-trusted-ca-file ''
+ path to the peer server TLS trusted CA file.
+ --peer-cert-allowed-cn ''
+ Required CN for client certs connecting to the peer endpoint.
+ --peer-auto-tls 'false'
+ peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided.
+ --peer-crl-file ''
+ path to the peer certificate revocation list file.
+ --cipher-suites ''
+ comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).
+
+logging flags
+
+ --debug 'false'
+ enable debug-level logging for etcd.
+ --log-package-levels ''
+ specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').
+ --log-output 'default'
+ specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.
+
+unsafe flags:
+
+Please be CAUTIOUS when using unsafe flags because it will break the guarantees
+given by the consensus protocol.
+
+ --force-new-cluster 'false'
+ force to create a new one-member cluster.
+
+profiling flags:
+ --enable-pprof 'false'
+ Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"
+ --metrics 'basic'
+ Set level of detail for exported metrics, specify 'extensive' to include histogram metrics.
+ --listen-metrics-urls ''
+ List of URLs to listen on for metrics.
+
+auth flags:
+ --auth-token 'simple'
+ Specify a v3 authentication token type and its options ('simple' or 'jwt').
+
+experimental flags:
+ --experimental-initial-corrupt-check 'false'
+ enable to check data corruption before serving any client/peer traffic.
+ --experimental-corrupt-check-time '0s'
+ duration of time between cluster corruption check passes.
+ --experimental-enable-v2v3 ''
+ serve v2 requests through the v3 backend under a given prefix.
+`
+)
diff --git a/vendor/github.com/coreos/etcd/etcdmain/main.go b/vendor/github.com/coreos/etcd/etcdmain/main.go
new file mode 100644
index 0000000..06bbae5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/main.go
@@ -0,0 +1,60 @@
+// Copyright 2015 The etcd Authors
+//
+// 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"
+ "os"
+ "strings"
+
+ "github.com/coreos/go-systemd/daemon"
+ systemdutil "github.com/coreos/go-systemd/util"
+)
+
+func Main() {
+ checkSupportArch()
+
+ if len(os.Args) > 1 {
+ cmd := os.Args[1]
+ if covArgs := os.Getenv("ETCDCOV_ARGS"); len(covArgs) > 0 {
+ args := strings.Split(os.Getenv("ETCDCOV_ARGS"), "\xe7\xcd")[1:]
+ rootCmd.SetArgs(args)
+ cmd = "grpc-proxy"
+ }
+ switch cmd {
+ case "gateway", "grpc-proxy":
+ if err := rootCmd.Execute(); err != nil {
+ fmt.Fprint(os.Stderr, err)
+ os.Exit(1)
+ }
+ return
+ }
+ }
+
+ startEtcdOrProxyV2()
+}
+
+func notifySystemd() {
+ if !systemdutil.IsRunningSystemd() {
+ return
+ }
+ sent, err := daemon.SdNotify(false, "READY=1")
+ if err != nil {
+ plog.Errorf("failed to notify systemd for readiness: %v", err)
+ }
+ if !sent {
+ plog.Errorf("forgot to set Type=notify in systemd service file?")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdmain/util.go b/vendor/github.com/coreos/etcd/etcdmain/util.go
new file mode 100644
index 0000000..9657271
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdmain/util.go
@@ -0,0 +1,65 @@
+// Copyright 2017 The etcd Authors
+//
+// 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"
+ "os"
+
+ "github.com/coreos/etcd/pkg/srv"
+ "github.com/coreos/etcd/pkg/transport"
+)
+
+func discoverEndpoints(dns string, ca string, insecure bool) (s srv.SRVClients) {
+ if dns == "" {
+ return s
+ }
+ srvs, err := srv.GetClient("etcd-client", dns)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ endpoints := srvs.Endpoints
+ plog.Infof("discovered the cluster %s from %s", endpoints, dns)
+ if insecure {
+ return *srvs
+ }
+ // confirm TLS connections are good
+ tlsInfo := transport.TLSInfo{
+ TrustedCAFile: ca,
+ ServerName: dns,
+ }
+ plog.Infof("validating discovered endpoints %v", endpoints)
+ endpoints, err = transport.ValidateSecureEndpoints(tlsInfo, endpoints)
+ if err != nil {
+ plog.Warningf("%v", err)
+ }
+ plog.Infof("using discovered endpoints %v", endpoints)
+
+ // map endpoints back to SRVClients struct with SRV data
+ eps := make(map[string]struct{})
+ for _, ep := range endpoints {
+ eps[ep] = struct{}{}
+ }
+ for i := range srvs.Endpoints {
+ if _, ok := eps[srvs.Endpoints[i]]; !ok {
+ continue
+ }
+ s.Endpoints = append(s.Endpoints, srvs.Endpoints[i])
+ s.SRVs = append(s.SRVs, srvs.SRVs[i])
+ }
+
+ return s
+}