github.com/macb/etcd@v0.3.1-0.20140227003422-a60481c6b1a0/tests/functional/discovery_test.go (about)

     1  package test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/coreos/etcd/third_party/github.com/stretchr/testify/assert"
    14  
    15  	etcdtest "github.com/coreos/etcd/tests"
    16  	"github.com/coreos/etcd/server"
    17  	goetcd "github.com/coreos/etcd/third_party/github.com/coreos/go-etcd/etcd"
    18  )
    19  
    20  type garbageHandler struct {
    21  	t       *testing.T
    22  	success bool
    23  }
    24  
    25  func (g *garbageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    26  	fmt.Fprintln(w, "Hello, client")
    27  	if r.URL.String() != "/v2/keys/_etcd/registry/1/node1" {
    28  		g.t.Fatalf("Unexpected web request")
    29  	}
    30  	g.success = true
    31  }
    32  
    33  // TestDiscoveryDownNoBackupPeers ensures that etcd stops if it is started with a
    34  // bad discovery URL and no backups.
    35  func TestDiscoveryDownNoBackupPeers(t *testing.T) {
    36  	g := garbageHandler{t: t}
    37  	ts := httptest.NewServer(&g)
    38  	defer ts.Close()
    39  
    40  	discover := ts.URL + "/v2/keys/_etcd/registry/1"
    41  	proc, err := startServer([]string{"-discovery", discover})
    42  
    43  	if err != nil {
    44  		t.Fatal(err.Error())
    45  	}
    46  	defer stopServer(proc)
    47  
    48  	client := http.Client{}
    49  	err = assertServerNotUp(client, "http")
    50  	if err != nil {
    51  		t.Fatal(err.Error())
    52  	}
    53  
    54  	if !g.success {
    55  		t.Fatal("Discovery server never called")
    56  	}
    57  }
    58  
    59  // TestDiscoveryDownWithBackupPeers ensures that etcd runs if it is started with a
    60  // bad discovery URL and a peer list.
    61  func TestDiscoveryDownWithBackupPeers(t *testing.T) {
    62  	etcdtest.RunServer(func(s *server.Server) {
    63  		g := garbageHandler{t: t}
    64  		ts := httptest.NewServer(&g)
    65  		defer ts.Close()
    66  
    67  		discover := ts.URL + "/v2/keys/_etcd/registry/1"
    68  		u, ok := s.PeerHost("ETCDTEST")
    69  		if !ok {
    70  			t.Fatalf("Couldn't find the URL")
    71  		}
    72  		proc, err := startServer([]string{"-discovery", discover, "-peers", u})
    73  
    74  		if err != nil {
    75  			t.Fatal(err.Error())
    76  		}
    77  		defer stopServer(proc)
    78  
    79  		client := http.Client{}
    80  		err = assertServerFunctional(client, "http")
    81  		if err != nil {
    82  			t.Fatal(err.Error())
    83  		}
    84  
    85  		if !g.success {
    86  			t.Fatal("Discovery server never called")
    87  		}
    88  	})
    89  }
    90  
    91  // TestDiscoveryNoWithBackupPeers ensures that etcd runs if it is started with
    92  // no discovery URL and a peer list.
    93  func TestDiscoveryNoWithBackupPeers(t *testing.T) {
    94  	etcdtest.RunServer(func(s *server.Server) {
    95  		u, ok := s.PeerHost("ETCDTEST")
    96  		if !ok {
    97  			t.Fatalf("Couldn't find the URL")
    98  		}
    99  		proc, err := startServer([]string{"-peers", u})
   100  
   101  		if err != nil {
   102  			t.Fatal(err.Error())
   103  		}
   104  		defer stopServer(proc)
   105  
   106  		client := http.Client{}
   107  		err = assertServerFunctional(client, "http")
   108  		if err != nil {
   109  			t.Fatal(err.Error())
   110  		}
   111  	})
   112  }
   113  
   114  // TestDiscoveryDownNoBackupPeersWithDataDir ensures that etcd runs if it is
   115  // started with a bad discovery URL, no backups and valid data dir.
   116  func TestDiscoveryDownNoBackupPeersWithDataDir(t *testing.T) {
   117  	etcdtest.RunServer(func(s *server.Server) {
   118  		u, ok := s.PeerHost("ETCDTEST")
   119  		if !ok {
   120  			t.Fatalf("Couldn't find the URL")
   121  		}
   122  
   123  		// run etcd and connect to ETCDTEST server
   124  		proc, err := startServer([]string{"-peers", u})
   125  		if err != nil {
   126  			t.Fatal(err.Error())
   127  		}
   128  
   129  		// check it runs well
   130  		client := http.Client{}
   131  		err = assertServerFunctional(client, "http")
   132  		if err != nil {
   133  			t.Fatal(err.Error())
   134  		}
   135  
   136  		// stop etcd, and leave valid data dir for later usage
   137  		stopServer(proc)
   138  
   139  		g := garbageHandler{t: t}
   140  		ts := httptest.NewServer(&g)
   141  		defer ts.Close()
   142  
   143  		discover := ts.URL + "/v2/keys/_etcd/registry/1"
   144  		// connect to ETCDTEST server again with previous data dir
   145  		proc, err = startServerWithDataDir([]string{"-discovery", discover})
   146  		if err != nil {
   147  			t.Fatal(err.Error())
   148  		}
   149  		defer stopServer(proc)
   150  
   151  		// TODO(yichengq): it needs some time to do leader election
   152  		// improve to get rid of it
   153  		time.Sleep(1 * time.Second)
   154  
   155  		client = http.Client{}
   156  		err = assertServerFunctional(client, "http")
   157  		if err != nil {
   158  			t.Fatal(err.Error())
   159  		}
   160  
   161  		if !g.success {
   162  			t.Fatal("Discovery server never called")
   163  		}
   164  	})
   165  }
   166  
   167  // TestDiscoveryFirstPeer ensures that etcd starts as the leader if it
   168  // registers as the first peer.
   169  func TestDiscoveryFirstPeer(t *testing.T) {
   170  	etcdtest.RunServer(func(s *server.Server) {
   171  		proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
   172  		if err != nil {
   173  			t.Fatal(err.Error())
   174  		}
   175  		defer stopServer(proc)
   176  
   177  		client := http.Client{}
   178  		err = assertServerFunctional(client, "http")
   179  		if err != nil {
   180  			t.Fatal(err.Error())
   181  		}
   182  	})
   183  }
   184  
   185  // TestDiscoverySecondPeerFirstDown ensures that etcd stops if it is started with a
   186  // correct discovery URL but no active machines are found.
   187  func TestDiscoverySecondPeerFirstDown(t *testing.T) {
   188  	etcdtest.RunServer(func(s *server.Server) {
   189  		v := url.Values{}
   190  		v.Set("value", "started")
   191  		resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/_state"), v)
   192  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   193  
   194  		proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
   195  		if err != nil {
   196  			t.Fatal(err.Error())
   197  		}
   198  		defer stopServer(proc)
   199  
   200  		client := http.Client{}
   201  		err = assertServerNotUp(client, "http")
   202  		if err != nil {
   203  			t.Fatal(err.Error())
   204  		}
   205  	})
   206  }
   207  
   208  // TestDiscoverySecondPeerFirstNoResponse ensures that if the first etcd
   209  // machine stops after heartbeating that the second machine fails too.
   210  func TestDiscoverySecondPeerFirstNoResponse(t *testing.T) {
   211  	etcdtest.RunServer(func(s *server.Server) {
   212  		v := url.Values{}
   213  		v.Set("value", "started")
   214  		resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/_state"), v)
   215  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   216  
   217  		v = url.Values{}
   218  		v.Set("value", "http://127.0.0.1:49151")
   219  		resp, err = etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/ETCDTEST"), v)
   220  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   221  
   222  		proc, err := startServer([]string{"-retry-interval", "0.2", "-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
   223  		if err != nil {
   224  			t.Fatal(err.Error())
   225  		}
   226  		defer stopServer(proc)
   227  
   228  		// TODO(bp): etcd will take 30 seconds to shutdown, figure this
   229  		// out instead
   230  		time.Sleep(1 * time.Second)
   231  
   232  		client := http.Client{}
   233  		_, err = client.Get("/")
   234  		if err != nil && strings.Contains(err.Error(), "connection reset by peer") {
   235  			t.Fatal(err.Error())
   236  		}
   237  	})
   238  }
   239  
   240  // TestDiscoverySecondPeerUp ensures that a second peer joining a discovery
   241  // cluster works.
   242  func TestDiscoverySecondPeerUp(t *testing.T) {
   243  	etcdtest.RunServer(func(s *server.Server) {
   244  		v := url.Values{}
   245  		v.Set("value", "started")
   246  		resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/3/_state"), v)
   247  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   248  
   249  		u, ok := s.PeerURL("ETCDTEST")
   250  		if !ok {
   251  			t.Fatalf("Couldn't find the URL")
   252  		}
   253  
   254  		wc := goetcd.NewClient([]string{s.URL()})
   255  		testResp, err := wc.Set("test", "0", 0)
   256  
   257  		if err != nil {
   258  			t.Fatalf("Couldn't set a test key on the leader %v", err)
   259  		}
   260  
   261  		v = url.Values{}
   262  		v.Set("value", u)
   263  		resp, err = etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/3/ETCDTEST"), v)
   264  		assert.Equal(t, resp.StatusCode, http.StatusCreated)
   265  
   266  		proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/3"})
   267  		if err != nil {
   268  			t.Fatal(err.Error())
   269  		}
   270  		defer stopServer(proc)
   271  
   272  		watch := fmt.Sprintf("%s%s%d", s.URL(), "/v2/keys/_etcd/registry/3/node1?wait=true&waitIndex=", testResp.EtcdIndex)
   273  		resp, err = http.Get(watch)
   274  		if err != nil {
   275  			t.Fatal(err.Error())
   276  		}
   277  
   278  		// TODO(bp): need to have a better way of knowing a machine is up
   279  		for i := 0; i < 10; i++ {
   280  			time.Sleep(1 * time.Second)
   281  
   282  			etcdc := goetcd.NewClient(nil)
   283  			_, err = etcdc.Set("foobar", "baz", 0)
   284  			if err == nil {
   285  				break
   286  			}
   287  		}
   288  
   289  		if err != nil {
   290  			t.Fatal(err.Error())
   291  		}
   292  	})
   293  }
   294  
   295  func assertServerNotUp(client http.Client, scheme string) error {
   296  	path := fmt.Sprintf("%s://127.0.0.1:4001/v2/keys/foo", scheme)
   297  	fields := url.Values(map[string][]string{"value": []string{"bar"}})
   298  
   299  	for i := 0; i < 10; i++ {
   300  		time.Sleep(1 * time.Second)
   301  
   302  		_, err := client.PostForm(path, fields)
   303  		if err == nil {
   304  			return errors.New("Expected error during POST, got nil")
   305  		} else {
   306  			errString := err.Error()
   307  			if strings.Contains(errString, "connection refused") {
   308  				return nil
   309  			} else {
   310  				return err
   311  			}
   312  		}
   313  	}
   314  
   315  	return nil
   316  }