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