1package meta_test 2 3import ( 4 "fmt" 5 "math/rand" 6 "reflect" 7 "testing" 8 "time" 9 10 "github.com/influxdata/influxdb" 11 "github.com/influxdata/influxdb/pkg/testing/assert" 12 "github.com/influxdata/influxql" 13 14 "github.com/influxdata/influxdb/services/meta" 15) 16 17func init() { 18 rand.Seed(time.Now().UnixNano()) 19} 20 21func Test_Data_DropDatabase(t *testing.T) { 22 data := &meta.Data{ 23 Databases: []meta.DatabaseInfo{ 24 {Name: "db0"}, 25 {Name: "db1"}, 26 {Name: "db2"}, 27 {Name: "db4"}, 28 {Name: "db5"}, 29 }, 30 Users: []meta.UserInfo{ 31 {Name: "user1", Privileges: map[string]influxql.Privilege{"db1": influxql.ReadPrivilege, "db2": influxql.ReadPrivilege}}, 32 {Name: "user2", Privileges: map[string]influxql.Privilege{"db2": influxql.ReadPrivilege}}, 33 }, 34 } 35 36 // Dropping the first database removes it from the Data object. 37 expDbs := make([]meta.DatabaseInfo, 4) 38 copy(expDbs, data.Databases[1:]) 39 if err := data.DropDatabase("db0"); err != nil { 40 t.Fatal(err) 41 } else if got, exp := data.Databases, expDbs; !reflect.DeepEqual(got, exp) { 42 t.Fatalf("got %v, expected %v", got, exp) 43 } 44 45 // Dropping a middle database removes it from the data object. 46 expDbs = []meta.DatabaseInfo{{Name: "db1"}, {Name: "db2"}, {Name: "db5"}} 47 if err := data.DropDatabase("db4"); err != nil { 48 t.Fatal(err) 49 } else if got, exp := data.Databases, expDbs; !reflect.DeepEqual(got, exp) { 50 t.Fatalf("got %v, expected %v", got, exp) 51 } 52 53 // Dropping the last database removes it from the data object. 54 expDbs = []meta.DatabaseInfo{{Name: "db1"}, {Name: "db2"}} 55 if err := data.DropDatabase("db5"); err != nil { 56 t.Fatal(err) 57 } else if got, exp := data.Databases, expDbs; !reflect.DeepEqual(got, exp) { 58 t.Fatalf("got %v, expected %v", got, exp) 59 } 60 61 // Dropping a database also drops all the user privileges associated with 62 // it. 63 expUsers := []meta.UserInfo{ 64 {Name: "user1", Privileges: map[string]influxql.Privilege{"db1": influxql.ReadPrivilege}}, 65 {Name: "user2", Privileges: map[string]influxql.Privilege{}}, 66 } 67 if err := data.DropDatabase("db2"); err != nil { 68 t.Fatal(err) 69 } else if got, exp := data.Users, expUsers; !reflect.DeepEqual(got, exp) { 70 t.Fatalf("got %v, expected %v", got, exp) 71 } 72} 73 74func Test_Data_CreateDatabase(t *testing.T) { 75 data := meta.Data{} 76 77 // Test creating a database succeedes. 78 if err := data.CreateDatabase("foo"); err != nil { 79 t.Fatal(err) 80 } 81 82 // Test creating a database with a name that is too long fails. 83 name := randString(meta.MaxNameLen + 1) 84 if err := data.CreateDatabase(name); err != meta.ErrNameTooLong { 85 t.Fatalf("exp: %v, got: %v", meta.ErrNameTooLong, err) 86 } 87} 88 89func Test_Data_CreateRetentionPolicy(t *testing.T) { 90 data := meta.Data{} 91 92 err := data.CreateDatabase("foo") 93 if err != nil { 94 t.Fatal(err) 95 } 96 97 err = data.CreateRetentionPolicy("foo", &meta.RetentionPolicyInfo{ 98 Name: "bar", 99 ReplicaN: 1, 100 Duration: 24 * time.Hour, 101 }, false) 102 if err != nil { 103 t.Fatal(err) 104 } 105 106 rp, err := data.RetentionPolicy("foo", "bar") 107 if err != nil { 108 t.Fatal(err) 109 } 110 111 if rp == nil { 112 t.Fatal("creation of retention policy failed") 113 } 114 115 // Try to recreate the same RP with default set to true, should fail 116 err = data.CreateRetentionPolicy("foo", &meta.RetentionPolicyInfo{ 117 Name: "bar", 118 ReplicaN: 1, 119 Duration: 24 * time.Hour, 120 }, true) 121 if err == nil || err != meta.ErrRetentionPolicyConflict { 122 t.Fatalf("unexpected error. got: %v, exp: %s", err, meta.ErrRetentionPolicyConflict) 123 } 124 125 // Creating the same RP with the same specifications should succeed 126 err = data.CreateRetentionPolicy("foo", &meta.RetentionPolicyInfo{ 127 Name: "bar", 128 ReplicaN: 1, 129 Duration: 24 * time.Hour, 130 }, false) 131 if err != nil { 132 t.Fatal(err) 133 } 134 135 // Try creating a retention policy with a name that is too long. Should fail. 136 err = data.CreateRetentionPolicy("foo", &meta.RetentionPolicyInfo{ 137 Name: randString(meta.MaxNameLen + 1), 138 ReplicaN: 1, 139 Duration: 24 * time.Hour, 140 }, true) 141 if err != meta.ErrNameTooLong { 142 t.Fatalf("exp: %v, got %v", meta.ErrNameTooLong, err) 143 } 144} 145 146func TestData_AdminUserExists(t *testing.T) { 147 data := meta.Data{} 148 149 // No users means no admin. 150 if data.AdminUserExists() { 151 t.Fatal("no admin user should exist") 152 } 153 154 // Add a non-admin user. 155 if err := data.CreateUser("user1", "a", false); err != nil { 156 t.Fatal(err) 157 } 158 if got, exp := data.AdminUserExists(), false; got != exp { 159 t.Fatalf("got %v, expected %v", got, exp) 160 } 161 162 // Add an admin user. 163 if err := data.CreateUser("admin1", "a", true); err != nil { 164 t.Fatal(err) 165 } 166 if got, exp := data.AdminUserExists(), true; got != exp { 167 t.Fatalf("got %v, expected %v", got, exp) 168 } 169 170 // Remove the original user 171 if err := data.DropUser("user1"); err != nil { 172 t.Fatal(err) 173 } 174 if got, exp := data.AdminUserExists(), true; got != exp { 175 t.Fatalf("got %v, expected %v", got, exp) 176 } 177 178 // Add another admin 179 if err := data.CreateUser("admin2", "a", true); err != nil { 180 t.Fatal(err) 181 } 182 if got, exp := data.AdminUserExists(), true; got != exp { 183 t.Fatalf("got %v, expected %v", got, exp) 184 } 185 186 // Revoke privileges of the first admin 187 if err := data.SetAdminPrivilege("admin1", false); err != nil { 188 t.Fatal(err) 189 } 190 if got, exp := data.AdminUserExists(), true; got != exp { 191 t.Fatalf("got %v, expected %v", got, exp) 192 } 193 194 // Add user1 back. 195 if err := data.CreateUser("user1", "a", false); err != nil { 196 t.Fatal(err) 197 } 198 // Revoke remaining admin. 199 if err := data.SetAdminPrivilege("admin2", false); err != nil { 200 t.Fatal(err) 201 } 202 // No longer any admins 203 if got, exp := data.AdminUserExists(), false; got != exp { 204 t.Fatalf("got %v, expected %v", got, exp) 205 } 206 207 // Make user1 an admin 208 if err := data.SetAdminPrivilege("user1", true); err != nil { 209 t.Fatal(err) 210 } 211 if got, exp := data.AdminUserExists(), true; got != exp { 212 t.Fatalf("got %v, expected %v", got, exp) 213 } 214 215 // Drop user1... 216 if err := data.DropUser("user1"); err != nil { 217 t.Fatal(err) 218 } 219 if got, exp := data.AdminUserExists(), false; got != exp { 220 t.Fatalf("got %v, expected %v", got, exp) 221 } 222} 223 224func TestData_SetPrivilege(t *testing.T) { 225 data := meta.Data{} 226 if err := data.CreateDatabase("db0"); err != nil { 227 t.Fatal(err) 228 } 229 230 if err := data.CreateUser("user1", "", false); err != nil { 231 t.Fatal(err) 232 } 233 234 // When the user does not exist, SetPrivilege returns an error. 235 if got, exp := data.SetPrivilege("not a user", "db0", influxql.AllPrivileges), meta.ErrUserNotFound; got != exp { 236 t.Fatalf("got %v, expected %v", got, exp) 237 } 238 239 // When the database does not exist, SetPrivilege returns an error. 240 if got, exp := data.SetPrivilege("user1", "db1", influxql.AllPrivileges), influxdb.ErrDatabaseNotFound("db1"); got == nil || got.Error() != exp.Error() { 241 t.Fatalf("got %v, expected %v", got, exp) 242 } 243 244 // Otherwise, SetPrivilege sets the expected privileges. 245 if got := data.SetPrivilege("user1", "db0", influxql.AllPrivileges); got != nil { 246 t.Fatalf("got %v, expected %v", got, nil) 247 } 248} 249 250func TestData_TruncateShardGroups(t *testing.T) { 251 data := &meta.Data{} 252 253 must := func(err error) { 254 if err != nil { 255 t.Fatal(err) 256 } 257 } 258 259 must(data.CreateDatabase("db")) 260 rp := meta.NewRetentionPolicyInfo("rp") 261 rp.ShardGroupDuration = 24 * time.Hour 262 must(data.CreateRetentionPolicy("db", rp, true)) 263 264 must(data.CreateShardGroup("db", "rp", time.Unix(0, 0))) 265 266 sg0, err := data.ShardGroupByTimestamp("db", "rp", time.Unix(0, 0)) 267 if err != nil { 268 t.Fatal("Failed to find shard group:", err) 269 } 270 271 if sg0.Truncated() { 272 t.Fatal("shard group already truncated") 273 } 274 275 sgEnd, err := data.ShardGroupByTimestamp("db", "rp", sg0.StartTime.Add(rp.ShardGroupDuration-1)) 276 if err != nil { 277 t.Fatal("Failed to find shard group for end range:", err) 278 } 279 280 if sgEnd == nil || sgEnd.ID != sg0.ID { 281 t.Fatalf("Retention policy mis-match: Expected %v, Got %v", sg0, sgEnd) 282 } 283 284 must(data.CreateShardGroup("db", "rp", sg0.StartTime.Add(rp.ShardGroupDuration))) 285 286 sg1, err := data.ShardGroupByTimestamp("db", "rp", sg0.StartTime.Add(rp.ShardGroupDuration+time.Minute)) 287 if err != nil { 288 t.Fatal("Failed to find second shard group:", err) 289 } 290 291 if sg1.Truncated() { 292 t.Fatal("second shard group already truncated") 293 } 294 295 // shouldn't do anything 296 must(data.CreateShardGroup("db", "rp", sg0.EndTime.Add(-time.Minute))) 297 298 sgs, err := data.ShardGroupsByTimeRange("db", "rp", time.Unix(0, 0), sg1.EndTime.Add(time.Minute)) 299 if err != nil { 300 t.Fatal("Failed to find shard groups:", err) 301 } 302 303 if len(sgs) != 2 { 304 t.Fatalf("Expected %d shard groups, found %d", 2, len(sgs)) 305 } 306 307 truncateTime := sg0.EndTime.Add(-time.Minute) 308 data.TruncateShardGroups(truncateTime) 309 310 // at this point, we should get nil shard groups for times after truncateTime 311 for _, tc := range []struct { 312 t time.Time 313 exists bool 314 }{ 315 {sg0.StartTime, true}, 316 {sg0.EndTime.Add(-1), false}, 317 {truncateTime.Add(-1), true}, 318 {truncateTime, false}, 319 {sg1.StartTime, false}, 320 } { 321 sg, err := data.ShardGroupByTimestamp("db", "rp", tc.t) 322 if err != nil { 323 t.Fatalf("Failed to find shardgroup for %v: %v", tc.t, err) 324 } 325 if tc.exists && sg == nil { 326 t.Fatalf("Shard group for timestamp '%v' should exist, got nil", tc.t) 327 } 328 } 329 330 for _, x := range data.Databases[0].RetentionPolicies[0].ShardGroups { 331 switch x.ID { 332 case sg0.ID: 333 *sg0 = x 334 case sg1.ID: 335 *sg1 = x 336 } 337 } 338 339 if sg0.TruncatedAt != truncateTime { 340 t.Fatalf("Incorrect truncation of current shard group. Expected %v, got %v", truncateTime, sg0.TruncatedAt) 341 } 342 343 if sg1.TruncatedAt != sg1.StartTime { 344 t.Fatalf("Incorrect truncation of future shard group. Expected %v, got %v", sg1.StartTime, sg1.TruncatedAt) 345 } 346} 347 348func TestUserInfo_AuthorizeDatabase(t *testing.T) { 349 emptyUser := &meta.UserInfo{} 350 if !emptyUser.AuthorizeDatabase(influxql.NoPrivileges, "anydb") { 351 t.Fatal("expected NoPrivileges to be authorized but it wasn't") 352 } 353 if emptyUser.AuthorizeDatabase(influxql.ReadPrivilege, "anydb") { 354 t.Fatal("expected ReadPrivilege to prevent authorization, but it was authorized") 355 } 356 357 adminUser := &meta.UserInfo{Admin: true} 358 if !adminUser.AuthorizeDatabase(influxql.AllPrivileges, "anydb") { 359 t.Fatalf("expected admin to be authorized but it wasn't") 360 } 361} 362 363func TestShardGroupInfo_Contains(t *testing.T) { 364 sgi := &meta.ShardGroupInfo{StartTime: time.Unix(10, 0), EndTime: time.Unix(20, 0)} 365 366 tests := []struct { 367 ts time.Time 368 exp bool 369 }{ 370 {time.Unix(0, 0), false}, 371 {time.Unix(9, 0), false}, 372 {time.Unix(10, 0), true}, 373 {time.Unix(11, 0), true}, 374 {time.Unix(15, 0), true}, 375 {time.Unix(19, 0), true}, 376 {time.Unix(20, 0), false}, 377 {time.Unix(21, 0), false}, 378 } 379 for _, test := range tests { 380 t.Run(fmt.Sprintf("ts=%d", test.ts.Unix()), func(t *testing.T) { 381 got := sgi.Contains(test.ts) 382 assert.Equal(t, got, test.exp) 383 }) 384 } 385} 386 387func randString(n int) string { 388 var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 389 b := make([]rune, n) 390 for i := range b { 391 b[i] = letters[rand.Intn(len(letters))] 392 } 393 return string(b) 394} 395