influxdb/services/meta/service_test.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
}