1442 lines
35 KiB
Go
1442 lines
35 KiB
Go
package meta_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"path"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/influxdata/influxdb"
|
|
|
|
"github.com/influxdata/influxdb/influxql"
|
|
"github.com/influxdata/influxdb/services/meta"
|
|
"github.com/influxdata/influxdb/tcp"
|
|
"github.com/influxdata/influxdb/toml"
|
|
)
|
|
|
|
func TestMetaService_CreateDatabase(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
// Make sure a default retention policy was created.
|
|
_, err = c.RetentionPolicy("db0", "default")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.DefaultRetentionPolicy != "default" {
|
|
t.Fatalf("rp name wrong: %s", db.DefaultRetentionPolicy)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_CreateDatabaseIfNotExists(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
qry := `CREATE DATABASE IF NOT EXISTS db0`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_CreateDatabaseWithRetentionPolicy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
qry := `CREATE DATABASE db0 WITH DURATION 1h REPLICATION 1 NAME rp0`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
rp := db.RetentionPolicy("rp0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if rp.Name != "rp0" {
|
|
t.Fatalf("rp name wrong: %s", rp.Name)
|
|
} else if rp.Duration != time.Hour {
|
|
t.Fatalf("rp duration wrong: %s", rp.Duration.String())
|
|
} else if rp.ReplicaN != 1 {
|
|
t.Fatalf("rp replication wrong: %d", rp.ReplicaN)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_Databases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
// Create two databases.
|
|
db, err := c.CreateDatabase("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
db, err = c.CreateDatabase("db1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db1" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
dbs, err := c.Databases()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(dbs) != 2 {
|
|
t.Fatalf("expected 2 databases but got %d", len(dbs))
|
|
} else if dbs[0].Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", dbs[0].Name)
|
|
} else if dbs[1].Name != "db1" {
|
|
t.Fatalf("db name wrong: %s", dbs[1].Name)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_DropDatabase(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
qry := `CREATE DATABASE db0`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
qry = `DROP DATABASE db0`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
if db, _ = c.Database("db0"); db != nil {
|
|
t.Fatalf("expected database to not return: %v", db)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_CreateRetentionPolicy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
qry := `CREATE RETENTION POLICY rp0 ON db0 DURATION 1h REPLICATION 1`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
rp, err := c.RetentionPolicy("db0", "rp0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if rp.Name != "rp0" {
|
|
t.Fatalf("rp name wrong: %s", rp.Name)
|
|
} else if rp.Duration != time.Hour {
|
|
t.Fatalf("rp duration wrong: %s", rp.Duration.String())
|
|
} else if rp.ReplicaN != 1 {
|
|
t.Fatalf("rp replication wrong: %d", rp.ReplicaN)
|
|
}
|
|
|
|
// Create the same policy. Should not error.
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
rp, err = c.RetentionPolicy("db0", "rp0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if rp.Name != "rp0" {
|
|
t.Fatalf("rp name wrong: %s", rp.Name)
|
|
} else if rp.Duration != time.Hour {
|
|
t.Fatalf("rp duration wrong: %s", rp.Duration.String())
|
|
} else if rp.ReplicaN != 1 {
|
|
t.Fatalf("rp replication wrong: %d", rp.ReplicaN)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_SetDefaultRetentionPolicy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
qry := `CREATE DATABASE db0 WITH DURATION 1h REPLICATION 1 NAME rp0`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
rp, err := c.RetentionPolicy("db0", "rp0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if rp.Name != "rp0" {
|
|
t.Fatalf("rp name wrong: %s", rp.Name)
|
|
} else if rp.Duration != time.Hour {
|
|
t.Fatalf("rp duration wrong: %s", rp.Duration.String())
|
|
} else if rp.ReplicaN != 1 {
|
|
t.Fatalf("rp replication wrong: %d", rp.ReplicaN)
|
|
}
|
|
|
|
// Make sure default retention policy is now rp0
|
|
if db.DefaultRetentionPolicy != "rp0" {
|
|
t.Fatalf("rp name wrong: %s", db.DefaultRetentionPolicy)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_DropRetentionPolicy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
qry := `CREATE RETENTION POLICY rp0 ON db0 DURATION 1h REPLICATION 1`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
rp, err := c.RetentionPolicy("db0", "rp0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if rp.Name != "rp0" {
|
|
t.Fatalf("rp name wrong: %s", rp.Name)
|
|
} else if rp.Duration != time.Hour {
|
|
t.Fatalf("rp duration wrong: %s", rp.Duration.String())
|
|
} else if rp.ReplicaN != 1 {
|
|
t.Fatalf("rp replication wrong: %d", rp.ReplicaN)
|
|
}
|
|
|
|
qry = `DROP RETENTION POLICY rp0 ON db0`
|
|
if res := c.ExecuteStatement(mustParseStatement(qry)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
rp, err = c.RetentionPolicy("db0", "rp0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if rp != nil {
|
|
t.Fatalf("rp should have been dropped")
|
|
}
|
|
}
|
|
|
|
func TestMetaService_CreateUser(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
// Create an admin user
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE USER fred WITH PASSWORD 'supersecure' WITH ALL PRIVILEGES")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
// Create a non-admin user
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE USER wilma WITH PASSWORD 'password'")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
u, err := c.User("fred")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if exp, got := "fred", u.Name; exp != got {
|
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
|
}
|
|
if !u.Admin {
|
|
t.Fatalf("expected user to be admin")
|
|
}
|
|
|
|
u, err = c.Authenticate("fred", "supersecure")
|
|
if u == nil || err != nil || u.Name != "fred" {
|
|
t.Fatalf("failed to authenticate")
|
|
}
|
|
|
|
// Auth for bad password should fail
|
|
u, err = c.Authenticate("fred", "badpassword")
|
|
if u != nil || err != meta.ErrAuthenticate {
|
|
t.Fatalf("authentication should fail with %s", meta.ErrAuthenticate)
|
|
}
|
|
|
|
// Auth for no password should fail
|
|
u, err = c.Authenticate("fred", "")
|
|
if u != nil || err != meta.ErrAuthenticate {
|
|
t.Fatalf("authentication should fail with %s", meta.ErrAuthenticate)
|
|
}
|
|
|
|
// Change password should succeed.
|
|
if res := c.ExecuteStatement(mustParseStatement("SET PASSWORD FOR fred = 'moresupersecure'")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
// Auth for old password should fail
|
|
u, err = c.Authenticate("fred", "supersecure")
|
|
if u != nil || err != meta.ErrAuthenticate {
|
|
t.Fatalf("authentication should fail with %s", meta.ErrAuthenticate)
|
|
}
|
|
|
|
// Auth for new password should succeed.
|
|
u, err = c.Authenticate("fred", "moresupersecure")
|
|
if u == nil || err != nil || u.Name != "fred" {
|
|
t.Fatalf("failed to authenticate")
|
|
}
|
|
|
|
// Auth for unkonwn user should fail
|
|
u, err = c.Authenticate("foo", "")
|
|
if u != nil || err != meta.ErrUserNotFound {
|
|
t.Fatalf("authentication should fail with %s", meta.ErrUserNotFound)
|
|
}
|
|
|
|
u, err = c.User("wilma")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if exp, got := "wilma", u.Name; exp != got {
|
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
|
}
|
|
if u.Admin {
|
|
t.Fatalf("expected user not to be an admin")
|
|
}
|
|
|
|
if exp, got := 2, c.UserCount(); exp != got {
|
|
t.Fatalf("unexpected user count. got: %d exp: %d", got, exp)
|
|
}
|
|
|
|
// Grant privilidges to a non-admin user
|
|
if res := c.ExecuteStatement(mustParseStatement("GRANT ALL PRIVILEGES TO wilma")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
u, err = c.User("wilma")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if exp, got := "wilma", u.Name; exp != got {
|
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
|
}
|
|
if !u.Admin {
|
|
t.Fatalf("expected user to be an admin")
|
|
}
|
|
|
|
// Revoke privilidges from user
|
|
if res := c.ExecuteStatement(mustParseStatement("REVOKE ALL PRIVILEGES FROM wilma")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
u, err = c.User("wilma")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if exp, got := "wilma", u.Name; exp != got {
|
|
t.Fatalf("unexpected user name: exp: %s got: %s", exp, got)
|
|
}
|
|
if u.Admin {
|
|
t.Fatalf("expected user not to be an admin")
|
|
}
|
|
|
|
// Create a database to use for assiging privileges to.
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
// Assign a single privilege at the database level
|
|
if res := c.ExecuteStatement(mustParseStatement("GRANT READ ON db0 TO wilma")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
p, err := c.UserPrivilege("wilma", "db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if p == nil {
|
|
t.Fatal("expected privilege but was nil")
|
|
}
|
|
if exp, got := influxql.ReadPrivilege, *p; exp != got {
|
|
t.Fatalf("unexpected privilege. exp: %d, got: %d", exp, got)
|
|
}
|
|
|
|
// Remove a single privilege at the database level
|
|
if res := c.ExecuteStatement(mustParseStatement("REVOKE READ ON db0 FROM wilma")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
p, err = c.UserPrivilege("wilma", "db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if p == nil {
|
|
t.Fatal("expected privilege but was nil")
|
|
}
|
|
if exp, got := influxql.NoPrivileges, *p; exp != got {
|
|
t.Fatalf("unexpected privilege. exp: %d, got: %d", exp, got)
|
|
}
|
|
|
|
// Drop a user
|
|
if res := c.ExecuteStatement(mustParseStatement("DROP USER wilma")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
u, err = c.User("wilma")
|
|
if err != meta.ErrUserNotFound {
|
|
t.Fatalf("user lookup should fail with %s", meta.ErrUserNotFound)
|
|
}
|
|
|
|
if exp, got := 1, c.UserCount(); exp != got {
|
|
t.Fatalf("unexpected user count. got: %d exp: %d", got, exp)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_ContinuousQueries(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
// Create a database to use
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
// Create a CQ
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE CONTINUOUS QUERY cq0 ON db0 BEGIN SELECT count(value) INTO foo_count FROM foo GROUP BY time(10m) END")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
res := c.ExecuteStatement(mustParseStatement("SHOW CONTINUOUS QUERIES"))
|
|
if res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
exp := `{"series":[{"name":"db0","columns":["name","query"],"values":[["cq0","CREATE CONTINUOUS QUERY cq0 ON db0 BEGIN SELECT count(value) INTO foo_count FROM foo GROUP BY time(10m) END"]]}]}`
|
|
got := mustMarshalJSON(res)
|
|
if exp != got {
|
|
t.Fatalf("unexpected response.\n\nexp: %s\ngot: %s\n", exp, got)
|
|
}
|
|
|
|
// Recreate an existing CQ
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE CONTINUOUS QUERY cq0 ON db0 BEGIN SELECT max(value) INTO foo_max FROM foo GROUP BY time(10m) END")); res.Err == nil {
|
|
t.Fatalf("expected error: got %v", res.Err)
|
|
}
|
|
|
|
// Create a few more CQ's
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE CONTINUOUS QUERY cq1 ON db0 BEGIN SELECT max(value) INTO foo_max FROM foo GROUP BY time(10m) END")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE CONTINUOUS QUERY cq2 ON db0 BEGIN SELECT min(value) INTO foo_min FROM foo GROUP BY time(10m) END")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
// Drop a single CQ
|
|
if res := c.ExecuteStatement(mustParseStatement("DROP CONTINUOUS QUERY cq1 ON db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
res = c.ExecuteStatement(mustParseStatement("SHOW CONTINUOUS QUERIES"))
|
|
if res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
exp = `{"series":[{"name":"db0","columns":["name","query"],"values":[["cq0","CREATE CONTINUOUS QUERY cq0 ON db0 BEGIN SELECT count(value) INTO foo_count FROM foo GROUP BY time(10m) END"],["cq2","CREATE CONTINUOUS QUERY cq2 ON db0 BEGIN SELECT min(value) INTO foo_min FROM foo GROUP BY time(10m) END"]]}]}`
|
|
got = mustMarshalJSON(res)
|
|
if exp != got {
|
|
t.Fatalf("unexpected response.\n\nexp: %s\ngot: %s\n", exp, got)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_Subscriptions_Create(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
// Create a database to use
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
// Create a subscription
|
|
if res := c.ExecuteStatement(mustParseStatement(`CREATE SUBSCRIPTION sub0 ON db0."default" DESTINATIONS ALL 'udp://example.com:9090'`)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
// Re-create a subscription
|
|
if res := c.ExecuteStatement(mustParseStatement(`CREATE SUBSCRIPTION sub0 ON db0."default" DESTINATIONS ALL 'udp://example.com:9090'`)); res.Err == nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
res := c.ExecuteStatement(mustParseStatement(`SHOW SUBSCRIPTIONS`))
|
|
if res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
} else if got, exp := len(res.Series), 1; got != exp {
|
|
t.Fatalf("unexpected response.\n\ngot: %d series\nexp: %d\n", got, exp)
|
|
}
|
|
|
|
// Create another subscription.
|
|
if res := c.ExecuteStatement(mustParseStatement(`CREATE SUBSCRIPTION sub1 ON db0."default" DESTINATIONS ALL 'udp://example.com:6060'`)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
// The subscriptions are correctly created.
|
|
if res = c.ExecuteStatement(mustParseStatement(`SHOW SUBSCRIPTIONS`)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
exp := `{"series":[{"name":"db0","columns":["retention_policy","name","mode","destinations"],"values":[["default","sub0","ALL",["udp://example.com:9090"]],["default","sub1","ALL",["udp://example.com:6060"]]]}]}`
|
|
got := mustMarshalJSON(res)
|
|
if got != exp {
|
|
t.Fatalf("unexpected response.\n\ngot: %s\nexp: %s\n", exp, got)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_Subscriptions_Show(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
// Create a database to use
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
// SHOW SUBSCRIPTIONS returns no subscriptions when there are none.
|
|
res := c.ExecuteStatement(mustParseStatement(`SHOW SUBSCRIPTIONS`))
|
|
if res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
} else if got, exp := len(res.Series), 0; got != exp {
|
|
t.Fatalf("got %d series, expected %d", got, exp)
|
|
}
|
|
|
|
// Create a subscription.
|
|
if res = c.ExecuteStatement(mustParseStatement(`CREATE SUBSCRIPTION sub0 ON db0."default" DESTINATIONS ALL 'udp://example.com:9090'`)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
// SHOW SUBSCRIPTIONS returns the created subscription.
|
|
if res = c.ExecuteStatement(mustParseStatement(`SHOW SUBSCRIPTIONS`)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
} else if got, exp := len(res.Series), 1; got != exp {
|
|
t.Fatalf("got %d series, expected %d", got, exp)
|
|
}
|
|
|
|
exp := `{"series":[{"name":"db0","columns":["retention_policy","name","mode","destinations"],"values":[["default","sub0","ALL",["udp://example.com:9090"]]]}]}`
|
|
got := mustMarshalJSON(res)
|
|
if got != exp {
|
|
t.Fatalf("unexpected response.\n\ngot: %s\nexp: %s\n", got, exp)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_Subscriptions_Drop(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
// Create a database to use
|
|
if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
db, err := c.Database("db0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if db.Name != "db0" {
|
|
t.Fatalf("db name wrong: %s", db.Name)
|
|
}
|
|
|
|
// DROP SUBSCRIPTION returns ErrSubscriptionNotFound when the
|
|
// subscription is unknown.
|
|
res := c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION foo ON db0."default"`))
|
|
if got, exp := res.Err, meta.ErrSubscriptionNotFound; got.Error() != exp.Error() {
|
|
t.Fatalf("got: %s, exp: %s", got, exp)
|
|
}
|
|
|
|
// Create a subscription.
|
|
if res = c.ExecuteStatement(mustParseStatement(`CREATE SUBSCRIPTION sub0 ON db0."default" DESTINATIONS ALL 'udp://example.com:9090'`)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
// DROP SUBSCRIPTION returns an influxdb.ErrDatabaseNotFound when
|
|
// the database is unknown.
|
|
res = c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION sub0 ON foo."default"`))
|
|
if got, exp := res.Err, influxdb.ErrDatabaseNotFound("foo"); got.Error() != exp.Error() {
|
|
t.Fatalf("got: %s, exp: %s", got, exp)
|
|
}
|
|
|
|
// DROP SUBSCRIPTION returns an influxdb.ErrRetentionPolicyNotFound
|
|
// when the retention policy is unknown.
|
|
res = c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION sub0 ON db0."foo_policy"`))
|
|
if got, exp := res.Err, influxdb.ErrRetentionPolicyNotFound("foo_policy"); got.Error() != exp.Error() {
|
|
t.Fatalf("got: %s, exp: %s", got, exp)
|
|
}
|
|
|
|
// DROP SUBSCRIPTION drops the subsciption if it can find it.
|
|
res = c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION sub0 ON db0."default"`))
|
|
if got := res.Err; got != nil {
|
|
t.Fatalf("got: %s, exp: %v", got, nil)
|
|
}
|
|
|
|
if res = c.ExecuteStatement(mustParseStatement(`SHOW SUBSCRIPTIONS`)); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
} else if got, exp := len(res.Series), 0; got != exp {
|
|
t.Fatalf("got %d series, expected %d", got, exp)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_Shards(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
exp := &meta.NodeInfo{
|
|
ID: 2,
|
|
Host: "foo:8180",
|
|
TCPHost: "bar:8281",
|
|
}
|
|
|
|
if _, err := c.CreateDataNode(exp.Host, exp.TCPHost); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := c.CreateDatabase("db0"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Test creating a shard group.
|
|
tmin := time.Now()
|
|
sg, err := c.CreateShardGroup("db0", "default", tmin)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if sg == nil {
|
|
t.Fatalf("expected ShardGroup")
|
|
}
|
|
|
|
// Test pre-creating shard groups.
|
|
dur := sg.EndTime.Sub(sg.StartTime) + time.Nanosecond
|
|
tmax := tmin.Add(dur)
|
|
if err := c.PrecreateShardGroups(tmin, tmax); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Test finding shard groups by time range.
|
|
groups, err := c.ShardGroupsByTimeRange("db0", "default", tmin, tmax)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if len(groups) != 2 {
|
|
t.Fatalf("wrong number of shard groups: %d", len(groups))
|
|
}
|
|
|
|
// Test finding shard owner.
|
|
db, rp, owner := c.ShardOwner(groups[0].Shards[0].ID)
|
|
if db != "db0" {
|
|
t.Fatalf("wrong db name: %s", db)
|
|
} else if rp != "default" {
|
|
t.Fatalf("wrong rp name: %s", rp)
|
|
} else if owner.ID != groups[0].ID {
|
|
t.Fatalf("wrong owner: exp %d got %d", groups[0].ID, owner.ID)
|
|
}
|
|
|
|
// Test deleting a shard group.
|
|
if err := c.DeleteShardGroup("db0", "default", groups[0].ID); err != nil {
|
|
t.Fatal(err)
|
|
} else if groups, err = c.ShardGroupsByTimeRange("db0", "default", tmin, tmax); err != nil {
|
|
t.Fatal(err)
|
|
} else if len(groups) != 1 {
|
|
t.Fatalf("wrong number of shard groups after delete: %d", len(groups))
|
|
}
|
|
}
|
|
|
|
func TestMetaService_CreateRemoveMetaNode(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
joinPeers := freePorts(4)
|
|
raftPeers := freePorts(4)
|
|
|
|
cfg1 := newConfig()
|
|
cfg1.HTTPBindAddress = joinPeers[0]
|
|
cfg1.BindAddress = raftPeers[0]
|
|
defer os.RemoveAll(cfg1.Dir)
|
|
cfg2 := newConfig()
|
|
cfg2.HTTPBindAddress = joinPeers[1]
|
|
cfg2.BindAddress = raftPeers[1]
|
|
defer os.RemoveAll(cfg2.Dir)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
cfg1.JoinPeers = joinPeers[0:2]
|
|
s1 := newService(cfg1)
|
|
go func() {
|
|
defer wg.Done()
|
|
if err := s1.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
defer s1.Close()
|
|
|
|
cfg2.JoinPeers = joinPeers[0:2]
|
|
s2 := newService(cfg2)
|
|
go func() {
|
|
defer wg.Done()
|
|
if err := s2.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
defer s2.Close()
|
|
wg.Wait()
|
|
|
|
cfg3 := newConfig()
|
|
joinPeers[2] = freePort()
|
|
cfg3.HTTPBindAddress = joinPeers[2]
|
|
raftPeers[2] = freePort()
|
|
cfg3.BindAddress = raftPeers[2]
|
|
defer os.RemoveAll(cfg3.Dir)
|
|
|
|
cfg3.JoinPeers = joinPeers[0:3]
|
|
s3 := newService(cfg3)
|
|
if err := s3.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s3.Close()
|
|
|
|
c1 := meta.NewClient(joinPeers[0:3], false)
|
|
if err := c1.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c1.Close()
|
|
|
|
metaNodes, _ := c1.MetaNodes()
|
|
if len(metaNodes) != 3 {
|
|
t.Fatalf("meta nodes wrong: %v", metaNodes)
|
|
}
|
|
|
|
c := meta.NewClient([]string{s1.HTTPAddr()}, false)
|
|
if err := c.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
if res := c.ExecuteStatement(mustParseStatement("DROP META SERVER 3")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
metaNodes, _ = c.MetaNodes()
|
|
if len(metaNodes) != 2 {
|
|
t.Fatalf("meta nodes wrong: %v", metaNodes)
|
|
}
|
|
|
|
cfg4 := newConfig()
|
|
cfg4.HTTPBindAddress = freePort()
|
|
cfg4.BindAddress = freePort()
|
|
cfg4.JoinPeers = []string{joinPeers[0], joinPeers[1], cfg4.HTTPBindAddress}
|
|
defer os.RemoveAll(cfg4.Dir)
|
|
s4 := newService(cfg4)
|
|
if err := s4.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s4.Close()
|
|
|
|
c2 := meta.NewClient(cfg4.JoinPeers, false)
|
|
if err := c2.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c2.Close()
|
|
|
|
metaNodes, _ = c2.MetaNodes()
|
|
if len(metaNodes) != 3 {
|
|
t.Fatalf("meta nodes wrong: %v", metaNodes)
|
|
}
|
|
}
|
|
|
|
// Ensure that if we attempt to create a database and the client
|
|
// is pointed at a server that isn't the leader, it automatically
|
|
// hits the leader and finishes the command
|
|
func TestMetaService_CommandAgainstNonLeader(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfgs := make([]*meta.Config, 3)
|
|
srvs := make([]*testService, 3)
|
|
joinPeers := freePorts(len(cfgs))
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(len(cfgs))
|
|
|
|
for i, _ := range cfgs {
|
|
c := newConfig()
|
|
c.HTTPBindAddress = joinPeers[i]
|
|
c.JoinPeers = joinPeers
|
|
cfgs[i] = c
|
|
|
|
srvs[i] = newService(c)
|
|
go func(srv *testService) {
|
|
defer wg.Done()
|
|
if err := srv.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}(srvs[i])
|
|
defer srvs[i].Close()
|
|
defer os.RemoveAll(c.Dir)
|
|
}
|
|
wg.Wait()
|
|
|
|
for i := range cfgs {
|
|
c := meta.NewClient([]string{joinPeers[i]}, false)
|
|
if err := c.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
metaNodes, _ := c.MetaNodes()
|
|
if len(metaNodes) != 3 {
|
|
t.Fatalf("node %d - meta nodes wrong: %v", i, metaNodes)
|
|
}
|
|
|
|
if _, err := c.CreateDatabase(fmt.Sprintf("foo%d", i)); err != nil {
|
|
t.Fatalf("node %d: %s", i, err)
|
|
}
|
|
|
|
if db, err := c.Database(fmt.Sprintf("foo%d", i)); db == nil || err != nil {
|
|
t.Fatalf("node %d: database foo wasn't created: %s", i, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure that the client will fail over to another server if the leader goes
|
|
// down. Also ensure that the cluster will come back up successfully after restart
|
|
func TestMetaService_FailureAndRestartCluster(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfgs := make([]*meta.Config, 3)
|
|
srvs := make([]*testService, 3)
|
|
joinPeers := freePorts(len(cfgs))
|
|
raftPeers := freePorts(len(cfgs))
|
|
|
|
var swg sync.WaitGroup
|
|
swg.Add(len(cfgs))
|
|
for i, _ := range cfgs {
|
|
c := newConfig()
|
|
c.HTTPBindAddress = joinPeers[i]
|
|
c.BindAddress = raftPeers[i]
|
|
c.JoinPeers = joinPeers
|
|
cfgs[i] = c
|
|
|
|
srvs[i] = newService(c)
|
|
go func(i int, srv *testService) {
|
|
defer swg.Done()
|
|
if err := srv.Open(); err != nil {
|
|
t.Logf("opening server %d", i)
|
|
t.Fatal(err)
|
|
}
|
|
}(i, srvs[i])
|
|
|
|
defer srvs[i].Close()
|
|
defer os.RemoveAll(c.Dir)
|
|
}
|
|
swg.Wait()
|
|
|
|
c := meta.NewClient(joinPeers, false)
|
|
if err := c.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
// check to see we were assigned a valid clusterID
|
|
c1ID := c.ClusterID()
|
|
if c1ID == 0 {
|
|
t.Fatalf("invalid cluster id: %d", c1ID)
|
|
}
|
|
|
|
if _, err := c.CreateDatabase("foo"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if db, err := c.Database("foo"); db == nil || err != nil {
|
|
t.Fatalf("database foo wasn't created: %s", err)
|
|
}
|
|
|
|
if err := srvs[0].Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := c.CreateDatabase("bar"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if db, err := c.Database("bar"); db == nil || err != nil {
|
|
t.Fatalf("database bar wasn't created: %s", err)
|
|
}
|
|
|
|
if err := srvs[1].Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := srvs[2].Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// give them a second to shut down
|
|
time.Sleep(time.Second)
|
|
|
|
// need to start them all at once so they can discover the bind addresses for raft
|
|
var wg sync.WaitGroup
|
|
wg.Add(len(cfgs))
|
|
for i, cfg := range cfgs {
|
|
srvs[i] = newService(cfg)
|
|
go func(srv *testService) {
|
|
if err := srv.Open(); err != nil {
|
|
panic(err)
|
|
}
|
|
wg.Done()
|
|
}(srvs[i])
|
|
defer srvs[i].Close()
|
|
}
|
|
wg.Wait()
|
|
time.Sleep(time.Second)
|
|
|
|
c2 := meta.NewClient(joinPeers, false)
|
|
if err := c2.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c2.Close()
|
|
|
|
c2ID := c2.ClusterID()
|
|
if c1ID != c2ID {
|
|
t.Fatalf("invalid cluster id. got: %d, exp: %d", c2ID, c1ID)
|
|
}
|
|
|
|
if db, err := c2.Database("bar"); db == nil || err != nil {
|
|
t.Fatalf("database bar wasn't created: %s", err)
|
|
}
|
|
|
|
if _, err := c2.CreateDatabase("asdf"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if db, err := c2.Database("asdf"); db == nil || err != nil {
|
|
t.Fatalf("database bar wasn't created: %s", err)
|
|
}
|
|
}
|
|
|
|
// Ensures that everything works after a host name change. This is
|
|
// skipped by default. To enable add hosts foobar and asdf to your
|
|
// /etc/hosts file and point those to 127.0.0.1
|
|
func TestMetaService_NameChangeSingleNode(t *testing.T) {
|
|
t.Skip("not enabled")
|
|
t.Parallel()
|
|
|
|
cfg := newConfig()
|
|
defer os.RemoveAll(cfg.Dir)
|
|
cfg.BindAddress = "foobar:0"
|
|
cfg.HTTPBindAddress = "foobar:0"
|
|
s := newService(cfg)
|
|
if err := s.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s.Close()
|
|
|
|
c := meta.NewClient([]string{s.HTTPAddr()}, false)
|
|
if err := c.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
if _, err := c.CreateDatabase("foo"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s.Close()
|
|
time.Sleep(time.Second)
|
|
|
|
cfg.BindAddress = "asdf" + ":" + strings.Split(s.RaftAddr(), ":")[1]
|
|
cfg.HTTPBindAddress = "asdf" + ":" + strings.Split(s.HTTPAddr(), ":")[1]
|
|
s = newService(cfg)
|
|
if err := s.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s.Close()
|
|
|
|
c2 := meta.NewClient([]string{s.HTTPAddr()}, false)
|
|
if err := c2.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c2.Close()
|
|
|
|
db, err := c2.Database("foo")
|
|
if db == nil || err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
nodes, err := c2.MetaNodes()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
exp := []meta.NodeInfo{{ID: 1, Host: cfg.HTTPBindAddress, TCPHost: cfg.BindAddress}}
|
|
|
|
time.Sleep(10 * time.Second)
|
|
if !reflect.DeepEqual(nodes, exp) {
|
|
t.Fatalf("nodes don't match: %v", nodes)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_CreateDataNode(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
exp := &meta.NodeInfo{
|
|
ID: 1,
|
|
Host: "foo:8180",
|
|
TCPHost: "bar:8281",
|
|
}
|
|
|
|
n, err := c.CreateDataNode(exp.Host, exp.TCPHost)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(n, exp) {
|
|
t.Fatalf("data node attributes wrong: %v", n)
|
|
}
|
|
|
|
nodes, err := c.DataNodes()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(nodes, []meta.NodeInfo{*exp}) {
|
|
t.Fatalf("nodes wrong: %v", nodes)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_DropDataNode(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c := newServiceAndClient()
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c.Close()
|
|
|
|
exp := &meta.NodeInfo{
|
|
ID: 1,
|
|
Host: "foo:8180",
|
|
TCPHost: "bar:8281",
|
|
}
|
|
|
|
n, err := c.CreateDataNode(exp.Host, exp.TCPHost)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(n, exp) {
|
|
t.Fatalf("data node attributes wrong: %v", n)
|
|
}
|
|
|
|
nodes, err := c.DataNodes()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(nodes, []meta.NodeInfo{*exp}) {
|
|
t.Fatalf("nodes wrong: %v", nodes)
|
|
}
|
|
|
|
if _, err := c.CreateDatabase("foo"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sg, err := c.CreateShardGroup("foo", "default", time.Now())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(sg.Shards[0].Owners, []meta.ShardOwner{{1}}) {
|
|
t.Fatalf("expected owners to be [1]: %v", sg.Shards[0].Owners)
|
|
}
|
|
|
|
if res := c.ExecuteStatement(mustParseStatement("DROP DATA SERVER 1")); res.Err != nil {
|
|
t.Fatal(res.Err)
|
|
}
|
|
|
|
rp, _ := c.RetentionPolicy("foo", "default")
|
|
if len(rp.ShardGroups[0].Shards[0].Owners) != 0 {
|
|
t.Fatalf("expected shard to have no owners: %v", rp.ShardGroups[0].Shards[0].Owners)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_PersistClusterIDAfterRestart(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cfg := newConfig()
|
|
defer os.RemoveAll(cfg.Dir)
|
|
s := newService(cfg)
|
|
if err := s.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer s.Close()
|
|
|
|
c := meta.NewClient([]string{s.HTTPAddr()}, false)
|
|
if err := c.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id := c.ClusterID()
|
|
if id == 0 {
|
|
t.Fatal("cluster ID can't be zero")
|
|
}
|
|
|
|
s.Close()
|
|
s = newService(cfg)
|
|
if err := s.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
c = meta.NewClient([]string{s.HTTPAddr()}, false)
|
|
if err := c.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
idAfter := c.ClusterID()
|
|
if idAfter == 0 {
|
|
t.Fatal("cluster ID can't be zero")
|
|
} else if idAfter != id {
|
|
t.Fatalf("cluster id not the same: %d, %d", idAfter, id)
|
|
}
|
|
}
|
|
|
|
func TestMetaService_Ping(t *testing.T) {
|
|
cfgs := make([]*meta.Config, 3)
|
|
srvs := make([]*testService, 3)
|
|
joinPeers := freePorts(len(cfgs))
|
|
|
|
var swg sync.WaitGroup
|
|
swg.Add(len(cfgs))
|
|
|
|
for i, _ := range cfgs {
|
|
c := newConfig()
|
|
c.HTTPBindAddress = joinPeers[i]
|
|
c.JoinPeers = joinPeers
|
|
cfgs[i] = c
|
|
|
|
srvs[i] = newService(c)
|
|
go func(i int, srv *testService) {
|
|
defer swg.Done()
|
|
if err := srv.Open(); err != nil {
|
|
t.Fatalf("error opening server %d: %s", i, err)
|
|
}
|
|
}(i, srvs[i])
|
|
defer srvs[i].Close()
|
|
defer os.RemoveAll(c.Dir)
|
|
}
|
|
swg.Wait()
|
|
|
|
c := meta.NewClient(joinPeers, false)
|
|
if err := c.Open(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
if err := c.Ping(false); err != nil {
|
|
t.Fatalf("ping false all failed: %s", err)
|
|
}
|
|
if err := c.Ping(true); err != nil {
|
|
t.Fatalf("ping false true failed: %s", err)
|
|
}
|
|
|
|
srvs[1].Close()
|
|
// give the server time to close
|
|
time.Sleep(time.Second)
|
|
|
|
if err := c.Ping(false); err != nil {
|
|
t.Fatalf("ping false some failed: %s", err)
|
|
}
|
|
|
|
if err := c.Ping(true); err == nil {
|
|
t.Fatal("expected error on ping")
|
|
}
|
|
}
|
|
|
|
func TestMetaService_AcquireLease(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
d, s, c1 := newServiceAndClient()
|
|
c2 := newClient(s)
|
|
defer os.RemoveAll(d)
|
|
defer s.Close()
|
|
defer c1.Close()
|
|
defer c2.Close()
|
|
|
|
n1, err := c1.CreateDataNode("foo1:8180", "bar1:8281")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
n2, err := c2.CreateDataNode("foo2:8180", "bar2:8281")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Client 1 acquires a lease. Should succeed.
|
|
l, err := c1.AcquireLease("foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if l == nil {
|
|
t.Fatal("expected *Lease")
|
|
} else if l.Name != "foo" {
|
|
t.Fatalf("lease name wrong: %s", l.Name)
|
|
} else if l.Owner != n1.ID {
|
|
t.Fatalf("owner ID wrong. exp %d got %d", n1.ID, l.Owner)
|
|
}
|
|
|
|
t.Logf("c1: %d, c2: %d", c1.NodeID(), c2.NodeID())
|
|
// Client 2 attempts to acquire the same lease. Should fail.
|
|
l, err = c2.AcquireLease("foo")
|
|
if err == nil {
|
|
t.Fatal("expected to fail because another node owns the lease")
|
|
}
|
|
|
|
// Wait for Client 1's lease to expire.
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// Client 2 retries to acquire the lease. Should succeed this time.
|
|
l, err = c2.AcquireLease("foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if l == nil {
|
|
t.Fatal("expected *Lease")
|
|
} else if l.Name != "foo" {
|
|
t.Fatalf("lease name wrong: %s", l.Name)
|
|
} else if l.Owner != n2.ID {
|
|
t.Fatalf("owner ID wrong. exp %d got %d", n2.ID, l.Owner)
|
|
}
|
|
}
|
|
|
|
// newServiceAndClient returns new data directory, *Service, and *Client or panics.
|
|
// Caller is responsible for deleting data dir and closing client.
|
|
func newServiceAndClient() (string, *testService, *meta.Client) {
|
|
cfg := newConfig()
|
|
s := newService(cfg)
|
|
if err := s.Open(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
c := newClient(s)
|
|
|
|
return cfg.Dir, s, c
|
|
}
|
|
|
|
func newClient(s *testService) *meta.Client {
|
|
c := meta.NewClient([]string{s.HTTPAddr()}, false)
|
|
if err := c.Open(); err != nil {
|
|
panic(err)
|
|
}
|
|
return c
|
|
}
|
|
|
|
func newConfig() *meta.Config {
|
|
cfg := meta.NewConfig()
|
|
cfg.BindAddress = "127.0.0.1:0"
|
|
cfg.HTTPBindAddress = "127.0.0.1:0"
|
|
cfg.Dir = testTempDir(2)
|
|
cfg.LeaseDuration = toml.Duration(1 * time.Second)
|
|
return cfg
|
|
}
|
|
|
|
func testTempDir(skip int) string {
|
|
// Get name of the calling function.
|
|
pc, _, _, ok := runtime.Caller(skip)
|
|
if !ok {
|
|
panic("failed to get name of test function")
|
|
}
|
|
_, prefix := path.Split(runtime.FuncForPC(pc).Name())
|
|
// Make a temp dir prefixed with calling function's name.
|
|
dir, err := ioutil.TempDir(os.TempDir(), prefix)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return dir
|
|
}
|
|
|
|
type testService struct {
|
|
*meta.Service
|
|
ln net.Listener
|
|
}
|
|
|
|
func (t *testService) Close() error {
|
|
if err := t.Service.Close(); err != nil {
|
|
return err
|
|
}
|
|
return t.ln.Close()
|
|
}
|
|
|
|
func newService(cfg *meta.Config) *testService {
|
|
// Open shared TCP connection.
|
|
ln, err := net.Listen("tcp", cfg.BindAddress)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Multiplex listener.
|
|
mux := tcp.NewMux()
|
|
|
|
s := meta.NewService(cfg)
|
|
s.RaftListener = mux.Listen(meta.MuxHeader)
|
|
|
|
go mux.Serve(ln)
|
|
|
|
return &testService{Service: s, ln: ln}
|
|
}
|
|
|
|
func mustParseStatement(s string) influxql.Statement {
|
|
stmt, err := influxql.ParseStatement(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return stmt
|
|
}
|
|
|
|
func mustMarshalJSON(v interface{}) string {
|
|
b, e := json.Marshal(v)
|
|
if e != nil {
|
|
panic(e)
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
func freePort() string {
|
|
l, _ := net.Listen("tcp", "127.0.0.1:0")
|
|
defer l.Close()
|
|
return l.Addr().String()
|
|
}
|
|
|
|
func freePorts(i int) []string {
|
|
var ports []string
|
|
for j := 0; j < i; j++ {
|
|
ports = append(ports, freePort())
|
|
}
|
|
return ports
|
|
}
|