Example #1
0
func TestHTTPMembersAPIAddSuccess(t *testing.T) {
	wantAction := &membersAPIActionAdd{
		peerURLs: types.URLs([]url.URL{
			{Scheme: "http", Host: "127.0.0.1:7002"},
		}),
	}

	mAPI := &httpMembersAPI{
		client: &actionAssertingHTTPClient{
			t:   t,
			act: wantAction,
			resp: http.Response{
				StatusCode: http.StatusCreated,
			},
			body: []byte(`{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"]}`),
		},
	}

	wantResponseMember := &Member{
		ID:       "94088180e21eb87b",
		PeerURLs: []string{"http://127.0.0.1:7002"},
	}

	m, err := mAPI.Add(context.Background(), "http://127.0.0.1:7002")
	if err != nil {
		t.Errorf("got non-nil err: %#v", err)
	}
	if !reflect.DeepEqual(wantResponseMember, m) {
		t.Errorf("incorrect Member: want=%#v got=%#v", wantResponseMember, m)
	}
}
Example #2
0
func TestMembersAPIActionAdd(t *testing.T) {
	ep := url.URL{Scheme: "http", Host: "example.com"}
	act := &membersAPIActionAdd{
		peerURLs: types.URLs([]url.URL{
			url.URL{Scheme: "https", Host: "127.0.0.1:8081"},
			url.URL{Scheme: "http", Host: "127.0.0.1:8080"},
		}),
	}

	wantURL := &url.URL{
		Scheme: "http",
		Host:   "example.com",
		Path:   "/v2/members",
	}
	wantHeader := http.Header{
		"Content-Type": []string{"application/json"},
	}
	wantBody := []byte(`{"peerURLs":["https://127.0.0.1:8081","http://127.0.0.1:8080"]}`)

	got := *act.HTTPRequest(ep)
	err := assertResponse(got, wantURL, wantHeader, wantBody)
	if err != nil {
		t.Error(err.Error())
	}
}
// urlsEqual checks equality of url.URLS between two arrays.
// This check pass even if an URL is in hostname and opposite is in IP address.
func urlsEqual(a []url.URL, b []url.URL) bool {
	if len(a) != len(b) {
		return false
	}
	urls, err := ResolveTCPAddrs([][]url.URL{a, b})
	if err != nil {
		return false
	}
	a, b = urls[0], urls[1]
	sort.Sort(types.URLs(a))
	sort.Sort(types.URLs(b))
	for i := range a {
		if !reflect.DeepEqual(a[i], b[i]) {
			return false
		}
	}

	return true
}
Example #4
0
func TestTransportUpdate(t *testing.T) {
	peer := newFakePeer()
	tr := &transport{
		peers: map[types.ID]Peer{types.ID(1): peer},
	}
	u := "http://localhost:2380"
	tr.UpdatePeer(types.ID(1), []string{u})
	wurls := types.URLs(testutil.MustNewURLs(t, []string{"http://localhost:2380"}))
	if !reflect.DeepEqual(peer.urls, wurls) {
		t.Errorf("urls = %+v, want %+v", peer.urls, wurls)
	}
}
Example #5
0
func TestMemberCreateRequestMarshal(t *testing.T) {
	req := memberCreateOrUpdateRequest{
		PeerURLs: types.URLs([]url.URL{
			{Scheme: "http", Host: "127.0.0.1:8081"},
			{Scheme: "https", Host: "127.0.0.1:8080"},
		}),
	}
	want := []byte(`{"peerURLs":["http://127.0.0.1:8081","https://127.0.0.1:8080"]}`)

	got, err := json.Marshal(&req)
	if err != nil {
		t.Fatalf("Marshal returned unexpected err=%v", err)
	}

	if !reflect.DeepEqual(want, got) {
		t.Fatalf("Failed to marshal memberCreateRequest: want=%s, got=%s", want, got)
	}
}
Example #6
0
func TestMemberCreateRequestUnmarshal(t *testing.T) {
	body := []byte(`{"peerURLs": ["http://127.0.0.1:8081", "https://127.0.0.1:8080"]}`)
	want := MemberCreateRequest{
		PeerURLs: types.URLs([]url.URL{
			url.URL{Scheme: "http", Host: "127.0.0.1:8081"},
			url.URL{Scheme: "https", Host: "127.0.0.1:8080"},
		}),
	}

	var req MemberCreateRequest
	if err := json.Unmarshal(body, &req); err != nil {
		t.Fatalf("Unmarshal returned unexpected err=%v", err)
	}

	if !reflect.DeepEqual(want, req) {
		t.Fatalf("Failed to unmarshal MemberCreateRequest: want=%#v, got=%#v", want, req)
	}
}
Example #7
0
// Set parses command line sets of names to IPs formatted like:
// mach0=http://1.1.1.1,mach0=http://2.2.2.2,mach0=http://1.1.1.1,mach1=http://2.2.2.2,mach1=http://3.3.3.3
func (c *Cluster) Set(s string) error {
	*c = Cluster{}
	v, err := url.ParseQuery(strings.Replace(s, ",", "&", -1))
	if err != nil {
		return err
	}

	for name, urls := range v {
		if len(urls) == 0 || urls[0] == "" {
			return fmt.Errorf("Empty URL given for %q", name)
		}

		m := newMember(name, types.URLs(*flags.NewURLsValue(strings.Join(urls, ","))), nil)
		err := c.Add(*m)
		if err != nil {
			return err
		}
	}
	return nil
}
Example #8
0
// NewClusterFromString returns a Cluster instantiated from the given cluster token
// and cluster string, by parsing members from a set of discovery-formatted
// names-to-IPs, like:
// mach0=http://1.1.1.1,mach0=http://2.2.2.2,mach1=http://3.3.3.3,mach2=http://4.4.4.4
func NewClusterFromString(token string, cluster string) (*Cluster, error) {
	c := newCluster(token)

	v, err := url.ParseQuery(strings.Replace(cluster, ",", "&", -1))
	if err != nil {
		return nil, err
	}
	for name, urls := range v {
		if len(urls) == 0 || urls[0] == "" {
			return nil, fmt.Errorf("Empty URL given for %q", name)
		}
		purls := &flags.URLsValue{}
		if err := purls.Set(strings.Join(urls, ",")); err != nil {
			return nil, err
		}
		m := NewMember(name, types.URLs(*purls), c.token, nil)
		if _, ok := c.members[m.ID]; ok {
			return nil, fmt.Errorf("Member exists with identical ID %v", m)
		}
		c.members[m.ID] = m
	}
	c.genID()
	return c, nil
}
Example #9
0
File: etcd.go Project: utahcon/etcd
func Main() {
	cfg := NewConfig()
	err := cfg.Parse(os.Args[1:])
	if err != nil {
		plog.Errorf("error verifying flags, %v. See 'etcd --help'.", err)
		switch err {
		case errUnsetAdvertiseClientURLsFlag:
			plog.Errorf("When listening on specific address(es), this etcd process must advertise accessible url(s) to each connected client.")
		}
		os.Exit(1)
	}
	setupLogging(cfg)

	var stopped <-chan struct{}

	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())

	// TODO: check whether fields are set instead of whether fields have default value
	if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
		cfg.initialCluster = initialClusterFromName(cfg.name)
	}

	if cfg.dir == "" {
		cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
		plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.dir)
	}

	which := identifyDataDirOrDie(cfg.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, err = startEtcd(cfg)
		case dirProxy:
			err = startProxy(cfg)
		default:
			plog.Panicf("unhandled dir type %v", which)
		}
	} else {
		shouldProxy := cfg.isProxy()
		if !shouldProxy {
			stopped, err = startEtcd(cfg)
			if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
				plog.Noticef("discovery cluster full, falling back to %s", fallbackFlagProxy)
				shouldProxy = true
			}
		}
		if shouldProxy {
			err = startProxy(cfg)
		}
	}

	if err != nil {
		switch err {
		case discovery.ErrDuplicateID:
			plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.name, cfg.durl)
			plog.Errorf("But etcd could not find valid cluster configuration in the given data dir (%s).", cfg.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.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:
			if strings.Contains(err.Error(), "include") && strings.Contains(err.Error(), "--initial-cluster") {
				plog.Infof("%v", err)
				if cfg.initialCluster == initialClusterFromName(cfg.name) {
					plog.Infof("forgot to set --initial-cluster flag?")
				}
				if types.URLs(cfg.apurls).String() == defaultInitialAdvertisePeerURLs {
					plog.Infof("forgot to set --initial-advertise-peer-urls flag?")
				}
				if cfg.initialCluster == initialClusterFromName(cfg.name) && len(cfg.durl) == 0 {
					plog.Infof("if you want to use discovery service, please set --discovery flag.")
				}
				os.Exit(1)
			}
			if etcdserver.IsDiscoveryError(err) {
				plog.Errorf("%v", err)
				plog.Infof("discovery token %s was used, but failed to bootstrap the cluster.", cfg.durl)
				plog.Infof("please generate a new discovery token and try to bootstrap again.")
				os.Exit(1)
			}
			plog.Fatalf("%v", err)
		}
		os.Exit(1)
	}

	osutil.HandleInterrupts()

	if systemdutil.IsRunningSystemd() {
		// At this point, the initialization of etcd is done.
		// The listeners are listening on the TCP ports and ready
		// for accepting connections.
		// The http server is probably ready for serving incoming
		// connections. If it is not, the connection might be pending
		// for less than one second.
		err := daemon.SdNotify("READY=1")
		if err != nil {
			plog.Errorf("failed to notify systemd for readiness")
		}
	}

	<-stopped
	osutil.Exit(0)
}