1 // Copyright 2017 Google Inc.
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 
15 #include "google/cloud/bigtable/testing/table_integration_test.h"
16 #include "google/cloud/internal/getenv.h"
17 #include "google/cloud/testing_util/assert_ok.h"
18 #include "absl/memory/memory.h"
19 #include <google/protobuf/text_format.h>
20 #include <algorithm>
21 #include <cctype>
22 
23 namespace google {
24 namespace cloud {
25 namespace bigtable {
26 namespace testing {
27 
28 std::string TableTestEnvironment::project_id_;
29 std::string TableTestEnvironment::instance_id_;
30 std::string TableTestEnvironment::zone_a_;
31 std::string TableTestEnvironment::zone_b_;
32 google::cloud::internal::DefaultPRNG TableTestEnvironment::generator_;
33 std::string TableTestEnvironment::table_id_;
34 bool TableTestEnvironment::using_cloud_bigtable_emulator_;
35 
TableTestEnvironment()36 TableTestEnvironment::TableTestEnvironment() {
37   project_id_ =
38       google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value_or("");
39   instance_id_ = google::cloud::internal::GetEnv(
40                      "GOOGLE_CLOUD_CPP_BIGTABLE_TEST_INSTANCE_ID")
41                      .value_or("");
42   zone_a_ =
43       google::cloud::internal::GetEnv("GOOGLE_CLOUD_CPP_BIGTABLE_TEST_ZONE_A")
44           .value_or("");
45   zone_b_ =
46       google::cloud::internal::GetEnv("GOOGLE_CLOUD_CPP_BIGTABLE_TEST_ZONE_B")
47           .value_or("");
48 }
49 
SetUp()50 void TableTestEnvironment::SetUp() {
51   ASSERT_FALSE(project_id_.empty());
52   ASSERT_FALSE(instance_id_.empty());
53   ASSERT_FALSE(zone_a_.empty());
54   ASSERT_FALSE(zone_b_.empty());
55 
56   using_cloud_bigtable_emulator_ =
57       google::cloud::internal::GetEnv("BIGTABLE_EMULATOR_HOST").has_value();
58 
59   generator_ = google::cloud::internal::MakeDefaultPRNG();
60 
61   auto admin_client = bigtable::CreateDefaultAdminClient(
62       TableTestEnvironment::project_id(), ClientOptions());
63   auto table_admin =
64       bigtable::TableAdmin(admin_client, TableTestEnvironment::instance_id());
65 
66   std::string const family1 = "family1";
67   std::string const family2 = "family2";
68   std::string const family3 = "family3";
69   std::string const family4 = "family4";
70   auto constexpr kTestMaxVersions = 10;
71   auto const test_gc_rule = bigtable::GcRule::MaxNumVersions(kTestMaxVersions);
72   bigtable::TableConfig table_config = bigtable::TableConfig(
73       {
74           {family1, test_gc_rule},
75           {family2, test_gc_rule},
76           {family3, test_gc_rule},
77           {family4, test_gc_rule},
78       },
79       {});
80 
81   table_id_ = RandomTableId();
82   ASSERT_STATUS_OK(table_admin.CreateTable(table_id_, table_config));
83 }
84 
TearDown()85 void TableTestEnvironment::TearDown() {
86   auto admin_client = bigtable::CreateDefaultAdminClient(
87       TableTestEnvironment::project_id(), ClientOptions());
88   auto table_admin =
89       bigtable::TableAdmin(admin_client, TableTestEnvironment::instance_id());
90 
91   ASSERT_STATUS_OK(table_admin.DeleteTable(table_id_));
92 }
93 
CreateRandomId(std::string const & prefix,int count)94 std::string TableTestEnvironment::CreateRandomId(std::string const& prefix,
95                                                  int count) {
96   return prefix +
97          google::cloud::internal::Sample(
98              generator_, count, "abcdefghijklmnopqrstuvwxyz0123456789");
99 }
100 
RandomTableId()101 std::string TableTestEnvironment::RandomTableId() {
102   // This value was discovered by trial and error, it is not documented in the
103   // proto files.
104   auto constexpr kMaxTableIdLength = 50;
105   static char const kPrefix[] = "table-";
106   static_assert(kMaxTableIdLength > sizeof(kPrefix), "prefix is too long");
107   auto constexpr kSampleCount = kMaxTableIdLength - sizeof(kPrefix) + 1;
108   return CreateRandomId(kPrefix, kSampleCount);
109 }
110 
RandomBackupId()111 std::string TableTestEnvironment::RandomBackupId() {
112   // Per google/bigtable/admin/v2/bigtable_table_admin.proto, backup names must
113   // be between 1 and 50 characters such, [_a-zA-Z0-9][-_.a-zA-Z0-9]*.
114   constexpr int kMaxBackupIdLength = 50;
115   static char const kPrefix[] = "backup-";
116   static_assert(kMaxBackupIdLength > sizeof(kPrefix), "prefix is too long");
117   constexpr int kSampleCount = kMaxBackupIdLength - sizeof(kPrefix) + 1;
118   return CreateRandomId(kPrefix, kSampleCount);
119 }
120 
RandomInstanceId()121 std::string TableTestEnvironment::RandomInstanceId() {
122   // This value was discovered by trial and error, it is not documented in the
123   // proto files.
124   constexpr int kMaxInstanceIdLength = 33;
125 
126   // There are other derived names such as cluster id, display name that uses
127   // returned values, these fields have their limits (e.g. 30 for cluster id)
128   // so need to keep some reserve.
129   constexpr int kReserveForSuffix = 10;
130   static char const kPrefix[] = "inst-";
131   static_assert((kMaxInstanceIdLength - kReserveForSuffix) > sizeof(kPrefix),
132                 "prefix is too long");
133   auto constexpr kSampleCount =
134       (kMaxInstanceIdLength - kReserveForSuffix) - sizeof(kPrefix) + 1;
135   return CreateRandomId(kPrefix, kSampleCount);
136 }
137 
SetUp()138 void TableIntegrationTest::SetUp() {
139   admin_client_ = bigtable::CreateDefaultAdminClient(
140       TableTestEnvironment::project_id(), ClientOptions());
141   table_admin_ = absl::make_unique<bigtable::TableAdmin>(
142       admin_client_, TableTestEnvironment::instance_id());
143   data_client_ = bigtable::CreateDefaultDataClient(
144       TableTestEnvironment::project_id(), TableTestEnvironment::instance_id(),
145       ClientOptions());
146 
147   // In production, we cannot use `DropAllRows()` to cleanup the table because
148   // the integration tests sometimes consume all the 'DropRowRangeGroup' quota.
149   // Instead we delete the rows, when possible, using BulkApply().
150   BulkMutation bulk;
151   auto table = GetTable();
152   // Bigtable does not support more than 100,000 mutations in a BulkMutation.
153   // If we had that many rows then just fallback on DropAllRows(). Most tests
154   // only have a small number of rows, so this is a good strategy to save
155   // DropAllRows() quota, and should be fast in most cases.
156   std::size_t const maximum_mutations = 100000;
157 
158   for (auto const& row :
159        table.ReadRows(RowRange::InfiniteRange(), Filter::PassAllFilter())) {
160     if (!row) {
161       break;
162     }
163     bulk.emplace_back(
164         SingleRowMutation(row->row_key(), bigtable::DeleteFromRow()));
165     if (bulk.size() > maximum_mutations) {
166       break;
167     }
168   }
169 
170   // The version of the emulator distributed with the Cloud SDK does not handle
171   // deleted rows correctly:
172   //   https://github.com/googleapis/google-cloud-go/issues/1240
173   // This has been fixed at HEAD, but the changes have not made it to a version
174   // we can use yet. So just use DropAllRows() in that case.
175   if (bulk.size() > maximum_mutations || UsingCloudBigtableEmulator()) {
176     ASSERT_STATUS_OK(
177         table_admin_->DropAllRows(TableTestEnvironment::table_id()));
178     return;
179   }
180   auto failures = table.BulkApply(std::move(bulk));
181   for (auto&& f : failures) {
182     ASSERT_STATUS_OK(f.status());
183   }
184 }
185 
GetTable()186 bigtable::Table TableIntegrationTest::GetTable() {
187   return bigtable::Table(data_client_, TableTestEnvironment::table_id());
188 }
189 
ReadRows(bigtable::Table & table,bigtable::Filter filter)190 std::vector<bigtable::Cell> TableIntegrationTest::ReadRows(
191     bigtable::Table& table, bigtable::Filter filter) {
192   auto reader = table.ReadRows(
193       bigtable::RowSet(bigtable::RowRange::InfiniteRange()), std::move(filter));
194   std::vector<bigtable::Cell> result;
195   for (auto const& row : reader) {
196     EXPECT_STATUS_OK(row);
197     std::copy(row->cells().begin(), row->cells().end(),
198               std::back_inserter(result));
199   }
200   return result;
201 }
202 
ReadRows(std::string const & table_name,bigtable::Filter filter)203 std::vector<bigtable::Cell> TableIntegrationTest::ReadRows(
204     std::string const& table_name, bigtable::Filter filter) {
205   bigtable::Table table(data_client_, table_name);
206   return ReadRows(table, std::move(filter));
207 }
208 
ReadRows(bigtable::Table & table,std::int64_t rows_limit,bigtable::Filter filter)209 std::vector<bigtable::Cell> TableIntegrationTest::ReadRows(
210     bigtable::Table& table, std::int64_t rows_limit, bigtable::Filter filter) {
211   auto reader =
212       table.ReadRows(bigtable::RowSet(bigtable::RowRange::InfiniteRange()),
213                      rows_limit, std::move(filter));
214   std::vector<bigtable::Cell> result;
215   for (auto const& row : reader) {
216     EXPECT_STATUS_OK(row);
217     std::copy(row->cells().begin(), row->cells().end(),
218               std::back_inserter(result));
219   }
220   return result;
221 }
222 
MoveCellsFromReader(bigtable::RowReader & reader)223 std::vector<bigtable::Cell> TableIntegrationTest::MoveCellsFromReader(
224     bigtable::RowReader& reader) {
225   std::vector<bigtable::Cell> result;
226   for (auto const& row : reader) {
227     EXPECT_STATUS_OK(row);
228     std::move(row->cells().begin(), row->cells().end(),
229               std::back_inserter(result));
230   }
231   return result;
232 }
233 
234 /// A helper function to create a list of cells.
CreateCells(bigtable::Table & table,std::vector<bigtable::Cell> const & cells)235 void TableIntegrationTest::CreateCells(
236     bigtable::Table& table, std::vector<bigtable::Cell> const& cells) {
237   std::map<RowKeyType, bigtable::SingleRowMutation> mutations;
238   for (auto const& cell : cells) {
239     using std::chrono::duration_cast;
240     using std::chrono::microseconds;
241     using std::chrono::milliseconds;
242     auto key = cell.row_key();
243     auto inserted = mutations.emplace(key, bigtable::SingleRowMutation(key));
244     inserted.first->second.emplace_back(bigtable::SetCell(
245         cell.family_name(), cell.column_qualifier(),
246         duration_cast<milliseconds>(microseconds(cell.timestamp())),
247         cell.value()));
248   }
249   bigtable::BulkMutation bulk;
250   for (auto& kv : mutations) {
251     bulk.emplace_back(std::move(kv.second));
252   }
253   auto failures = table.BulkApply(std::move(bulk));
254   ASSERT_THAT(failures, ::testing::IsEmpty());
255 }
256 
GetCellsIgnoringTimestamp(std::vector<bigtable::Cell> cells)257 std::vector<bigtable::Cell> TableIntegrationTest::GetCellsIgnoringTimestamp(
258     std::vector<bigtable::Cell> cells) {
259   // Create the expected_cells and actual_cells with same timestamp
260   std::vector<bigtable::Cell> return_cells;
261   std::transform(cells.begin(), cells.end(), std::back_inserter(return_cells),
262                  [](Cell& cell) {
263                    return bigtable::Cell(
264                        std::move(cell.row_key()), std::move(cell.family_name()),
265                        std::move(cell.column_qualifier()), 0,
266                        std::move(cell.value()), std::move(cell.labels()));
267                  });
268 
269   return return_cells;
270 }
271 
CheckEqualUnordered(std::vector<bigtable::Cell> expected,std::vector<bigtable::Cell> actual)272 void TableIntegrationTest::CheckEqualUnordered(
273     std::vector<bigtable::Cell> expected, std::vector<bigtable::Cell> actual) {
274   std::sort(expected.begin(), expected.end());
275   std::sort(actual.begin(), actual.end());
276   EXPECT_THAT(actual, ::testing::ContainerEq(expected));
277 }
278 
RandomTableId()279 std::string TableIntegrationTest::RandomTableId() {
280   return TableTestEnvironment::RandomTableId();
281 }
282 
project_id()283 std::string TableIntegrationTest::project_id() {
284   return TableTestEnvironment::project_id();
285 }
286 
instance_id()287 std::string TableIntegrationTest::instance_id() {
288   return TableTestEnvironment::instance_id();
289 }
290 
RandomBackupId()291 std::string TableIntegrationTest::RandomBackupId() {
292   return TableTestEnvironment::RandomBackupId();
293 }
294 
295 }  // namespace testing
296 
CellCompare(bigtable::Cell const & lhs,bigtable::Cell const & rhs)297 int CellCompare(bigtable::Cell const& lhs, bigtable::Cell const& rhs) {
298   auto compare_row_key = internal::CompareRowKey(lhs.row_key(), rhs.row_key());
299   if (compare_row_key != 0) {
300     return compare_row_key;
301   }
302   auto compare_family_name = lhs.family_name().compare(rhs.family_name());
303   if (compare_family_name != 0) {
304     return compare_family_name;
305   }
306   auto compare_column_qualifier = internal::CompareColumnQualifiers(
307       lhs.column_qualifier(), rhs.column_qualifier());
308   if (compare_column_qualifier != 0) {
309     return compare_column_qualifier;
310   }
311   if (lhs.timestamp() < rhs.timestamp()) {
312     return -1;
313   }
314   if (lhs.timestamp() > rhs.timestamp()) {
315     return 1;
316   }
317   auto compare_value = internal::CompareCellValues(lhs.value(), rhs.value());
318   if (compare_value != 0) {
319     return compare_value;
320   }
321   if (lhs.labels() < rhs.labels()) {
322     return -1;
323   }
324   if (lhs.labels() == rhs.labels()) {
325     return 0;
326   }
327   return 1;
328 }
329 
330 //@{
331 /// @name Helpers for GTest.
operator ==(Cell const & lhs,Cell const & rhs)332 bool operator==(Cell const& lhs, Cell const& rhs) {
333   return CellCompare(lhs, rhs) == 0;
334 }
335 
operator <(Cell const & lhs,Cell const & rhs)336 bool operator<(Cell const& lhs, Cell const& rhs) {
337   return CellCompare(lhs, rhs) < 0;
338 }
339 
340 /**
341  * This function is not used in this file, but it is used by GoogleTest; without
342  * it, failing tests will output binary blobs instead of human-readable text.
343  */
PrintTo(bigtable::Cell const & cell,std::ostream * os)344 void PrintTo(bigtable::Cell const& cell, std::ostream* os) {
345   *os << "  row_key=" << cell.row_key() << ", family=" << cell.family_name()
346       << ", column=" << cell.column_qualifier()
347       << ", timestamp=" << cell.timestamp().count() << ", value=<"
348       << cell.value() << ">, labels={";
349   char const* del = "";
350   for (auto const& label : cell.labels()) {
351     *os << del << label;
352     del = ",";
353   }
354   *os << "}";
355 }
356 //@}
357 
358 }  // namespace bigtable
359 }  // namespace cloud
360 }  // namespace google
361