// Clone returns a member with the same server configuration. The returned // member will not set PeerListeners and ClientListeners. func (m *member) Clone(t *testing.T) *member { mm := &member{} mm.ServerConfig = m.ServerConfig var err error clientURLStrs := m.ClientURLs.StringSlice() mm.ClientURLs, err = types.NewURLs(clientURLStrs) if err != nil { // this should never fail panic(err) } peerURLStrs := m.PeerURLs.StringSlice() mm.PeerURLs, err = types.NewURLs(peerURLStrs) if err != nil { // this should never fail panic(err) } clusterStr := m.Cluster.String() mm.Cluster, err = etcdserver.NewClusterFromString(clusterName, clusterStr) if err != nil { // this should never fail panic(err) } mm.Transport = mustNewTransport(t) mm.ElectionTicks = m.ElectionTicks return mm }
// NewEtcd creates a new default etcd server using 'dataDir' for persistence. func NewEtcd(dataDir string) *EtcdServer { clientURLs, err := types.NewURLs([]string{clientURLStr}) if err != nil { glog.Fatalf("Failed to parse client url %q: %v", clientURLStr, err) } peerURLs, err := types.NewURLs([]string{peerURLStr}) if err != nil { glog.Fatalf("Failed to parse peer url %q: %v", peerURLStr, err) } config := &etcdserver.ServerConfig{ Name: etcdName, ClientURLs: clientURLs, PeerURLs: peerURLs, DataDir: dataDir, InitialPeerURLsMap: map[string]types.URLs{etcdName: peerURLs}, NewCluster: true, SnapCount: snapCount, MaxSnapFiles: maxSnapFiles, MaxWALFiles: maxWALFiles, TickMs: tickMs, ElectionTicks: electionTicks, } return &EtcdServer{ config: config, } }
func mustNewMember(t *testing.T, name string) *member { var err error m := &member{} pln := newLocalListener(t) m.PeerListeners = []net.Listener{pln} m.PeerURLs, err = types.NewURLs([]string{"http://" + pln.Addr().String()}) if err != nil { t.Fatal(err) } cln := newLocalListener(t) m.ClientListeners = []net.Listener{cln} m.ClientURLs, err = types.NewURLs([]string{"http://" + cln.Addr().String()}) if err != nil { t.Fatal(err) } m.Name = name m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd") if err != nil { t.Fatal(err) } clusterStr := fmt.Sprintf("%s=http://%s", name, pln.Addr().String()) m.Cluster, err = etcdserver.NewClusterFromString(clusterName, clusterStr) if err != nil { t.Fatal(err) } m.NewCluster = true m.Transport = mustNewTransport(t) m.ElectionTicks = electionTicks m.TickMs = uint(tickDuration / time.Millisecond) return m }
// NewEtcd creates a new default etcd Server using 'dataDir' for persistence. Panics if could not be configured. func (lk LocalkubeServer) NewEtcd(clientURLStrs, peerURLStrs []string, name, dataDirectory string) (*EtcdServer, error) { clientURLs, err := types.NewURLs(clientURLStrs) if err != nil { return nil, err } peerURLs, err := types.NewURLs(peerURLStrs) if err != nil { return nil, err } urlsMap := map[string]types.URLs{ name: peerURLs, } config := &etcdserver.ServerConfig{ Name: name, ClientURLs: clientURLs, PeerURLs: peerURLs, DataDir: dataDirectory, InitialPeerURLsMap: urlsMap, NewCluster: true, SnapCount: etcdserver.DefaultSnapCount, MaxSnapFiles: 5, MaxWALFiles: 5, TickMs: 100, ElectionTicks: 10, } return &EtcdServer{ config: config, }, nil }
// Clone returns a member with the same server configuration. The returned // member will not set PeerListeners and ClientListeners. func (m *member) Clone(t *testing.T) *member { mm := &member{} mm.ServerConfig = m.ServerConfig var err error clientURLStrs := m.ClientURLs.StringSlice() mm.ClientURLs, err = types.NewURLs(clientURLStrs) if err != nil { // this should never fail panic(err) } peerURLStrs := m.PeerURLs.StringSlice() mm.PeerURLs, err = types.NewURLs(peerURLStrs) if err != nil { // this should never fail panic(err) } clusterStr := m.InitialPeerURLsMap.String() mm.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr) if err != nil { // this should never fail panic(err) } mm.InitialClusterToken = m.InitialClusterToken mm.ElectionTicks = m.ElectionTicks mm.PeerTLSInfo = m.PeerTLSInfo mm.ClientTLSInfo = m.ClientTLSInfo return mm }
// configureTestCluster will set the params to start an etcd server func configureTestCluster(t *testing.T, name string) *EtcdTestServer { var err error m := &EtcdTestServer{} pln := newLocalListener(t) m.PeerListeners = []net.Listener{pln} m.PeerURLs, err = types.NewURLs([]string{"http://" + pln.Addr().String()}) if err != nil { t.Fatal(err) } // Allow test launches to control where etcd data goes, for space or performance reasons baseDir := os.Getenv("TEST_ETCD_DIR") if len(baseDir) == 0 { baseDir = os.TempDir() } m.CertificatesDir, err = ioutil.TempDir(baseDir, "etcd_certificates") if err != nil { t.Fatal(err) } m.CertFile = path.Join(m.CertificatesDir, "etcdcert.pem") if err = ioutil.WriteFile(m.CertFile, []byte(CertFileContent), 0644); err != nil { t.Fatal(err) } m.KeyFile = path.Join(m.CertificatesDir, "etcdkey.pem") if err = ioutil.WriteFile(m.KeyFile, []byte(KeyFileContent), 0644); err != nil { t.Fatal(err) } m.CAFile = path.Join(m.CertificatesDir, "ca.pem") if err = ioutil.WriteFile(m.CAFile, []byte(CAFileContent), 0644); err != nil { t.Fatal(err) } cln := newSecuredLocalListener(t, m.CertFile, m.KeyFile, m.CAFile) m.ClientListeners = []net.Listener{cln} m.ClientURLs, err = types.NewURLs([]string{"https://" + cln.Addr().String()}) if err != nil { t.Fatal(err) } m.Name = name m.DataDir, err = ioutil.TempDir(baseDir, "etcd") if err != nil { t.Fatal(err) } clusterStr := fmt.Sprintf("%s=http://%s", name, pln.Addr().String()) m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr) if err != nil { t.Fatal(err) } m.InitialClusterToken = "TestEtcd" m.NewCluster = true m.ForceNewCluster = false m.ElectionTicks = 10 m.TickMs = uint(10) return m }
// configureTestCluster will set the params to start an etcd server func configureTestCluster(t *testing.T, name string) *EtcdTestServer { var err error m := &EtcdTestServer{} pln := newLocalListener(t) m.PeerListeners = []net.Listener{pln} m.PeerURLs, err = types.NewURLs([]string{"http://" + pln.Addr().String()}) if err != nil { t.Fatal(err) } m.CertificatesDir, err = ioutil.TempDir(os.TempDir(), "etcd_certificates") if err != nil { t.Fatal(err) } m.CertFile = path.Join(m.CertificatesDir, "etcdcert.pem") if err = ioutil.WriteFile(m.CertFile, []byte(CertFileContent), 0644); err != nil { t.Fatal(err) } m.KeyFile = path.Join(m.CertificatesDir, "etcdkey.pem") if err = ioutil.WriteFile(m.KeyFile, []byte(KeyFileContent), 0644); err != nil { t.Fatal(err) } m.CAFile = path.Join(m.CertificatesDir, "ca.pem") if err = ioutil.WriteFile(m.CAFile, []byte(CAFileContent), 0644); err != nil { t.Fatal(err) } cln := newSecuredLocalListener(t, m.CertFile, m.KeyFile, m.CAFile) m.ClientListeners = []net.Listener{cln} m.ClientURLs, err = types.NewURLs([]string{"https://" + cln.Addr().String()}) if err != nil { t.Fatal(err) } m.Name = name m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd") if err != nil { t.Fatal(err) } clusterStr := fmt.Sprintf("%s=http://%s", name, pln.Addr().String()) m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr) if err != nil { t.Fatal(err) } m.Transport, err = transport.NewTimeoutTransport(transport.TLSInfo{}, time.Second, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout) if err != nil { t.Fatal(err) } m.NewCluster = true m.ForceNewCluster = false m.ElectionTicks = 10 m.TickMs = uint(10) return m }
// mustNewMember return an inited member with the given name. If usePeerTLS is // true, it will set PeerTLSInfo and use https scheme to communicate between // peers. func mustNewMember(t *testing.T, name string, usePeerTLS bool) *member { var ( testTLSInfo = transport.TLSInfo{ KeyFile: "./fixtures/server.key.insecure", CertFile: "./fixtures/server.crt", TrustedCAFile: "./fixtures/ca.crt", ClientCertAuth: true, } err error ) m := &member{} peerScheme := "http" if usePeerTLS { peerScheme = "https" } pln := newLocalListener(t) m.PeerListeners = []net.Listener{pln} m.PeerURLs, err = types.NewURLs([]string{peerScheme + "://" + pln.Addr().String()}) if err != nil { t.Fatal(err) } if usePeerTLS { m.PeerTLSInfo = testTLSInfo } cln := newLocalListener(t) m.ClientListeners = []net.Listener{cln} m.ClientURLs, err = types.NewURLs([]string{"http://" + cln.Addr().String()}) if err != nil { t.Fatal(err) } m.Name = name m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd") if err != nil { t.Fatal(err) } clusterStr := fmt.Sprintf("%s=%s://%s", name, peerScheme, pln.Addr().String()) m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr) if err != nil { t.Fatal(err) } m.InitialClusterToken = clusterName m.NewCluster = true m.ServerConfig.PeerTLSInfo = m.PeerTLSInfo m.ElectionTicks = electionTicks m.TickMs = uint(tickDuration / time.Millisecond) return m }
// mustNewMember return an inited member with the given name. If peerTLS is // set, it will use https scheme to communicate between peers. func mustNewMember(t *testing.T, mcfg memberConfig) *member { var err error m := &member{} peerScheme, clientScheme := "http", "http" if mcfg.peerTLS != nil { peerScheme = "https" } if mcfg.clientTLS != nil { clientScheme = "https" } pln := newLocalListener(t) m.PeerListeners = []net.Listener{pln} m.PeerURLs, err = types.NewURLs([]string{peerScheme + "://" + pln.Addr().String()}) if err != nil { t.Fatal(err) } m.PeerTLSInfo = mcfg.peerTLS cln := newLocalListener(t) m.ClientListeners = []net.Listener{cln} m.ClientURLs, err = types.NewURLs([]string{clientScheme + "://" + cln.Addr().String()}) if err != nil { t.Fatal(err) } m.ClientTLSInfo = mcfg.clientTLS m.Name = mcfg.name m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd") if err != nil { t.Fatal(err) } clusterStr := fmt.Sprintf("%s=%s://%s", mcfg.name, peerScheme, pln.Addr().String()) m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr) if err != nil { t.Fatal(err) } m.InitialClusterToken = clusterName m.NewCluster = true m.BootstrapTimeout = 10 * time.Millisecond if m.PeerTLSInfo != nil { m.ServerConfig.PeerTLSInfo = *m.PeerTLSInfo } m.ElectionTicks = electionTicks m.TickMs = uint(tickDuration / time.Millisecond) m.QuotaBackendBytes = mcfg.quotaBackendBytes return m }
func mustNewURLs(t *testing.T, urls []string) []url.URL { u, err := types.NewURLs(urls) if err != nil { t.Fatalf("error creating new URLs from %q: %v", urls, err) } return u }
func (m *httpMembersAPI) Update(ctx context.Context, memberID string, peerURLs []string) error { urls, err := types.NewURLs(peerURLs) if err != nil { return err } req := &membersAPIActionUpdate{peerURLs: urls, memberID: memberID} resp, body, err := m.client.Do(ctx, req) if err != nil { return err } if err := assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusNotFound, http.StatusConflict); err != nil { return err } if resp.StatusCode != http.StatusNoContent { var merr membersError if err := json.Unmarshal(body, &merr); err != nil { return err } return merr } return nil }
func mustNewURLs(t *testing.T, urls []string) []url.URL { u, err := types.NewURLs(urls) if err != nil { t.Fatalf("unexpected new urls error: %v", err) } return u }
func (m *httpMembersAPI) Add(ctx context.Context, peerURL string) (*Member, error) { urls, err := types.NewURLs([]string{peerURL}) if err != nil { return nil, err } req := &membersAPIActionAdd{peerURLs: urls} resp, body, err := m.client.Do(ctx, req) if err != nil { return nil, err } if err := assertStatusCode(resp.StatusCode, http.StatusCreated, http.StatusConflict); err != nil { return nil, err } if resp.StatusCode != http.StatusCreated { var merr membersError if err := json.Unmarshal(body, &merr); err != nil { return nil, err } return nil, merr } var memb Member if err := json.Unmarshal(body, &memb); err != nil { return nil, err } return &memb, nil }
func TestGenClusterString(t *testing.T) { tests := []struct { name string urls []string wstr string }{ { "default", []string{"http://127.0.0.1:4001"}, "default=http://127.0.0.1:4001", }, { "node1", []string{"http://0.0.0.0:2379", "http://1.1.1.1:2379"}, "node1=http://0.0.0.0:2379,node1=http://1.1.1.1:2379", }, } for i, tt := range tests { urls, err := types.NewURLs(tt.urls) if err != nil { t.Fatalf("unexpected new urls error: %v", err) } str := genClusterString(tt.name, urls) if str != tt.wstr { t.Errorf("#%d: cluster = %s, want %s", i, str, tt.wstr) } } }
func TestUpgradeMember(t *testing.T) { defer afterTest(t) m := mustNewMember(t, "integration046") newPeerListeners := make([]net.Listener, 0) newPeerListeners = append(newPeerListeners, newListenerWithAddr(t, "127.0.0.1:59892")) m.PeerListeners = newPeerListeners urls, err := types.NewURLs([]string{"http://127.0.0.1:59892"}) if err != nil { t.Fatal(err) } m.PeerURLs = urls m.NewCluster = true c := &cluster{} c.Members = []*member{m} fillClusterForMembers(c.Members, "etcd-cluster") cmd := exec.Command("cp", "-r", "testdata/integration046_data/conf", "testdata/integration046_data/log", "testdata/integration046_data/snapshot", m.DataDir) err = cmd.Run() if err != nil { t.Fatal(err) } c.Launch(t) defer c.Terminate(t) clusterMustProgress(t, c.Members) }
// Set parses a command line set of URLs formatted like: // http://127.0.0.1:7001,http://10.1.1.2:80 func (us *URLsValue) Set(s string) error { strs := strings.Split(s, ",") nus, err := types.NewURLs(strs) if err != nil { return err } *us = URLsValue(nus) return nil }
func generateNodeMember(name, rafturl, etcdurl string) *member { pURLs, err := types.NewURLs([]string{rafturl}) if err != nil { log.Fatalf("Invalid Raft URL %s -- this log could never have worked", rafturl) } m := NewMember(name, pURLs, etcdDefaultClusterName) m.ClientURLs = []string{etcdurl} return m }
func (t *transport) AddRemote(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() if _, ok := t.remotes[id]; ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } t.remotes[id] = startRemote(t.roundTripper, urls, t.id, id, t.clusterID, t.raft, t.errorc) }
func (t *Transport) AddRemote(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() if _, ok := t.remotes[id]; ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } t.remotes[id] = startRemote(t.pipelineRt, urls, t.ID, id, t.ClusterID, t.Raft, t.ErrorC) }
// configureTestCluster will set the params to start an etcd server func configureTestCluster(t *testing.T, name string) *EtcdTestServer { var err error m := &EtcdTestServer{} pln := newLocalListener(t) m.PeerListeners = []net.Listener{pln} m.PeerURLs, err = types.NewURLs([]string{"http://" + pln.Addr().String()}) if err != nil { t.Fatal(err) } cln := newLocalListener(t) m.ClientListeners = []net.Listener{cln} m.ClientURLs, err = types.NewURLs([]string{"http://" + cln.Addr().String()}) if err != nil { t.Fatal(err) } m.Name = name m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd") if err != nil { t.Fatal(err) } clusterStr := fmt.Sprintf("%s=http://%s", name, pln.Addr().String()) m.InitialPeerURLsMap, err = types.NewURLsMap(clusterStr) if err != nil { t.Fatal(err) } m.Transport, err = transport.NewTimeoutTransport(transport.TLSInfo{}, time.Second, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout) if err != nil { t.Fatal(err) } m.NewCluster = true m.ForceNewCluster = false m.ElectionTicks = 10 m.TickMs = uint(10) return m }
func (t *transport) UpdatePeer(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() // TODO: return error or just panic? if _, ok := t.peers[id]; !ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } t.peers[id].Update(urls) }
func (t *transport) AddPeer(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() if _, ok := t.peers[id]; ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } fs := t.leaderStats.Follower(id.String()) t.peers[id] = startPeer(t.roundTripper, urls, t.id, id, t.clusterID, t.raft, fs, t.errorc, t.term) }
func NewSimpleEtcd() (*SimpleEtcd, error) { var err error se := &SimpleEtcd{} se.listener, err = net.Listen("tcp", ":0") if err != nil { return nil, err } se.Port = se.listener.Addr().(*net.TCPAddr).Port clientURLs, err := interfaceURLs(se.Port) if err != nil { se.Destroy() return nil, err } se.dataDir, err = ioutil.TempDir("", tempPrefix) if err != nil { se.Destroy() return nil, err } peerURLs, err := types.NewURLs([]string{peerURL}) if err != nil { se.Destroy() return nil, err } cfg := &etcdserver.ServerConfig{ Name: memberName, ClientURLs: clientURLs, PeerURLs: peerURLs, DataDir: se.dataDir, InitialPeerURLsMap: types.URLsMap{ memberName: peerURLs, }, NewCluster: true, TickMs: 100, ElectionTicks: 10, } se.server, err = etcdserver.NewServer(cfg) if err != nil { return nil, err } se.server.Start() go http.Serve(se.listener, etcdhttp.NewClientHandler(se.server, cfg.ReqTimeout())) return se, nil }
func (t *Transport) AddPeer(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() if _, ok := t.peers[id]; ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } fs := t.LeaderStats.Follower(id.String()) t.peers[id] = startPeer(t.streamRt, t.pipelineRt, urls, t.ID, id, t.ClusterID, t.Raft, fs, t.ErrorC, t.V3demo) addPeerToProber(t.prober, id.String(), us) }
func (t *Transport) UpdatePeer(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() // TODO: return error or just panic? if _, ok := t.peers[id]; !ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } t.peers[id].update(urls) t.prober.Remove(id.String()) addPeerToProber(t.prober, id.String(), us) }
// TODO: support TLS func (c *cluster) Launch(t *testing.T) { if c.Size <= 0 { t.Fatalf("cluster size <= 0") } lns := make([]net.Listener, c.Size) addrs := make([]string, c.Size) for i := 0; i < c.Size; i++ { l := newLocalListener(t) // each member claims only one peer listener lns[i] = l addrs[i] = fmt.Sprintf("%v=%v", c.name(i), "http://"+l.Addr().String()) } clusterStr := strings.Join(addrs, ",") var err error for i := 0; i < c.Size; i++ { m := member{} m.PeerListeners = []net.Listener{lns[i]} cln := newLocalListener(t) m.ClientListeners = []net.Listener{cln} m.Name = c.name(i) m.ClientURLs, err = types.NewURLs([]string{"http://" + cln.Addr().String()}) if err != nil { t.Fatal(err) } m.DataDir, err = ioutil.TempDir(os.TempDir(), "etcd") if err != nil { t.Fatal(err) } m.Cluster, err = etcdserver.NewClusterFromString(clusterName, clusterStr) if err != nil { t.Fatal(err) } m.ClusterState = etcdserver.ClusterStateValueNew m.Transport, err = transport.NewTransport(transport.TLSInfo{}) if err != nil { t.Fatal(err) } // TODO: need the support of graceful stop in Sender to remove this m.Transport.DisableKeepAlives = true m.Transport.Dial = (&net.Dialer{Timeout: 100 * time.Millisecond}).Dial m.Launch(t) c.Members = append(c.Members, m) } }
func (cs *ClusterServer) MemberAdd(ctx context.Context, r *pb.MemberAddRequest) (*pb.MemberAddResponse, error) { urls, err := types.NewURLs(r.PeerURLs) if err != nil { return nil, rpctypes.ErrGRPCMemberBadURLs } now := time.Now() m := membership.NewMember("", urls, "", &now) if err = cs.server.AddMember(ctx, *m); err != nil { return nil, togRPCError(err) } return &pb.MemberAddResponse{ Header: cs.header(), Member: &pb.Member{ID: uint64(m.ID), PeerURLs: m.PeerURLs}, }, nil }
func (m *MemberCreateRequest) UnmarshalJSON(data []byte) error { s := struct { PeerURLs []string `json:"peerURLs"` }{} err := json.Unmarshal(data, &s) if err != nil { return err } urls, err := types.NewURLs(s.PeerURLs) if err != nil { return err } m.PeerURLs = urls return nil }
func (t *Transport) AddPeer(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() if t.peers == nil { panic("transport stopped") } if _, ok := t.peers[id]; ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } fs := t.LeaderStats.Follower(id.String()) t.peers[id] = startPeer(t, urls, id, fs) addPeerToProber(t.prober, id.String(), us) plog.Infof("added peer %s", id) }
func (t *Transport) AddRemote(id types.ID, us []string) { t.mu.Lock() defer t.mu.Unlock() if t.remotes == nil { // there's no clean way to shutdown the golang http server // (see: https://github.com/golang/go/issues/4674) before // stopping the transport; ignore any new connections. return } if _, ok := t.peers[id]; ok { return } if _, ok := t.remotes[id]; ok { return } urls, err := types.NewURLs(us) if err != nil { plog.Panicf("newURLs %+v should never fail: %+v", us, err) } t.remotes[id] = startRemote(t, urls, id) }