diff --git a/cluster/join.go b/cluster/join.go index 4e348c7a..b95fff99 100644 --- a/cluster/join.go +++ b/cluster/join.go @@ -16,14 +16,12 @@ import ( httpd "github.com/rqlite/rqlite/http" ) -const numAttempts int = 3 -const attemptInterval time.Duration = 5 * time.Second - // Join attempts to join the cluster at one of the addresses given in joinAddr. // It walks through joinAddr in order, and sets the node ID and Raft address of // the joining node as id addr respectively. It returns the endpoint successfully // used to join the cluster. -func Join(joinAddr []string, id, addr string, voter bool, meta map[string]string, tlsConfig *tls.Config) (string, error) { +func Join(joinAddr []string, id, addr string, voter bool, meta map[string]string, numAttempts int, + attemptInterval time.Duration, tlsConfig *tls.Config) (string, error) { var err error var j string logger := log.New(os.Stderr, "[cluster-join] ", log.LstdFlags) diff --git a/cluster/join_test.go b/cluster/join_test.go index 84732742..8dc266f8 100644 --- a/cluster/join_test.go +++ b/cluster/join_test.go @@ -7,8 +7,12 @@ import ( "net/http" "net/http/httptest" "testing" + "time" ) +const numAttempts int = 3 +const attemptInterval time.Duration = 5 * time.Second + func Test_SingleJoinOK(t *testing.T) { var body map[string]interface{} ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -31,7 +35,8 @@ func Test_SingleJoinOK(t *testing.T) { defer ts.Close() - j, err := Join([]string{ts.URL}, "id0", "127.0.0.1:9090", false, nil, nil) + j, err := Join([]string{ts.URL}, "id0", "127.0.0.1:9090", false, nil, + numAttempts, attemptInterval, nil) if err != nil { t.Fatalf("failed to join a single node: %s", err.Error()) } @@ -74,7 +79,8 @@ func Test_SingleJoinMetaOK(t *testing.T) { nodeAddr := "127.0.0.1:9090" md := map[string]string{"foo": "bar"} - j, err := Join([]string{ts.URL}, "id0", nodeAddr, true, md, nil) + j, err := Join([]string{ts.URL}, "id0", nodeAddr, true, md, + numAttempts, attemptInterval, nil) if err != nil { t.Fatalf("failed to join a single node: %s", err.Error()) } @@ -100,7 +106,8 @@ func Test_SingleJoinFail(t *testing.T) { })) defer ts.Close() - _, err := Join([]string{ts.URL}, "id0", "127.0.0.1:9090", true, nil, nil) + _, err := Join([]string{ts.URL}, "id0", "127.0.0.1:9090", true, nil, + numAttempts, attemptInterval, nil) if err == nil { t.Fatalf("expected error when joining bad node") } @@ -114,7 +121,8 @@ func Test_DoubleJoinOK(t *testing.T) { })) defer ts2.Close() - j, err := Join([]string{ts1.URL, ts2.URL}, "id0", "127.0.0.1:9090", true, nil, nil) + j, err := Join([]string{ts1.URL, ts2.URL}, "id0", "127.0.0.1:9090", true, nil, + numAttempts, attemptInterval, nil) if err != nil { t.Fatalf("failed to join a single node: %s", err.Error()) } @@ -132,7 +140,8 @@ func Test_DoubleJoinOKSecondNode(t *testing.T) { })) defer ts2.Close() - j, err := Join([]string{ts1.URL, ts2.URL}, "id0", "127.0.0.1:9090", true, nil, nil) + j, err := Join([]string{ts1.URL, ts2.URL}, "id0", "127.0.0.1:9090", true, nil, + numAttempts, attemptInterval, nil) if err != nil { t.Fatalf("failed to join a single node: %s", err.Error()) } @@ -152,7 +161,8 @@ func Test_DoubleJoinOKSecondNodeRedirect(t *testing.T) { })) defer ts2.Close() - j, err := Join([]string{ts2.URL}, "id0", "127.0.0.1:9090", true, nil, nil) + j, err := Join([]string{ts2.URL}, "id0", "127.0.0.1:9090", true, nil, + numAttempts, attemptInterval, nil) if err != nil { t.Fatalf("failed to join a single node: %s", err.Error()) } diff --git a/cmd/rqlited/main.go b/cmd/rqlited/main.go index 664a728d..c30b1902 100644 --- a/cmd/rqlited/main.go +++ b/cmd/rqlited/main.go @@ -58,6 +58,8 @@ var nodeID string var raftAddr string var raftAdv string var joinAddr string +var joinAttempts int +var joinInterval string var noVerify bool var noNodeVerify bool var discoURL string @@ -100,6 +102,8 @@ func init() { flag.StringVar(&raftAddr, "raft-addr", "localhost:4002", "Raft communication bind address") flag.StringVar(&raftAdv, "raft-adv-addr", "", "Advertised Raft communication address. If not set, same as Raft bind") flag.StringVar(&joinAddr, "join", "", "Comma-delimited list of nodes, through which a cluster can be joined (proto://host:port)") + flag.IntVar(&joinAttempts, "join-attempts", 5, "Number of join attempts to make") + flag.StringVar(&joinInterval, "join-interval", "5s", "Period between join attempts") flag.StringVar(&discoURL, "disco-url", "http://discovery.rqlite.com", "Set Discovery Service URL") flag.StringVar(&discoID, "disco-id", "", "Set Discovery ID. If not set, Discovery Service not used") flag.BoolVar(&expvar, "expvar", true, "Serve expvar data on HTTP server") @@ -239,6 +243,11 @@ func main() { advAddr = raftAdv } + joinDur, err := time.ParseDuration(joinInterval) + if err != nil { + log.Fatalf("failed to parse Join interval %s: %s", joinInterval, err.Error()) + } + tlsConfig := tls.Config{InsecureSkipVerify: noVerify} if x509CACert != "" { asn1Data, err := ioutil.ReadFile(x509CACert) @@ -252,7 +261,8 @@ func main() { } } - if j, err := cluster.Join(joins, str.ID(), advAddr, !raftNonVoter, meta, &tlsConfig); err != nil { + if j, err := cluster.Join(joins, str.ID(), advAddr, !raftNonVoter, meta, + joinAttempts, joinDur, &tlsConfig); err != nil { log.Fatalf("failed to join cluster at %s: %s", joins, err.Error()) } else { log.Println("successfully joined cluster at", j)