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