1 // Copyright 2020 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 
15 #include "google/cloud/bigtable/instance_admin.h"
16 #include "google/cloud/bigtable/testing/table_integration_test.h"
17 #include "google/cloud/internal/getenv.h"
18 #include "google/cloud/internal/random.h"
19 #include "google/cloud/internal/time_utils.h"
20 #include "google/cloud/testing_util/assert_ok.h"
21 #include "google/cloud/testing_util/chrono_literals.h"
22 #include "google/cloud/testing_util/contains_once.h"
23 #include "absl/memory/memory.h"
24 #include <google/protobuf/util/time_util.h>
25 #include <gmock/gmock.h>
26 #include <string>
27 #include <vector>
28 
29 namespace google {
30 namespace cloud {
31 namespace bigtable {
32 inline namespace BIGTABLE_CLIENT_NS {
33 namespace {
34 
35 using ::google::cloud::testing_util::ContainsOnce;
36 using ::testing::Contains;
37 using ::testing::Not;
38 namespace btadmin = google::bigtable::admin::v2;
39 namespace bigtable = google::cloud::bigtable;
40 
41 class AdminBackupIntegrationTest
42     : public bigtable::testing::TableIntegrationTest {
43  protected:
44   std::unique_ptr<bigtable::TableAdmin> table_admin_;
45   std::unique_ptr<bigtable::InstanceAdmin> instance_admin_;
46 
SetUp()47   void SetUp() override {
48     if (google::cloud::internal::GetEnv(
49             "ENABLE_BIGTABLE_ADMIN_INTEGRATION_TESTS")
50             .value_or("") != "yes") {
51       GTEST_SKIP();
52     }
53 
54     TableIntegrationTest::SetUp();
55 
56     std::shared_ptr<bigtable::AdminClient> admin_client =
57         bigtable::CreateDefaultAdminClient(
58             bigtable::testing::TableTestEnvironment::project_id(),
59             bigtable::ClientOptions());
60     table_admin_ = absl::make_unique<bigtable::TableAdmin>(
61         admin_client, bigtable::testing::TableTestEnvironment::instance_id());
62     auto instance_admin_client = bigtable::CreateDefaultInstanceAdminClient(
63         bigtable::testing::TableTestEnvironment::project_id(),
64         bigtable::ClientOptions());
65     instance_admin_ =
66         absl::make_unique<bigtable::InstanceAdmin>(instance_admin_client);
67   }
68 };
69 
70 /// @test Verify that `bigtable::TableAdmin` Backup CRUD operations work as
71 /// expected.
TEST_F(AdminBackupIntegrationTest,CreateListGetUpdateRestoreDeleteBackup)72 TEST_F(AdminBackupIntegrationTest, CreateListGetUpdateRestoreDeleteBackup) {
73   using GC = bigtable::GcRule;
74   std::string const table_id = RandomTableId();
75 
76   // verify new table id in current table list
77   auto previous_table_list =
78       table_admin_->ListTables(btadmin::Table::NAME_ONLY);
79   ASSERT_STATUS_OK(previous_table_list);
80   ASSERT_THAT(
81       TableNames(*previous_table_list),
82       Not(Contains(table_admin_->instance_name() + "/tables/" + table_id)))
83       << "Table (" << table_id << ") already exists."
84       << " This is unexpected, as the table ids are generated at random.";
85   // create table config
86   bigtable::TableConfig table_config(
87       {{"fam", GC::MaxNumVersions(5)},
88        {"foo", GC::MaxAge(std::chrono::hours(24))}},
89       {"a1000", "a2000", "b3000", "m5000"});
90 
91   // create table
92   ASSERT_STATUS_OK(table_admin_->CreateTable(table_id, table_config));
93 
94   auto clusters_list =
95       instance_admin_->ListClusters(table_admin_->instance_id());
96   ASSERT_STATUS_OK(clusters_list);
97   std::string const backup_cluster_full_name =
98       clusters_list->clusters.begin()->name();
99   std::string const backup_cluster_id = backup_cluster_full_name.substr(
100       backup_cluster_full_name.rfind('/') + 1,
101       backup_cluster_full_name.size() - backup_cluster_full_name.rfind('/'));
102   std::string const backup_id = RandomBackupId();
103   std::string const backup_full_name =
104       backup_cluster_full_name + "/backups/" + backup_id;
105 
106   // list backups to verify new backup id does not already exist
107   auto previous_backup_list = table_admin_->ListBackups({});
108   ASSERT_STATUS_OK(previous_backup_list);
109   ASSERT_THAT(
110       BackupNames(*previous_backup_list),
111       Not(Contains(table_admin_->instance_name() + "/backups/" + backup_id)))
112       << "Backup (" << backup_id << ") already exists."
113       << " This is unexpected, as the backup ids are"
114       << " generated at random.";
115   // create backup
116   google::protobuf::Timestamp const expire_time =
117       google::protobuf::util::TimeUtil::GetCurrentTime() +
118       google::protobuf::util::TimeUtil::HoursToDuration(12);
119 
120   auto created_backup = table_admin_->CreateBackup(
121       {backup_cluster_id, backup_id, table_id,
122        google::cloud::internal::ToChronoTimePoint(expire_time)});
123   ASSERT_STATUS_OK(created_backup);
124   EXPECT_EQ(created_backup->name(), backup_full_name);
125 
126   // get backup to verify create
127   auto get_backup = table_admin_->GetBackup(backup_cluster_id, backup_id);
128   ASSERT_STATUS_OK(get_backup);
129   EXPECT_EQ(get_backup->name(), backup_full_name);
130 
131   // update backup
132   google::protobuf::Timestamp const updated_expire_time =
133       expire_time + google::protobuf::util::TimeUtil::HoursToDuration(12);
134   auto updated_backup = table_admin_->UpdateBackup(
135       {backup_cluster_id, backup_id,
136        google::cloud::internal::ToChronoTimePoint(updated_expire_time)});
137 
138   // get backup to verify update
139   auto get_updated_backup =
140       table_admin_->GetBackup(backup_cluster_id, backup_id);
141   ASSERT_STATUS_OK(get_updated_backup);
142   EXPECT_EQ(get_updated_backup->name(), backup_full_name);
143   EXPECT_EQ(get_updated_backup->expire_time(), updated_expire_time);
144 
145   // delete table
146   EXPECT_STATUS_OK(table_admin_->DeleteTable(table_id));
147   // List to verify it is no longer there
148   auto current_table_list = table_admin_->ListTables(btadmin::Table::NAME_ONLY);
149   ASSERT_STATUS_OK(current_table_list);
150   EXPECT_THAT(
151       TableNames(*current_table_list),
152       Not(Contains(table_admin_->instance_name() + "/tables/" + table_id)));
153 
154   // restore table
155   auto restore_result =
156       table_admin_->RestoreTable({table_id, backup_cluster_id, backup_id});
157   EXPECT_STATUS_OK(restore_result);
158   current_table_list = table_admin_->ListTables(btadmin::Table::NAME_ONLY);
159   ASSERT_STATUS_OK(current_table_list);
160   EXPECT_THAT(
161       TableNames(*current_table_list),
162       ContainsOnce(table_admin_->instance_name() + "/tables/" + table_id));
163 
164   // delete backup
165   EXPECT_STATUS_OK(table_admin_->DeleteBackup(backup_cluster_id, backup_id));
166 
167   // delete table
168   EXPECT_STATUS_OK(table_admin_->DeleteTable(table_id));
169   // List to verify it is no longer there
170   current_table_list = table_admin_->ListTables(btadmin::Table::NAME_ONLY);
171   ASSERT_STATUS_OK(current_table_list);
172   EXPECT_THAT(
173       TableNames(*current_table_list),
174       Not(Contains(table_admin_->instance_name() + "/tables/" + table_id)));
175 }
176 
177 }  // namespace
178 }  // namespace BIGTABLE_CLIENT_NS
179 }  // namespace bigtable
180 }  // namespace cloud
181 }  // namespace google
182 
main(int argc,char * argv[])183 int main(int argc, char* argv[]) {
184   ::testing::InitGoogleMock(&argc, argv);
185   (void)::testing::AddGlobalTestEnvironment(
186       new google::cloud::bigtable::testing::TableTestEnvironment);
187   return RUN_ALL_TESTS();
188 }
189