// setupCluster sets up an initial cluster definition for bootstrap or discovery. func setupCluster(cfg *config) (*etcdserver.Cluster, error) { var cls *etcdserver.Cluster var err error switch { case cfg.durl != "": // If using discovery, generate a temporary cluster based on // self's advertised peer URLs clusterStr := genClusterString(cfg.name, cfg.apurls) cls, err = etcdserver.NewClusterFromString(cfg.durl, clusterStr) case cfg.dnsCluster != "": clusterStr, clusterToken, err := discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls) if err != nil { return nil, err } cls, err = etcdserver.NewClusterFromString(clusterToken, clusterStr) default: // We're statically configured, and cluster has appropriately been set. cls, err = etcdserver.NewClusterFromString(cfg.initialClusterToken, cfg.initialCluster) } return cls, err }
// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes. func startProxy(cfg *config) error { cls, err := setupCluster(cfg) if err != nil { return fmt.Errorf("error setting up initial cluster: %v", err) } if cfg.durl != "" { s, err := discovery.GetCluster(cfg.durl, cfg.dproxy) if err != nil { return err } if cls, err = etcdserver.NewClusterFromString(cfg.durl, s); err != nil { return err } } pt, err := transport.NewTransport(cfg.clientTLSInfo) if err != nil { return err } tr, err := transport.NewTransport(cfg.peerTLSInfo) if err != nil { return err } cfg.dir = path.Join(cfg.dir, "proxy") err = os.MkdirAll(cfg.dir, 0700) if err != nil { return err } var peerURLs []string clusterfile := path.Join(cfg.dir, "cluster") b, err := ioutil.ReadFile(clusterfile) switch { case err == nil: urls := struct{ PeerURLs []string }{} err := json.Unmarshal(b, &urls) if err != nil { return err } peerURLs = urls.PeerURLs log.Printf("proxy: using peer urls %v from cluster file ./%s", peerURLs, clusterfile) case os.IsNotExist(err): peerURLs = cls.PeerURLs() log.Printf("proxy: using peer urls %v ", peerURLs) default: return err } uf := func() []string { gcls, err := etcdserver.GetClusterFromRemotePeers(peerURLs, tr) // TODO: remove the 2nd check when we fix GetClusterFromPeers // GetClusterFromPeers should not return nil error with an invaild empty cluster if err != nil { log.Printf("proxy: %v", err) return []string{} } if len(gcls.Members()) == 0 { return cls.ClientURLs() } cls = gcls urls := struct{ PeerURLs []string }{cls.PeerURLs()} b, err := json.Marshal(urls) if err != nil { log.Printf("proxy: error on marshal peer urls %s", err) return cls.ClientURLs() } err = ioutil.WriteFile(clusterfile+".bak", b, 0600) if err != nil { log.Printf("proxy: error on writing urls %s", err) return cls.ClientURLs() } err = os.Rename(clusterfile+".bak", clusterfile) if err != nil { log.Printf("proxy: error on updating clusterfile %s", err) return cls.ClientURLs() } if !reflect.DeepEqual(cls.PeerURLs(), peerURLs) { log.Printf("proxy: updated peer urls in cluster file from %v to %v", peerURLs, cls.PeerURLs()) } peerURLs = cls.PeerURLs() return cls.ClientURLs() } ph := proxy.NewHandler(pt, uf) ph = &cors.CORSHandler{ Handler: ph, Info: cfg.corsInfo, } if cfg.isReadonlyProxy() { ph = proxy.NewReadonlyHandler(ph) } // Start a proxy server goroutine for each listen address for _, u := range cfg.lcurls { l, err := transport.NewListener(u.Host, u.Scheme, cfg.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 }