1// Copyright 2015 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package bigtable 16 17import ( 18 "fmt" 19 "math" 20 "sort" 21 "strings" 22 "testing" 23 "time" 24 25 "cloud.google.com/go/internal/testutil" 26 "github.com/golang/protobuf/proto" 27 "golang.org/x/net/context" 28 "google.golang.org/api/iterator" 29 btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2" 30) 31 32func TestAdminIntegration(t *testing.T) { 33 testEnv, err := NewIntegrationEnv() 34 if err != nil { 35 t.Fatalf("IntegrationEnv: %v", err) 36 } 37 defer testEnv.Close() 38 39 timeout := 2 * time.Second 40 if testEnv.Config().UseProd { 41 timeout = 5 * time.Minute 42 } 43 ctx, _ := context.WithTimeout(context.Background(), timeout) 44 45 adminClient, err := testEnv.NewAdminClient() 46 if err != nil { 47 t.Fatalf("NewAdminClient: %v", err) 48 } 49 defer adminClient.Close() 50 51 iAdminClient, err := testEnv.NewInstanceAdminClient() 52 if err != nil { 53 t.Fatalf("NewInstanceAdminClient: %v", err) 54 } 55 if iAdminClient != nil { 56 defer iAdminClient.Close() 57 58 iInfo, err := iAdminClient.InstanceInfo(ctx, adminClient.instance) 59 if err != nil { 60 t.Errorf("InstanceInfo: %v", err) 61 } 62 if iInfo.Name != adminClient.instance { 63 t.Errorf("InstanceInfo returned name %#v, want %#v", iInfo.Name, adminClient.instance) 64 } 65 } 66 67 list := func() []string { 68 tbls, err := adminClient.Tables(ctx) 69 if err != nil { 70 t.Fatalf("Fetching list of tables: %v", err) 71 } 72 sort.Strings(tbls) 73 return tbls 74 } 75 containsAll := func(got, want []string) bool { 76 gotSet := make(map[string]bool) 77 78 for _, s := range got { 79 gotSet[s] = true 80 } 81 for _, s := range want { 82 if !gotSet[s] { 83 return false 84 } 85 } 86 return true 87 } 88 89 defer adminClient.DeleteTable(ctx, "mytable") 90 91 if err := adminClient.CreateTable(ctx, "mytable"); err != nil { 92 t.Fatalf("Creating table: %v", err) 93 } 94 95 defer adminClient.DeleteTable(ctx, "myothertable") 96 97 if err := adminClient.CreateTable(ctx, "myothertable"); err != nil { 98 t.Fatalf("Creating table: %v", err) 99 } 100 101 if got, want := list(), []string{"myothertable", "mytable"}; !containsAll(got, want) { 102 t.Errorf("adminClient.Tables returned %#v, want %#v", got, want) 103 } 104 105 must(adminClient.WaitForReplication(ctx, "mytable")) 106 107 if err := adminClient.DeleteTable(ctx, "myothertable"); err != nil { 108 t.Fatalf("Deleting table: %v", err) 109 } 110 tables := list() 111 if got, want := tables, []string{"mytable"}; !containsAll(got, want) { 112 t.Errorf("adminClient.Tables returned %#v, want %#v", got, want) 113 } 114 if got, unwanted := tables, []string{"myothertable"}; containsAll(got, unwanted) { 115 t.Errorf("adminClient.Tables return %#v. unwanted %#v", got, unwanted) 116 } 117 118 tblConf := TableConf{ 119 TableID: "conftable", 120 Families: map[string]GCPolicy{ 121 "fam1": MaxVersionsPolicy(1), 122 "fam2": MaxVersionsPolicy(2), 123 }, 124 } 125 if err := adminClient.CreateTableFromConf(ctx, &tblConf); err != nil { 126 t.Fatalf("Creating table from TableConf: %v", err) 127 } 128 defer adminClient.DeleteTable(ctx, tblConf.TableID) 129 130 tblInfo, err := adminClient.TableInfo(ctx, tblConf.TableID) 131 if err != nil { 132 t.Fatalf("Getting table info: %v", err) 133 } 134 sort.Strings(tblInfo.Families) 135 wantFams := []string{"fam1", "fam2"} 136 if !testutil.Equal(tblInfo.Families, wantFams) { 137 t.Errorf("Column family mismatch, got %v, want %v", tblInfo.Families, wantFams) 138 } 139 140 // Populate mytable and drop row ranges 141 if err = adminClient.CreateColumnFamily(ctx, "mytable", "cf"); err != nil { 142 t.Fatalf("Creating column family: %v", err) 143 } 144 145 client, err := testEnv.NewClient() 146 if err != nil { 147 t.Fatalf("NewClient: %v", err) 148 } 149 defer client.Close() 150 151 tbl := client.Open("mytable") 152 153 prefixes := []string{"a", "b", "c"} 154 for _, prefix := range prefixes { 155 for i := 0; i < 5; i++ { 156 mut := NewMutation() 157 mut.Set("cf", "col", 1000, []byte("1")) 158 if err := tbl.Apply(ctx, fmt.Sprintf("%v-%v", prefix, i), mut); err != nil { 159 t.Fatalf("Mutating row: %v", err) 160 } 161 } 162 } 163 164 if err = adminClient.DropRowRange(ctx, "mytable", "a"); err != nil { 165 t.Errorf("DropRowRange a: %v", err) 166 } 167 if err = adminClient.DropRowRange(ctx, "mytable", "c"); err != nil { 168 t.Errorf("DropRowRange c: %v", err) 169 } 170 if err = adminClient.DropRowRange(ctx, "mytable", "x"); err != nil { 171 t.Errorf("DropRowRange x: %v", err) 172 } 173 174 var gotRowCount int 175 must(tbl.ReadRows(ctx, RowRange{}, func(row Row) bool { 176 gotRowCount += 1 177 if !strings.HasPrefix(row.Key(), "b") { 178 t.Errorf("Invalid row after dropping range: %v", row) 179 } 180 return true 181 })) 182 if gotRowCount != 5 { 183 t.Errorf("Invalid row count after dropping range: got %v, want %v", gotRowCount, 5) 184 } 185} 186 187func TestInstanceUpdate(t *testing.T) { 188 testEnv, err := NewIntegrationEnv() 189 if err != nil { 190 t.Fatalf("IntegrationEnv: %v", err) 191 } 192 defer testEnv.Close() 193 194 timeout := 2 * time.Second 195 if testEnv.Config().UseProd { 196 timeout = 5 * time.Minute 197 } 198 ctx, cancel := context.WithTimeout(context.Background(), timeout) 199 defer cancel() 200 201 adminClient, err := testEnv.NewAdminClient() 202 if err != nil { 203 t.Fatalf("NewAdminClient: %v", err) 204 } 205 206 defer adminClient.Close() 207 208 iAdminClient, err := testEnv.NewInstanceAdminClient() 209 if err != nil { 210 t.Fatalf("NewInstanceAdminClient: %v", err) 211 } 212 213 if iAdminClient == nil { 214 return 215 } 216 217 defer iAdminClient.Close() 218 219 iInfo, err := iAdminClient.InstanceInfo(ctx, adminClient.instance) 220 if err != nil { 221 t.Errorf("InstanceInfo: %v", err) 222 } 223 224 if iInfo.Name != adminClient.instance { 225 t.Errorf("InstanceInfo returned name %#v, want %#v", iInfo.Name, adminClient.instance) 226 } 227 228 if iInfo.DisplayName != adminClient.instance { 229 t.Errorf("InstanceInfo returned name %#v, want %#v", iInfo.Name, adminClient.instance) 230 } 231 232 const numNodes = 4 233 // update cluster nodes 234 if err := iAdminClient.UpdateCluster(ctx, adminClient.instance, testEnv.Config().Cluster, int32(numNodes)); err != nil { 235 t.Errorf("UpdateCluster: %v", err) 236 } 237 238 // get cluster after updating 239 cis, err := iAdminClient.GetCluster(ctx, adminClient.instance, testEnv.Config().Cluster) 240 if err != nil { 241 t.Errorf("GetCluster %v", err) 242 } 243 if cis.ServeNodes != int(numNodes) { 244 t.Errorf("ServeNodes returned %d, want %d", cis.ServeNodes, int(numNodes)) 245 } 246} 247 248func TestAdminSnapshotIntegration(t *testing.T) { 249 testEnv, err := NewIntegrationEnv() 250 if err != nil { 251 t.Fatalf("IntegrationEnv: %v", err) 252 } 253 defer testEnv.Close() 254 255 if !testEnv.Config().UseProd { 256 t.Skip("emulator doesn't support snapshots") 257 } 258 259 timeout := 2 * time.Second 260 if testEnv.Config().UseProd { 261 timeout = 5 * time.Minute 262 } 263 ctx, _ := context.WithTimeout(context.Background(), timeout) 264 265 adminClient, err := testEnv.NewAdminClient() 266 if err != nil { 267 t.Fatalf("NewAdminClient: %v", err) 268 } 269 defer adminClient.Close() 270 271 table := testEnv.Config().Table 272 cluster := testEnv.Config().Cluster 273 274 list := func(cluster string) ([]*SnapshotInfo, error) { 275 infos := []*SnapshotInfo(nil) 276 277 it := adminClient.Snapshots(ctx, cluster) 278 for { 279 s, err := it.Next() 280 if err == iterator.Done { 281 break 282 } 283 if err != nil { 284 return nil, err 285 } 286 infos = append(infos, s) 287 } 288 return infos, err 289 } 290 291 // Delete the table at the end of the test. Schedule ahead of time 292 // in case the client fails 293 defer adminClient.DeleteTable(ctx, table) 294 295 if err := adminClient.CreateTable(ctx, table); err != nil { 296 t.Fatalf("Creating table: %v", err) 297 } 298 299 // Precondition: no snapshots 300 snapshots, err := list(cluster) 301 if err != nil { 302 t.Fatalf("Initial snapshot list: %v", err) 303 } 304 if got, want := len(snapshots), 0; got != want { 305 t.Fatalf("Initial snapshot list len: %d, want: %d", got, want) 306 } 307 308 // Create snapshot 309 defer adminClient.DeleteSnapshot(ctx, cluster, "mysnapshot") 310 311 if err = adminClient.SnapshotTable(ctx, table, cluster, "mysnapshot", 5*time.Hour); err != nil { 312 t.Fatalf("Creating snaphot: %v", err) 313 } 314 315 // List snapshot 316 snapshots, err = list(cluster) 317 if err != nil { 318 t.Fatalf("Listing snapshots: %v", err) 319 } 320 if got, want := len(snapshots), 1; got != want { 321 t.Fatalf("Listing snapshot count: %d, want: %d", got, want) 322 } 323 if got, want := snapshots[0].Name, "mysnapshot"; got != want { 324 t.Fatalf("Snapshot name: %s, want: %s", got, want) 325 } 326 if got, want := snapshots[0].SourceTable, table; got != want { 327 t.Fatalf("Snapshot SourceTable: %s, want: %s", got, want) 328 } 329 if got, want := snapshots[0].DeleteTime, snapshots[0].CreateTime.Add(5*time.Hour); math.Abs(got.Sub(want).Minutes()) > 1 { 330 t.Fatalf("Snapshot DeleteTime: %s, want: %s", got, want) 331 } 332 333 // Get snapshot 334 snapshot, err := adminClient.SnapshotInfo(ctx, cluster, "mysnapshot") 335 if err != nil { 336 t.Fatalf("SnapshotInfo: %v", snapshot) 337 } 338 if got, want := *snapshot, *snapshots[0]; got != want { 339 t.Fatalf("SnapshotInfo: %v, want: %v", got, want) 340 } 341 342 // Restore 343 restoredTable := table + "-restored" 344 defer adminClient.DeleteTable(ctx, restoredTable) 345 if err = adminClient.CreateTableFromSnapshot(ctx, restoredTable, cluster, "mysnapshot"); err != nil { 346 t.Fatalf("CreateTableFromSnapshot: %v", err) 347 } 348 if _, err := adminClient.TableInfo(ctx, restoredTable); err != nil { 349 t.Fatalf("Restored TableInfo: %v", err) 350 } 351 352 // Delete snapshot 353 if err = adminClient.DeleteSnapshot(ctx, cluster, "mysnapshot"); err != nil { 354 t.Fatalf("DeleteSnapshot: %v", err) 355 } 356 snapshots, err = list(cluster) 357 if err != nil { 358 t.Fatalf("List after Delete: %v", err) 359 } 360 if got, want := len(snapshots), 0; got != want { 361 t.Fatalf("List after delete len: %d, want: %d", got, want) 362 } 363} 364 365func TestGranularity(t *testing.T) { 366 testEnv, err := NewIntegrationEnv() 367 if err != nil { 368 t.Fatalf("IntegrationEnv: %v", err) 369 } 370 defer testEnv.Close() 371 372 timeout := 2 * time.Second 373 if testEnv.Config().UseProd { 374 timeout = 5 * time.Minute 375 } 376 ctx, _ := context.WithTimeout(context.Background(), timeout) 377 378 adminClient, err := testEnv.NewAdminClient() 379 if err != nil { 380 t.Fatalf("NewAdminClient: %v", err) 381 } 382 defer adminClient.Close() 383 384 list := func() []string { 385 tbls, err := adminClient.Tables(ctx) 386 if err != nil { 387 t.Fatalf("Fetching list of tables: %v", err) 388 } 389 sort.Strings(tbls) 390 return tbls 391 } 392 containsAll := func(got, want []string) bool { 393 gotSet := make(map[string]bool) 394 395 for _, s := range got { 396 gotSet[s] = true 397 } 398 for _, s := range want { 399 if !gotSet[s] { 400 return false 401 } 402 } 403 return true 404 } 405 406 defer adminClient.DeleteTable(ctx, "mytable") 407 408 if err := adminClient.CreateTable(ctx, "mytable"); err != nil { 409 t.Fatalf("Creating table: %v", err) 410 } 411 412 tables := list() 413 if got, want := tables, []string{"mytable"}; !containsAll(got, want) { 414 t.Errorf("adminClient.Tables returned %#v, want %#v", got, want) 415 } 416 417 // calling ModifyColumnFamilies to check the granularity of table 418 prefix := adminClient.instancePrefix() 419 req := &btapb.ModifyColumnFamiliesRequest{ 420 Name: prefix + "/tables/" + "mytable", 421 Modifications: []*btapb.ModifyColumnFamiliesRequest_Modification{{ 422 Id: "cf", 423 Mod: &btapb.ModifyColumnFamiliesRequest_Modification_Create{&btapb.ColumnFamily{}}, 424 }}, 425 } 426 table, err := adminClient.tClient.ModifyColumnFamilies(ctx, req) 427 if err != nil { 428 t.Fatalf("Creating column family: %v", err) 429 } 430 if table.Granularity != btapb.Table_TimestampGranularity(btapb.Table_MILLIS) { 431 t.Errorf("ModifyColumnFamilies returned granularity %#v, want %#v", table.Granularity, btapb.Table_TimestampGranularity(btapb.Table_MILLIS)) 432 } 433} 434 435func TestInstanceAdminClient_AppProfile(t *testing.T) { 436 testEnv, err := NewIntegrationEnv() 437 if err != nil { 438 t.Fatalf("IntegrationEnv: %v", err) 439 } 440 defer testEnv.Close() 441 442 timeout := 2 * time.Second 443 if testEnv.Config().UseProd { 444 timeout = 5 * time.Minute 445 } 446 ctx, cancel := context.WithTimeout(context.Background(), timeout) 447 defer cancel() 448 449 adminClient, err := testEnv.NewAdminClient() 450 if err != nil { 451 t.Fatalf("NewAdminClient: %v", err) 452 } 453 defer adminClient.Close() 454 455 iAdminClient, err := testEnv.NewInstanceAdminClient() 456 if err != nil { 457 t.Fatalf("NewInstanceAdminClient: %v", err) 458 } 459 460 if iAdminClient == nil { 461 return 462 } 463 464 defer iAdminClient.Close() 465 profile := ProfileConf{ 466 ProfileID: "app_profile1", 467 InstanceID: adminClient.instance, 468 ClusterID: testEnv.Config().Cluster, 469 Description: "creating new app profile 1", 470 RoutingPolicy: SingleClusterRouting, 471 } 472 473 createdProfile, err := iAdminClient.CreateAppProfile(ctx, profile) 474 if err != nil { 475 t.Fatalf("Creating app profile: %v", err) 476 477 } 478 479 gotProfile, err := iAdminClient.GetAppProfile(ctx, adminClient.instance, "app_profile1") 480 481 if err != nil { 482 t.Fatalf("Get app profile: %v", err) 483 } 484 485 if !proto.Equal(createdProfile, gotProfile) { 486 t.Fatalf("created profile: %s, got profile: %s", createdProfile.Name, gotProfile.Name) 487 488 } 489 490 list := func(instanceID string) ([]*btapb.AppProfile, error) { 491 profiles := []*btapb.AppProfile(nil) 492 493 it := iAdminClient.ListAppProfiles(ctx, instanceID) 494 for { 495 s, err := it.Next() 496 if err == iterator.Done { 497 break 498 } 499 if err != nil { 500 return nil, err 501 } 502 profiles = append(profiles, s) 503 } 504 return profiles, err 505 } 506 507 profiles, err := list(adminClient.instance) 508 if err != nil { 509 t.Fatalf("List app profile: %v", err) 510 } 511 512 if got, want := len(profiles), 1; got != want { 513 t.Fatalf("Initial app profile list len: %d, want: %d", got, want) 514 } 515 516 for _, test := range []struct { 517 desc string 518 uattrs ProfileAttrsToUpdate 519 want *btapb.AppProfile // nil means error 520 }{ 521 { 522 desc: "empty update", 523 uattrs: ProfileAttrsToUpdate{}, 524 want: nil, 525 }, 526 527 { 528 desc: "empty description update", 529 uattrs: ProfileAttrsToUpdate{Description: ""}, 530 want: &btapb.AppProfile{ 531 Name: gotProfile.Name, 532 Description: "", 533 RoutingPolicy: gotProfile.RoutingPolicy, 534 Etag: gotProfile.Etag}, 535 }, 536 { 537 desc: "routing update", 538 uattrs: ProfileAttrsToUpdate{ 539 RoutingPolicy: SingleClusterRouting, 540 ClusterID: testEnv.Config().Cluster, 541 }, 542 want: &btapb.AppProfile{ 543 Name: gotProfile.Name, 544 Description: "", 545 Etag: gotProfile.Etag, 546 RoutingPolicy: &btapb.AppProfile_SingleClusterRouting_{ 547 SingleClusterRouting: &btapb.AppProfile_SingleClusterRouting{ 548 ClusterId: testEnv.Config().Cluster, 549 }}, 550 }, 551 }, 552 } { 553 err = iAdminClient.UpdateAppProfile(ctx, adminClient.instance, "app_profile1", test.uattrs) 554 if err != nil { 555 if test.want != nil { 556 t.Errorf("%s: %v", test.desc, err) 557 } 558 continue 559 } 560 if err == nil && test.want == nil { 561 t.Errorf("%s: got nil, want error", test.desc) 562 continue 563 } 564 565 got, _ := iAdminClient.GetAppProfile(ctx, adminClient.instance, "app_profile1") 566 567 if !proto.Equal(got, test.want) { 568 t.Fatalf("%s : got profile : %v, want profile: %v", test.desc, gotProfile, test.want) 569 } 570 571 } 572 573 err = iAdminClient.DeleteAppProfile(ctx, adminClient.instance, "app_profile1") 574 if err != nil { 575 t.Fatalf("Delete app profile: %v", err) 576 } 577 578} 579