|
|
|
@ -1,17 +1,15 @@
|
|
|
|
|
package cluster
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/tls"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"net"
|
|
|
|
|
"reflect"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/rqlite/rqlite/rtls"
|
|
|
|
|
"github.com/rqlite/rqlite/cluster/servicetest"
|
|
|
|
|
"github.com/rqlite/rqlite/command"
|
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func Test_AddressProviderString(t *testing.T) {
|
|
|
|
@ -37,14 +35,17 @@ func Test_NewBootstrapper(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_BootstrapperBootDoneImmediately(t *testing.T) {
|
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
t.Fatalf("client made HTTP request")
|
|
|
|
|
}))
|
|
|
|
|
srv := servicetest.NewService()
|
|
|
|
|
srv.Handler = func(conn net.Conn) {
|
|
|
|
|
t.Fatalf("client made request")
|
|
|
|
|
}
|
|
|
|
|
srv.Start()
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
done := func() bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
p := NewAddressProviderString([]string{ts.URL})
|
|
|
|
|
p := NewAddressProviderString([]string{srv.Addr()})
|
|
|
|
|
bs := NewBootstrapper(p, nil)
|
|
|
|
|
if err := bs.Boot("node1", "192.168.1.1:1234", done, 10*time.Second); err != nil {
|
|
|
|
|
t.Fatalf("failed to boot: %s", err)
|
|
|
|
@ -55,15 +56,17 @@ func Test_BootstrapperBootDoneImmediately(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_BootstrapperBootTimeout(t *testing.T) {
|
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
|
|
|
}))
|
|
|
|
|
srv := servicetest.NewService()
|
|
|
|
|
srv.Handler = func(conn net.Conn) {
|
|
|
|
|
}
|
|
|
|
|
srv.Start()
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
done := func() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
p := NewAddressProviderString([]string{ts.URL})
|
|
|
|
|
bs := NewBootstrapper(p, nil)
|
|
|
|
|
p := NewAddressProviderString([]string{srv.Addr()})
|
|
|
|
|
bs := NewBootstrapper(p, NewClient(&simpleDialer{}, 0))
|
|
|
|
|
bs.Interval = time.Second
|
|
|
|
|
err := bs.Boot("node1", "192.168.1.1:1234", done, 5*time.Second)
|
|
|
|
|
if err == nil {
|
|
|
|
@ -78,23 +81,45 @@ func Test_BootstrapperBootTimeout(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_BootstrapperBootSingleJoin(t *testing.T) {
|
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.URL.Path != "/join" {
|
|
|
|
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
srv := servicetest.NewService()
|
|
|
|
|
srv.Handler = func(conn net.Conn) {
|
|
|
|
|
var p []byte
|
|
|
|
|
var err error
|
|
|
|
|
c := readCommand(conn)
|
|
|
|
|
if c == nil {
|
|
|
|
|
// Connection error handling
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if c.Type != Command_COMMAND_TYPE_JOIN {
|
|
|
|
|
t.Fatalf("unexpected command type: %d", c.Type)
|
|
|
|
|
}
|
|
|
|
|
jnr := c.GetJoinRequest()
|
|
|
|
|
if jnr == nil {
|
|
|
|
|
t.Fatal("expected join node request, got nil")
|
|
|
|
|
}
|
|
|
|
|
if jnr.Address != "192.168.1.1:1234" {
|
|
|
|
|
t.Fatalf("unexpected node address, got %s", jnr.Address)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}))
|
|
|
|
|
p, err = proto.Marshal(&CommandJoinResponse{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
conn.Close()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
writeBytesWithLength(conn, p)
|
|
|
|
|
}
|
|
|
|
|
srv.Start()
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
done := func() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p := NewAddressProviderString([]string{ts.URL})
|
|
|
|
|
bs := NewBootstrapper(p, nil)
|
|
|
|
|
p := NewAddressProviderString([]string{srv.Addr()})
|
|
|
|
|
bs := NewBootstrapper(p, NewClient(&simpleDialer{}, 0))
|
|
|
|
|
bs.Interval = time.Second
|
|
|
|
|
|
|
|
|
|
err := bs.Boot("node1", "192.168.1.1:1234", done, 60*time.Second)
|
|
|
|
|
err := bs.Boot("node1", "192.168.1.1:1234", done, 5*time.Second)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to boot: %s", err)
|
|
|
|
|
}
|
|
|
|
@ -104,26 +129,31 @@ func Test_BootstrapperBootSingleJoin(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_BootstrapperBootSingleNotify(t *testing.T) {
|
|
|
|
|
tsNotified := false
|
|
|
|
|
var body map[string]string
|
|
|
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.URL.Path == "/join" {
|
|
|
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
|
|
|
var gotNR *command.NotifyRequest
|
|
|
|
|
srv := servicetest.NewService()
|
|
|
|
|
srv.Handler = func(conn net.Conn) {
|
|
|
|
|
var p []byte
|
|
|
|
|
var err error
|
|
|
|
|
c := readCommand(conn)
|
|
|
|
|
if c == nil {
|
|
|
|
|
// Connection error handling
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tsNotified = true
|
|
|
|
|
b, err := io.ReadAll(r.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
if c.Type != Command_COMMAND_TYPE_NOTIFY {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
gotNR = c.GetNotifyRequest()
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal(b, &body); err != nil {
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
p, err = proto.Marshal(&CommandNotifyResponse{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
conn.Close()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
writeBytesWithLength(conn, p)
|
|
|
|
|
}
|
|
|
|
|
srv.Start()
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
n := -1
|
|
|
|
|
done := func() bool {
|
|
|
|
@ -131,8 +161,8 @@ func Test_BootstrapperBootSingleNotify(t *testing.T) {
|
|
|
|
|
return n == 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p := NewAddressProviderString([]string{ts.URL})
|
|
|
|
|
bs := NewBootstrapper(p, nil)
|
|
|
|
|
p := NewAddressProviderString([]string{srv.Addr()})
|
|
|
|
|
bs := NewBootstrapper(p, NewClient(&simpleDialer{}, 0))
|
|
|
|
|
bs.Interval = time.Second
|
|
|
|
|
|
|
|
|
|
err := bs.Boot("node1", "192.168.1.1:1234", done, 60*time.Second)
|
|
|
|
@ -140,108 +170,79 @@ func Test_BootstrapperBootSingleNotify(t *testing.T) {
|
|
|
|
|
t.Fatalf("failed to boot: %s", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tsNotified != true {
|
|
|
|
|
t.Fatalf("notify target not contacted")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if got, exp := body["id"], "node1"; got != exp {
|
|
|
|
|
if got, exp := gotNR.Id, "node1"; got != exp {
|
|
|
|
|
t.Fatalf("wrong node ID supplied, exp %s, got %s", exp, got)
|
|
|
|
|
}
|
|
|
|
|
if got, exp := body["addr"], "192.168.1.1:1234"; got != exp {
|
|
|
|
|
if got, exp := gotNR.Address, "192.168.1.1:1234"; got != exp {
|
|
|
|
|
t.Fatalf("wrong address supplied, exp %s, got %s", exp, got)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if exp, got := BootDone, bs.Status(); exp != got {
|
|
|
|
|
t.Fatalf("wrong status, exp %s, got %s", exp, got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_BootstrapperBootSingleNotifyHTTPS(t *testing.T) {
|
|
|
|
|
tsNotified := false
|
|
|
|
|
var body map[string]string
|
|
|
|
|
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.URL.Path == "/join" {
|
|
|
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
|
|
|
func Test_BootstrapperBootMultiJoinNotify(t *testing.T) {
|
|
|
|
|
srv1Join := false
|
|
|
|
|
srv1Notified := false
|
|
|
|
|
srv1 := servicetest.NewService()
|
|
|
|
|
srv1.Handler = func(conn net.Conn) {
|
|
|
|
|
var p []byte
|
|
|
|
|
var err error
|
|
|
|
|
c := readCommand(conn)
|
|
|
|
|
if c == nil {
|
|
|
|
|
// Connection error handling
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if r.URL.Path != "/notify" {
|
|
|
|
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
|
|
|
|
}
|
|
|
|
|
tsNotified = true
|
|
|
|
|
b, err := io.ReadAll(r.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
if c.Type == Command_COMMAND_TYPE_JOIN {
|
|
|
|
|
srv1Join = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal(b, &body); err != nil {
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
if c.Type != Command_COMMAND_TYPE_NOTIFY {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
defer ts.Close()
|
|
|
|
|
ts.TLS = &tls.Config{NextProtos: []string{"h2", "http/1.1"}}
|
|
|
|
|
ts.StartTLS()
|
|
|
|
|
srv1Notified = true
|
|
|
|
|
|
|
|
|
|
n := -1
|
|
|
|
|
done := func() bool {
|
|
|
|
|
n++
|
|
|
|
|
return n == 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tlsConfig, err := rtls.CreateClientConfig("", "", "", true)
|
|
|
|
|
p, err = proto.Marshal(&CommandNotifyResponse{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create TLS config: %s", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p := NewAddressProviderString([]string{ts.URL})
|
|
|
|
|
bs := NewBootstrapper(p, tlsConfig)
|
|
|
|
|
bs.Interval = time.Second
|
|
|
|
|
|
|
|
|
|
err = bs.Boot("node1", "192.168.1.1:1234", done, 60*time.Second)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to boot: %s", err)
|
|
|
|
|
conn.Close()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tsNotified != true {
|
|
|
|
|
t.Fatalf("notify target not contacted")
|
|
|
|
|
writeBytesWithLength(conn, p)
|
|
|
|
|
}
|
|
|
|
|
srv1.Start()
|
|
|
|
|
defer srv1.Close()
|
|
|
|
|
|
|
|
|
|
if got, exp := body["id"], "node1"; got != exp {
|
|
|
|
|
t.Fatalf("wrong node ID supplied, exp %s, got %s", exp, got)
|
|
|
|
|
}
|
|
|
|
|
if got, exp := body["addr"], "192.168.1.1:1234"; got != exp {
|
|
|
|
|
t.Fatalf("wrong address supplied, exp %s, got %s", exp, got)
|
|
|
|
|
srv2Join := false
|
|
|
|
|
srv2Notified := false
|
|
|
|
|
srv2 := servicetest.NewService()
|
|
|
|
|
srv2.Handler = func(conn net.Conn) {
|
|
|
|
|
var p []byte
|
|
|
|
|
var err error
|
|
|
|
|
c := readCommand(conn)
|
|
|
|
|
if c == nil {
|
|
|
|
|
// Connection error handling
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if exp, got := BootDone, bs.Status(); exp != got {
|
|
|
|
|
t.Fatalf("wrong status, exp %s, got %s", exp, got)
|
|
|
|
|
}
|
|
|
|
|
if c.Type == Command_COMMAND_TYPE_JOIN {
|
|
|
|
|
srv2Join = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Test_BootstrapperBootMultiNotify(t *testing.T) {
|
|
|
|
|
ts1Join := false
|
|
|
|
|
ts1Notified := false
|
|
|
|
|
ts1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.URL.Path == "/join" {
|
|
|
|
|
ts1Join = true
|
|
|
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
|
|
|
if c.Type != Command_COMMAND_TYPE_NOTIFY {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ts1Notified = true
|
|
|
|
|
}))
|
|
|
|
|
srv2Notified = true
|
|
|
|
|
|
|
|
|
|
ts2Join := false
|
|
|
|
|
ts2Notified := false
|
|
|
|
|
ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
if r.URL.Path == "/join" {
|
|
|
|
|
ts2Join = true
|
|
|
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
|
|
|
p, err = proto.Marshal(&CommandNotifyResponse{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
conn.Close()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ts2Notified = true
|
|
|
|
|
}))
|
|
|
|
|
writeBytesWithLength(conn, p)
|
|
|
|
|
}
|
|
|
|
|
srv2.Start()
|
|
|
|
|
defer srv2.Close()
|
|
|
|
|
|
|
|
|
|
n := -1
|
|
|
|
|
done := func() bool {
|
|
|
|
@ -249,8 +250,8 @@ func Test_BootstrapperBootMultiNotify(t *testing.T) {
|
|
|
|
|
return n == 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p := NewAddressProviderString([]string{ts1.URL, ts2.URL})
|
|
|
|
|
bs := NewBootstrapper(p, nil)
|
|
|
|
|
p := NewAddressProviderString([]string{srv1.Addr(), srv2.Addr()})
|
|
|
|
|
bs := NewBootstrapper(p, NewClient(&simpleDialer{}, 0))
|
|
|
|
|
bs.Interval = time.Second
|
|
|
|
|
|
|
|
|
|
err := bs.Boot("node1", "192.168.1.1:1234", done, 60*time.Second)
|
|
|
|
@ -258,10 +259,10 @@ func Test_BootstrapperBootMultiNotify(t *testing.T) {
|
|
|
|
|
t.Fatalf("failed to boot: %s", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ts1Join != true || ts2Join != true {
|
|
|
|
|
if srv1Join != true || srv2Join != true {
|
|
|
|
|
t.Fatalf("all join targets not contacted")
|
|
|
|
|
}
|
|
|
|
|
if ts1Notified != true || ts2Notified != true {
|
|
|
|
|
if srv1Notified != true || srv2Notified != true {
|
|
|
|
|
t.Fatalf("all notify targets not contacted")
|
|
|
|
|
}
|
|
|
|
|
if exp, got := BootDone, bs.Status(); exp != got {
|
|
|
|
|