1 // Copyright 2018 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/instance_admin.h"
16 #include "google/cloud/bigtable/internal/async_retry_multi_page.h"
17 #include "google/cloud/bigtable/internal/async_retry_op.h"
18 #include "google/cloud/bigtable/internal/async_retry_unary_rpc_and_poll.h"
19 #include "google/cloud/bigtable/internal/unary_client_utils.h"
20 #include "google/cloud/grpc_error_delegate.h"
21 #include "google/cloud/internal/throw_delegate.h"
22 #include <google/longrunning/operations.grpc.pb.h>
23 #include <google/protobuf/descriptor.h>
24 #include <google/protobuf/text_format.h>
25 #include <type_traits>
26 #include <unordered_set>
27
28 namespace btadmin = ::google::bigtable::admin::v2;
29
30 namespace google {
31 namespace cloud {
32 namespace bigtable {
33 inline namespace BIGTABLE_CLIENT_NS {
34 static_assert(std::is_copy_assignable<bigtable::InstanceAdmin>::value,
35 "bigtable::InstanceAdmin must be CopyAssignable");
36
37 using ClientUtils = bigtable::internal::UnaryClientUtils<InstanceAdminClient>;
38
ListInstances()39 StatusOr<InstanceList> InstanceAdmin::ListInstances() {
40 grpc::Status status;
41 InstanceList result;
42 // Copy the policies in effect for the operation.
43 auto rpc_policy = clone_rpc_retry_policy();
44 auto backoff_policy = clone_rpc_backoff_policy();
45
46 // Build the RPC request, try to minimize copying.
47 std::unordered_set<std::string> unique_failed_locations;
48 std::string page_token;
49 do {
50 btadmin::ListInstancesRequest request;
51 request.set_page_token(std::move(page_token));
52 request.set_parent(project_name());
53
54 auto response = ClientUtils::MakeCall(
55 *(client_), *rpc_policy, *backoff_policy,
56 MetadataUpdatePolicy(project_name(), MetadataParamTypes::PARENT),
57 &InstanceAdminClient::ListInstances, request,
58 "InstanceAdmin::ListInstances", status, true);
59 if (!status.ok()) {
60 break;
61 }
62
63 auto& instances = *response.mutable_instances();
64 std::move(instances.begin(), instances.end(),
65 std::back_inserter(result.instances));
66 auto& failed_locations = *response.mutable_failed_locations();
67 std::move(
68 failed_locations.begin(), failed_locations.end(),
69 std::inserter(unique_failed_locations, unique_failed_locations.end()));
70
71 page_token = std::move(*response.mutable_next_page_token());
72 } while (!page_token.empty());
73
74 if (!status.ok()) {
75 return MakeStatusFromRpcError(status);
76 }
77
78 std::move(unique_failed_locations.begin(), unique_failed_locations.end(),
79 std::back_inserter(result.failed_locations));
80 return result;
81 }
82
AsyncListInstances(CompletionQueue & cq)83 future<StatusOr<InstanceList>> InstanceAdmin::AsyncListInstances(
84 CompletionQueue& cq) {
85 promise<StatusOr<InstanceList>> instance_list_promise;
86 future<StatusOr<InstanceList>> result = instance_list_promise.get_future();
87 auto client = client_;
88 btadmin::ListInstancesRequest request;
89 request.set_parent(project_name());
90
91 struct Accumulator {
92 std::vector<Instance> instances;
93 std::unordered_set<std::string> failed_locations;
94 };
95
96 return internal::StartAsyncRetryMultiPage(
97 __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
98 MetadataUpdatePolicy(project_name(), MetadataParamTypes::PARENT),
99 [client](grpc::ClientContext* context,
100 btadmin::ListInstancesRequest const& request,
101 grpc::CompletionQueue* cq) {
102 return client->AsyncListInstances(context, request, cq);
103 },
104 std::move(request), Accumulator(),
105 [](Accumulator acc,
106 btadmin::ListInstancesResponse const& response) {
107 std::move(response.failed_locations().begin(),
108 response.failed_locations().end(),
109 std::inserter(acc.failed_locations,
110 acc.failed_locations.end()));
111 std::move(response.instances().begin(),
112 response.instances().end(),
113 std::back_inserter(acc.instances));
114 return acc;
115 },
116 cq)
117 .then([](future<StatusOr<Accumulator>> acc_future)
118 -> StatusOr<InstanceList> {
119 auto acc = acc_future.get();
120 if (!acc) {
121 return acc.status();
122 }
123 InstanceList res;
124 res.instances = std::move(acc->instances);
125 std::move(acc->failed_locations.begin(), acc->failed_locations.end(),
126 std::back_inserter(res.failed_locations));
127 return res;
128 });
129 }
130
CreateInstance(InstanceConfig instance_config)131 future<StatusOr<btadmin::Instance>> InstanceAdmin::CreateInstance(
132 InstanceConfig instance_config) {
133 CompletionQueue cq;
134 std::thread([](CompletionQueue cq) { cq.Run(); }, cq).detach();
135
136 return AsyncCreateInstance(cq, std::move(instance_config))
137 .then([cq](future<StatusOr<btadmin::Instance>> f) mutable {
138 cq.Shutdown();
139 return f.get();
140 });
141 }
142
143 future<StatusOr<google::bigtable::admin::v2::Instance>>
AsyncCreateInstance(CompletionQueue & cq,bigtable::InstanceConfig instance_config)144 InstanceAdmin::AsyncCreateInstance(CompletionQueue& cq,
145 bigtable::InstanceConfig instance_config) {
146 auto request = std::move(instance_config).as_proto();
147 request.set_parent(project_name());
148 for (auto& kv : *request.mutable_clusters()) {
149 kv.second.set_location(project_name() + "/locations/" +
150 kv.second.location());
151 }
152 std::shared_ptr<InstanceAdminClient> client(client_);
153 return internal::AsyncStartPollAfterRetryUnaryRpc<
154 google::bigtable::admin::v2::Instance>(
155 __func__, clone_polling_policy(), clone_rpc_retry_policy(),
156 clone_rpc_backoff_policy(), internal::ConstantIdempotencyPolicy(false),
157 MetadataUpdatePolicy(project_name(), MetadataParamTypes::PARENT), client,
158 [client](
159 grpc::ClientContext* context,
160 google::bigtable::admin::v2::CreateInstanceRequest const& request,
161 grpc::CompletionQueue* cq) {
162 return client->AsyncCreateInstance(context, request, cq);
163 },
164 std::move(request), cq);
165 }
166
CreateCluster(ClusterConfig cluster_config,std::string const & instance_id,std::string const & cluster_id)167 future<StatusOr<btadmin::Cluster>> InstanceAdmin::CreateCluster(
168 ClusterConfig cluster_config, std::string const& instance_id,
169 std::string const& cluster_id) {
170 CompletionQueue cq;
171 std::thread([](CompletionQueue cq) { cq.Run(); }, cq).detach();
172
173 return AsyncCreateCluster(cq, std::move(cluster_config), instance_id,
174 cluster_id)
175 .then([cq](future<StatusOr<btadmin::Cluster>> f) mutable {
176 cq.Shutdown();
177 return f.get();
178 });
179 }
180
181 future<StatusOr<google::bigtable::admin::v2::Cluster>>
AsyncCreateCluster(CompletionQueue & cq,ClusterConfig cluster_config,std::string const & instance_id,std::string const & cluster_id)182 InstanceAdmin::AsyncCreateCluster(CompletionQueue& cq,
183 ClusterConfig cluster_config,
184 std::string const& instance_id,
185 std::string const& cluster_id) {
186 auto cluster = std::move(cluster_config).as_proto();
187 cluster.set_location(project_name() + "/locations/" + cluster.location());
188 btadmin::CreateClusterRequest request;
189 request.mutable_cluster()->Swap(&cluster);
190 auto parent = InstanceName(instance_id);
191 request.set_parent(parent);
192 request.set_cluster_id(cluster_id);
193
194 std::shared_ptr<InstanceAdminClient> client(client_);
195 return internal::AsyncStartPollAfterRetryUnaryRpc<
196 google::bigtable::admin::v2::Cluster>(
197 __func__, clone_polling_policy(), clone_rpc_retry_policy(),
198 clone_rpc_backoff_policy(), internal::ConstantIdempotencyPolicy(false),
199 MetadataUpdatePolicy(parent, MetadataParamTypes::PARENT), client,
200 [client](grpc::ClientContext* context,
201 google::bigtable::admin::v2::CreateClusterRequest const& request,
202 grpc::CompletionQueue* cq) {
203 return client->AsyncCreateCluster(context, request, cq);
204 },
205 std::move(request), cq);
206 }
207
208 future<StatusOr<google::bigtable::admin::v2::Instance>>
UpdateInstance(InstanceUpdateConfig instance_update_config)209 InstanceAdmin::UpdateInstance(InstanceUpdateConfig instance_update_config) {
210 CompletionQueue cq;
211 std::thread([](CompletionQueue cq) { cq.Run(); }, cq).detach();
212
213 return AsyncUpdateInstance(cq, std::move(instance_update_config))
214 .then([cq](future<StatusOr<btadmin::Instance>> f) mutable {
215 cq.Shutdown();
216 return f.get();
217 });
218 }
219
220 future<StatusOr<google::bigtable::admin::v2::Instance>>
AsyncUpdateInstance(CompletionQueue & cq,InstanceUpdateConfig instance_update_config)221 InstanceAdmin::AsyncUpdateInstance(
222 CompletionQueue& cq, InstanceUpdateConfig instance_update_config) {
223 auto name = instance_update_config.GetName();
224 auto request = std::move(instance_update_config).as_proto();
225
226 std::shared_ptr<InstanceAdminClient> client(client_);
227 return internal::AsyncStartPollAfterRetryUnaryRpc<
228 google::bigtable::admin::v2::Instance>(
229 __func__, clone_polling_policy(), clone_rpc_retry_policy(),
230 clone_rpc_backoff_policy(), internal::ConstantIdempotencyPolicy(false),
231 MetadataUpdatePolicy(name, MetadataParamTypes::INSTANCE_NAME), client,
232 [client](grpc::ClientContext* context,
233 google::bigtable::admin::v2::PartialUpdateInstanceRequest const&
234 request,
235 grpc::CompletionQueue* cq) {
236 return client->AsyncUpdateInstance(context, request, cq);
237 },
238 std::move(request), cq);
239 }
240
GetInstance(std::string const & instance_id)241 StatusOr<btadmin::Instance> InstanceAdmin::GetInstance(
242 std::string const& instance_id) {
243 grpc::Status status;
244 // Copy the policies in effect for the operation.
245 auto rpc_policy = clone_rpc_retry_policy();
246 auto backoff_policy = clone_rpc_backoff_policy();
247
248 btadmin::GetInstanceRequest request;
249 // Setting instance name.
250 auto name = InstanceName(instance_id);
251 request.set_name(name);
252
253 // Call RPC call to get response
254 auto result = ClientUtils::MakeCall(
255 *(client_), *rpc_policy, *backoff_policy,
256 MetadataUpdatePolicy(name, MetadataParamTypes::NAME),
257 &InstanceAdminClient::GetInstance, request, "InstanceAdmin::GetInstance",
258 status, true);
259 if (!status.ok()) {
260 return MakeStatusFromRpcError(status);
261 }
262 return result;
263 }
264
AsyncGetInstance(CompletionQueue & cq,std::string const & instance_id)265 future<StatusOr<btadmin::Instance>> InstanceAdmin::AsyncGetInstance(
266 CompletionQueue& cq, std::string const& instance_id) {
267 btadmin::GetInstanceRequest request;
268 auto name = InstanceName(instance_id);
269 request.set_name(name);
270
271 auto client = client_;
272 return google::cloud::internal::StartRetryAsyncUnaryRpc(
273 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
274 /*is_idempotent=*/true,
275 [client, name](grpc::ClientContext* context,
276 btadmin::GetInstanceRequest const& request,
277 grpc::CompletionQueue* cq) {
278 MetadataUpdatePolicy(name, MetadataParamTypes::NAME).Setup(*context);
279 return client->AsyncGetInstance(context, request, cq);
280 },
281 std::move(request));
282 }
283
DeleteInstance(std::string const & instance_id)284 Status InstanceAdmin::DeleteInstance(std::string const& instance_id) {
285 grpc::Status status;
286 btadmin::DeleteInstanceRequest request;
287 auto name = InstanceName(instance_id);
288 request.set_name(name);
289
290 // This API is not idempotent, lets call it without retry
291 ClientUtils::MakeNonIdemponentCall(
292 *(client_), clone_rpc_retry_policy(),
293 MetadataUpdatePolicy(name, MetadataParamTypes::NAME),
294 &InstanceAdminClient::DeleteInstance, request,
295 "InstanceAdmin::DeleteInstance", status);
296 return MakeStatusFromRpcError(status);
297 }
298
AsyncDeleteCluster(CompletionQueue & cq,std::string const & instance_id,std::string const & cluster_id)299 future<Status> InstanceAdmin::AsyncDeleteCluster(
300 CompletionQueue& cq, std::string const& instance_id,
301 std::string const& cluster_id) {
302 btadmin::DeleteClusterRequest request;
303 auto name = ClusterName(instance_id, cluster_id);
304 request.set_name(name);
305
306 auto client = client_;
307 return google::cloud::internal::StartRetryAsyncUnaryRpc(
308 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
309 /*is_idempotent=*/false,
310 [client, name](grpc::ClientContext* context,
311 btadmin::DeleteClusterRequest const& request,
312 grpc::CompletionQueue* cq) {
313 MetadataUpdatePolicy(name, MetadataParamTypes::NAME)
314 .Setup(*context);
315 return client->AsyncDeleteCluster(context, request, cq);
316 },
317 std::move(request))
318 .then([](future<StatusOr<google::protobuf::Empty>> fut) {
319 auto res = fut.get();
320 if (res) {
321 return google::cloud::Status();
322 }
323 return res.status();
324 });
325 }
326
AsyncDeleteInstance(std::string const & instance_id,CompletionQueue & cq)327 future<Status> InstanceAdmin::AsyncDeleteInstance(
328 std::string const& instance_id, CompletionQueue& cq) {
329 google::bigtable::admin::v2::DeleteInstanceRequest request;
330 auto name = InstanceName(instance_id);
331 request.set_name(name);
332
333 auto client = client_;
334 return google::cloud::internal::StartRetryAsyncUnaryRpc(
335 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
336 /*is_idempotent=*/true,
337 [client, name](
338 grpc::ClientContext* context,
339 google::bigtable::admin::v2::DeleteInstanceRequest const&
340 request,
341 grpc::CompletionQueue* cq) {
342 MetadataUpdatePolicy(name, MetadataParamTypes::NAME)
343 .Setup(*context);
344 return client->AsyncDeleteInstance(context, request, cq);
345 },
346 std::move(request))
347 .then([](future<StatusOr<google::protobuf::Empty>> r) {
348 return r.get().status();
349 });
350 }
351
GetCluster(std::string const & instance_id,std::string const & cluster_id)352 StatusOr<btadmin::Cluster> InstanceAdmin::GetCluster(
353 std::string const& instance_id, std::string const& cluster_id) {
354 grpc::Status status;
355 auto rpc_policy = clone_rpc_retry_policy();
356 auto backoff_policy = clone_rpc_backoff_policy();
357
358 btadmin::GetClusterRequest request;
359 auto name = ClusterName(instance_id, cluster_id);
360 request.set_name(name);
361
362 auto result = ClientUtils::MakeCall(
363 *(client_), *rpc_policy, *backoff_policy,
364 MetadataUpdatePolicy(name, MetadataParamTypes::NAME),
365 &InstanceAdminClient::GetCluster, request, "InstanceAdmin::GetCluster",
366 status, true);
367 if (!status.ok()) {
368 return MakeStatusFromRpcError(status);
369 }
370 return result;
371 }
372
AsyncGetCluster(CompletionQueue & cq,std::string const & instance_id,std::string const & cluster_id)373 future<StatusOr<btadmin::Cluster>> InstanceAdmin::AsyncGetCluster(
374 CompletionQueue& cq, std::string const& instance_id,
375 std::string const& cluster_id) {
376 promise<StatusOr<btadmin::Cluster>> p;
377 auto result = p.get_future();
378 btadmin::GetClusterRequest request;
379 auto name = ClusterName(instance_id, cluster_id);
380 request.set_name(name);
381 auto client = client_;
382 return google::cloud::internal::StartRetryAsyncUnaryRpc(
383 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
384 /*is_idempotent=*/true,
385 [client, name](grpc::ClientContext* context,
386 btadmin::GetClusterRequest const& request,
387 grpc::CompletionQueue* cq) {
388 MetadataUpdatePolicy(name, MetadataParamTypes::NAME).Setup(*context);
389 return client->AsyncGetCluster(context, request, cq);
390 },
391 std::move(request));
392 }
393
ListClusters()394 StatusOr<ClusterList> InstanceAdmin::ListClusters() {
395 return ListClusters("-");
396 }
397
ListClusters(std::string const & instance_id)398 StatusOr<ClusterList> InstanceAdmin::ListClusters(
399 std::string const& instance_id) {
400 grpc::Status status;
401 ClusterList result;
402 std::unordered_set<std::string> unique_failed_locations;
403 std::string page_token;
404
405 // Copy the policies in effect for the operation.
406 auto rpc_policy = clone_rpc_retry_policy();
407 auto backoff_policy = clone_rpc_backoff_policy();
408
409 do {
410 // Build the RPC request, try to minimize copying.
411 btadmin::ListClustersRequest request;
412 request.set_page_token(std::move(page_token));
413 auto parent = InstanceName(instance_id);
414 request.set_parent(parent);
415
416 auto response = ClientUtils::MakeCall(
417 *(client_), *rpc_policy, *backoff_policy,
418 MetadataUpdatePolicy(parent, MetadataParamTypes::PARENT),
419 &InstanceAdminClient::ListClusters, request,
420 "InstanceAdmin::ListClusters", status, true);
421 if (!status.ok()) {
422 break;
423 }
424
425 auto& clusters = *response.mutable_clusters();
426 std::move(clusters.begin(), clusters.end(),
427 std::back_inserter(result.clusters));
428 auto& failed_locations = *response.mutable_failed_locations();
429 std::move(
430 failed_locations.begin(), failed_locations.end(),
431 std::inserter(unique_failed_locations, unique_failed_locations.end()));
432 page_token = std::move(*response.mutable_next_page_token());
433 } while (!page_token.empty());
434
435 if (!status.ok()) {
436 return MakeStatusFromRpcError(status);
437 }
438
439 std::move(unique_failed_locations.begin(), unique_failed_locations.end(),
440 std::back_inserter(result.failed_locations));
441 return result;
442 }
443
AsyncListClusters(CompletionQueue & cq)444 future<StatusOr<ClusterList>> InstanceAdmin::AsyncListClusters(
445 CompletionQueue& cq) {
446 return AsyncListClusters(cq, "-");
447 }
448
AsyncListClusters(CompletionQueue & cq,std::string const & instance_id)449 future<StatusOr<ClusterList>> InstanceAdmin::AsyncListClusters(
450 CompletionQueue& cq, std::string const& instance_id) {
451 auto client = client_;
452 btadmin::ListClustersRequest request;
453 auto parent = InstanceName(instance_id);
454 request.set_parent(parent);
455
456 struct Accumulator {
457 std::vector<btadmin::Cluster> clusters;
458 std::unordered_set<std::string> failed_locations;
459 };
460
461 return internal::StartAsyncRetryMultiPage(
462 __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
463 MetadataUpdatePolicy(parent, MetadataParamTypes::PARENT),
464 [client](grpc::ClientContext* context,
465 btadmin::ListClustersRequest const& request,
466 grpc::CompletionQueue* cq) {
467 return client->AsyncListClusters(context, request, cq);
468 },
469 std::move(request), Accumulator(),
470 [](Accumulator acc,
471 btadmin::ListClustersResponse const& response) {
472 std::move(response.failed_locations().begin(),
473 response.failed_locations().end(),
474 std::inserter(acc.failed_locations,
475 acc.failed_locations.end()));
476 std::move(response.clusters().begin(), response.clusters().end(),
477 std::back_inserter(acc.clusters));
478 return acc;
479 },
480 cq)
481 .then([](future<StatusOr<Accumulator>> acc_future)
482 -> StatusOr<ClusterList> {
483 auto acc = acc_future.get();
484 if (!acc) {
485 return acc.status();
486 }
487 ClusterList res;
488 res.clusters = std::move(acc->clusters);
489 std::move(acc->failed_locations.begin(), acc->failed_locations.end(),
490 std::back_inserter(res.failed_locations));
491 return res;
492 });
493 }
494
495 future<StatusOr<google::bigtable::admin::v2::Cluster>>
UpdateCluster(ClusterConfig cluster_config)496 InstanceAdmin::UpdateCluster(ClusterConfig cluster_config) {
497 CompletionQueue cq;
498 std::thread([](CompletionQueue cq) { cq.Run(); }, cq).detach();
499
500 return AsyncUpdateCluster(cq, std::move(cluster_config))
501 .then([cq](future<StatusOr<btadmin::Cluster>> f) mutable {
502 cq.Shutdown();
503 return f.get();
504 });
505 }
506
507 future<StatusOr<google::bigtable::admin::v2::Cluster>>
AsyncUpdateCluster(CompletionQueue & cq,ClusterConfig cluster_config)508 InstanceAdmin::AsyncUpdateCluster(CompletionQueue& cq,
509 ClusterConfig cluster_config) {
510 auto request = std::move(cluster_config).as_proto();
511 auto name = request.name();
512
513 std::shared_ptr<InstanceAdminClient> client(client_);
514 return internal::AsyncStartPollAfterRetryUnaryRpc<
515 google::bigtable::admin::v2::Cluster>(
516 __func__, clone_polling_policy(), clone_rpc_retry_policy(),
517 clone_rpc_backoff_policy(), internal::ConstantIdempotencyPolicy(false),
518 MetadataUpdatePolicy(name, MetadataParamTypes::NAME), client,
519 [client](grpc::ClientContext* context,
520 google::bigtable::admin::v2::Cluster const& request,
521 grpc::CompletionQueue* cq) {
522 return client->AsyncUpdateCluster(context, request, cq);
523 },
524 std::move(request), cq);
525 }
526
DeleteCluster(std::string const & instance_id,std::string const & cluster_id)527 Status InstanceAdmin::DeleteCluster(std::string const& instance_id,
528 std::string const& cluster_id) {
529 grpc::Status status;
530 btadmin::DeleteClusterRequest request;
531 auto name = ClusterName(instance_id, cluster_id);
532 request.set_name(name);
533
534 auto metadata_update_policy =
535 MetadataUpdatePolicy(name, MetadataParamTypes::NAME);
536
537 // This API is not idempotent, lets call it without retry
538 ClientUtils::MakeNonIdemponentCall(
539 *(client_), clone_rpc_retry_policy(), metadata_update_policy,
540 &InstanceAdminClient::DeleteCluster, request,
541 "InstanceAdmin::DeleteCluster", status);
542 return MakeStatusFromRpcError(status);
543 }
544
CreateAppProfile(std::string const & instance_id,AppProfileConfig config)545 StatusOr<btadmin::AppProfile> InstanceAdmin::CreateAppProfile(
546 std::string const& instance_id, AppProfileConfig config) {
547 grpc::Status status;
548 auto request = std::move(config).as_proto();
549 auto parent = InstanceName(instance_id);
550 request.set_parent(parent);
551
552 // This is a non-idempotent API, use the correct retry loop for this type of
553 // operation.
554 auto result = ClientUtils::MakeNonIdemponentCall(
555 *(client_), clone_rpc_retry_policy(),
556 MetadataUpdatePolicy(parent, MetadataParamTypes::PARENT),
557 &InstanceAdminClient::CreateAppProfile, request,
558 "InstanceAdmin::CreateAppProfile", status);
559
560 if (!status.ok()) {
561 return MakeStatusFromRpcError(status);
562 }
563 return result;
564 }
565
566 future<StatusOr<google::bigtable::admin::v2::AppProfile>>
AsyncCreateAppProfile(CompletionQueue & cq,std::string const & instance_id,AppProfileConfig config)567 InstanceAdmin::AsyncCreateAppProfile(CompletionQueue& cq,
568 std::string const& instance_id,
569 AppProfileConfig config) {
570 auto request = std::move(config).as_proto();
571 auto parent = InstanceName(instance_id);
572 request.set_parent(parent);
573
574 std::shared_ptr<InstanceAdminClient> client(client_);
575 return google::cloud::internal::StartRetryAsyncUnaryRpc(
576 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
577 /*is_idempotent=*/false,
578 [client, parent](grpc::ClientContext* context,
579 btadmin::CreateAppProfileRequest const& request,
580 grpc::CompletionQueue* cq) {
581 MetadataUpdatePolicy(parent, MetadataParamTypes::PARENT)
582 .Setup(*context);
583 return client->AsyncCreateAppProfile(context, request, cq);
584 },
585 std::move(request));
586 }
587
GetAppProfile(std::string const & instance_id,std::string const & profile_id)588 StatusOr<btadmin::AppProfile> InstanceAdmin::GetAppProfile(
589 std::string const& instance_id, std::string const& profile_id) {
590 grpc::Status status;
591 btadmin::GetAppProfileRequest request;
592 auto name = AppProfileName(instance_id, profile_id);
593 request.set_name(name);
594
595 auto result = ClientUtils::MakeCall(
596 *(client_), clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
597 MetadataUpdatePolicy(name, MetadataParamTypes::NAME),
598 &InstanceAdminClient::GetAppProfile, request,
599 "InstanceAdmin::GetAppProfile", status, true);
600
601 if (!status.ok()) {
602 return MakeStatusFromRpcError(status);
603 }
604 return result;
605 }
606
607 future<StatusOr<google::bigtable::admin::v2::AppProfile>>
AsyncGetAppProfile(CompletionQueue & cq,std::string const & instance_id,std::string const & profile_id)608 InstanceAdmin::AsyncGetAppProfile(CompletionQueue& cq,
609 std::string const& instance_id,
610 std::string const& profile_id) {
611 btadmin::GetAppProfileRequest request;
612 auto name = AppProfileName(instance_id, profile_id);
613 request.set_name(name);
614
615 std::shared_ptr<InstanceAdminClient> client(client_);
616 return google::cloud::internal::StartRetryAsyncUnaryRpc(
617 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
618 /*is_idempotent=*/true,
619 [client, name](grpc::ClientContext* context,
620 btadmin::GetAppProfileRequest const& request,
621 grpc::CompletionQueue* cq) {
622 MetadataUpdatePolicy(name, MetadataParamTypes::NAME).Setup(*context);
623 return client->AsyncGetAppProfile(context, request, cq);
624 },
625 std::move(request));
626 }
627
UpdateAppProfile(std::string const & instance_id,std::string const & profile_id,AppProfileUpdateConfig config)628 future<StatusOr<btadmin::AppProfile>> InstanceAdmin::UpdateAppProfile(
629 std::string const& instance_id, std::string const& profile_id,
630 AppProfileUpdateConfig config) {
631 CompletionQueue cq;
632 std::thread([](CompletionQueue cq) { cq.Run(); }, cq).detach();
633
634 return AsyncUpdateAppProfile(cq, instance_id, profile_id, std::move(config))
635 .then([cq](future<StatusOr<btadmin::AppProfile>> f) mutable {
636 cq.Shutdown();
637 return f.get();
638 });
639 }
640
641 future<StatusOr<google::bigtable::admin::v2::AppProfile>>
AsyncUpdateAppProfile(CompletionQueue & cq,std::string const & instance_id,std::string const & profile_id,AppProfileUpdateConfig config)642 InstanceAdmin::AsyncUpdateAppProfile(CompletionQueue& cq,
643 std::string const& instance_id,
644 std::string const& profile_id,
645 AppProfileUpdateConfig config) {
646 auto request = std::move(config).as_proto();
647 auto name = AppProfileName(instance_id, profile_id);
648 request.mutable_app_profile()->set_name(name);
649
650 std::shared_ptr<InstanceAdminClient> client(client_);
651 return internal::AsyncStartPollAfterRetryUnaryRpc<
652 google::bigtable::admin::v2::AppProfile>(
653 __func__, clone_polling_policy(), clone_rpc_retry_policy(),
654 clone_rpc_backoff_policy(), internal::ConstantIdempotencyPolicy(false),
655 MetadataUpdatePolicy(name, MetadataParamTypes::APP_PROFILE_NAME), client,
656 [client](
657 grpc::ClientContext* context,
658 google::bigtable::admin::v2::UpdateAppProfileRequest const& request,
659 grpc::CompletionQueue* cq) {
660 return client->AsyncUpdateAppProfile(context, request, cq);
661 },
662 std::move(request), cq);
663 }
664
ListAppProfiles(std::string const & instance_id)665 StatusOr<std::vector<btadmin::AppProfile>> InstanceAdmin::ListAppProfiles(
666 std::string const& instance_id) {
667 grpc::Status status;
668 std::vector<btadmin::AppProfile> result;
669 std::string page_token;
670 // Copy the policies in effect for the operation.
671 auto rpc_policy = clone_rpc_retry_policy();
672 auto backoff_policy = clone_rpc_backoff_policy();
673 auto parent = InstanceName(instance_id);
674
675 do {
676 // Build the RPC request, try to minimize copying.
677 btadmin::ListAppProfilesRequest request;
678 request.set_page_token(std::move(page_token));
679 request.set_parent(parent);
680
681 auto response = ClientUtils::MakeCall(
682 *(client_), *rpc_policy, *backoff_policy,
683 MetadataUpdatePolicy(parent, MetadataParamTypes::PARENT),
684 &InstanceAdminClient::ListAppProfiles, request,
685 "InstanceAdmin::ListAppProfiles", status, true);
686 if (!status.ok()) {
687 break;
688 }
689
690 for (auto& x : *response.mutable_app_profiles()) {
691 result.emplace_back(std::move(x));
692 }
693 page_token = std::move(*response.mutable_next_page_token());
694 } while (!page_token.empty());
695
696 if (!status.ok()) {
697 return MakeStatusFromRpcError(status);
698 }
699 return result;
700 }
701
702 future<StatusOr<std::vector<btadmin::AppProfile>>>
AsyncListAppProfiles(CompletionQueue & cq,std::string const & instance_id)703 InstanceAdmin::AsyncListAppProfiles(CompletionQueue& cq,
704 std::string const& instance_id) {
705 auto client = client_;
706 btadmin::ListAppProfilesRequest request;
707 auto parent = InstanceName(instance_id);
708 request.set_parent(parent);
709
710 return internal::StartAsyncRetryMultiPage(
711 __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
712 MetadataUpdatePolicy(parent, MetadataParamTypes::PARENT),
713 [client](grpc::ClientContext* context,
714 btadmin::ListAppProfilesRequest const& request,
715 grpc::CompletionQueue* cq) {
716 return client->AsyncListAppProfiles(context, request, cq);
717 },
718 std::move(request), std::vector<btadmin::AppProfile>(),
719 [](std::vector<btadmin::AppProfile> acc,
720 btadmin::ListAppProfilesResponse const& response) {
721 std::move(response.app_profiles().begin(),
722 response.app_profiles().end(), std::back_inserter(acc));
723 return acc;
724 },
725 cq);
726 }
727
DeleteAppProfile(std::string const & instance_id,std::string const & profile_id,bool ignore_warnings)728 Status InstanceAdmin::DeleteAppProfile(std::string const& instance_id,
729 std::string const& profile_id,
730 bool ignore_warnings) {
731 grpc::Status status;
732 btadmin::DeleteAppProfileRequest request;
733 auto name = AppProfileName(instance_id, profile_id);
734 request.set_name(name);
735 request.set_ignore_warnings(ignore_warnings);
736
737 ClientUtils::MakeNonIdemponentCall(
738 *(client_), clone_rpc_retry_policy(),
739 MetadataUpdatePolicy(name, MetadataParamTypes::NAME),
740 &InstanceAdminClient::DeleteAppProfile, request,
741 "InstanceAdmin::DeleteAppProfile", status);
742
743 return MakeStatusFromRpcError(status);
744 }
745
AsyncDeleteAppProfile(CompletionQueue & cq,std::string const & instance_id,std::string const & profile_id,bool ignore_warnings)746 future<Status> InstanceAdmin::AsyncDeleteAppProfile(
747 CompletionQueue& cq, std::string const& instance_id,
748 std::string const& profile_id, bool ignore_warnings) {
749 btadmin::DeleteAppProfileRequest request;
750 auto name = AppProfileName(instance_id, profile_id);
751 request.set_name(name);
752 request.set_ignore_warnings(ignore_warnings);
753
754 std::shared_ptr<InstanceAdminClient> client(client_);
755 return google::cloud::internal::StartRetryAsyncUnaryRpc(
756 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
757 /*is_idempotent=*/false,
758 [client, name](grpc::ClientContext* context,
759 btadmin::DeleteAppProfileRequest const& request,
760 grpc::CompletionQueue* cq) {
761 MetadataUpdatePolicy(name, MetadataParamTypes::NAME)
762 .Setup(*context);
763 return client->AsyncDeleteAppProfile(context, request, cq);
764 },
765 std::move(request))
766 .then([](future<StatusOr<google::protobuf::Empty>> fut) {
767 auto res = fut.get();
768 if (res) {
769 return google::cloud::Status();
770 }
771 return res.status();
772 });
773 }
774
GetIamPolicy(std::string const & instance_id)775 StatusOr<google::cloud::IamPolicy> InstanceAdmin::GetIamPolicy(
776 std::string const& instance_id) {
777 grpc::Status status;
778 auto rpc_policy = clone_rpc_retry_policy();
779 auto backoff_policy = clone_rpc_backoff_policy();
780
781 ::google::iam::v1::GetIamPolicyRequest request;
782 auto resource = InstanceName(instance_id);
783 request.set_resource(resource);
784
785 MetadataUpdatePolicy metadata_update_policy(resource,
786 MetadataParamTypes::RESOURCE);
787
788 auto proto = ClientUtils::MakeCall(
789 *(client_), *rpc_policy, *backoff_policy, metadata_update_policy,
790 &InstanceAdminClient::GetIamPolicy, request,
791 "InstanceAdmin::GetIamPolicy", status, true);
792
793 if (!status.ok()) {
794 return MakeStatusFromRpcError(status);
795 }
796
797 return ProtoToWrapper(std::move(proto));
798 }
799
GetNativeIamPolicy(std::string const & instance_id)800 StatusOr<google::iam::v1::Policy> InstanceAdmin::GetNativeIamPolicy(
801 std::string const& instance_id) {
802 grpc::Status status;
803 auto rpc_policy = clone_rpc_retry_policy();
804 auto backoff_policy = clone_rpc_backoff_policy();
805
806 ::google::iam::v1::GetIamPolicyRequest request;
807 auto resource = InstanceName(instance_id);
808 request.set_resource(resource);
809
810 MetadataUpdatePolicy metadata_update_policy(resource,
811 MetadataParamTypes::RESOURCE);
812
813 auto proto = ClientUtils::MakeCall(
814 *(client_), *rpc_policy, *backoff_policy, metadata_update_policy,
815 &InstanceAdminClient::GetIamPolicy, request,
816 "InstanceAdmin::GetIamPolicy", status, true);
817
818 if (!status.ok()) {
819 return MakeStatusFromRpcError(status);
820 }
821
822 return proto;
823 }
824
AsyncGetIamPolicy(CompletionQueue & cq,std::string const & instance_id)825 future<StatusOr<google::cloud::IamPolicy>> InstanceAdmin::AsyncGetIamPolicy(
826 CompletionQueue& cq, std::string const& instance_id) {
827 ::google::iam::v1::GetIamPolicyRequest request;
828
829 auto resource = InstanceName(instance_id);
830 request.set_resource(resource);
831
832 std::shared_ptr<InstanceAdminClient> client(client_);
833 return google::cloud::internal::StartRetryAsyncUnaryRpc(
834 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
835 /*is_idempotent=*/true,
836 [client, resource](
837 grpc::ClientContext* context,
838 ::google::iam::v1::GetIamPolicyRequest const& request,
839 grpc::CompletionQueue* cq) {
840 MetadataUpdatePolicy(resource, MetadataParamTypes::RESOURCE)
841 .Setup(*context);
842 return client->AsyncGetIamPolicy(context, request, cq);
843 },
844 std::move(request))
845 .then([](future<StatusOr<::google::iam::v1::Policy>> fut)
846 -> StatusOr<google::cloud::IamPolicy> {
847 auto res = fut.get();
848 if (!res) {
849 return res.status();
850 }
851 return ProtoToWrapper(std::move(*res));
852 });
853 }
854
855 future<StatusOr<google::iam::v1::Policy>>
AsyncGetNativeIamPolicy(CompletionQueue & cq,std::string const & instance_id)856 InstanceAdmin::AsyncGetNativeIamPolicy(CompletionQueue& cq,
857 std::string const& instance_id) {
858 ::google::iam::v1::GetIamPolicyRequest request;
859 auto resource = InstanceName(instance_id);
860 request.set_resource(resource);
861
862 std::shared_ptr<InstanceAdminClient> client(client_);
863 return google::cloud::internal::StartRetryAsyncUnaryRpc(
864 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
865 /*is_idempotent=*/true,
866 [client, resource](grpc::ClientContext* context,
867 ::google::iam::v1::GetIamPolicyRequest const& request,
868 grpc::CompletionQueue* cq) {
869 MetadataUpdatePolicy(resource, MetadataParamTypes::RESOURCE)
870 .Setup(*context);
871 return client->AsyncGetIamPolicy(context, request, cq);
872 },
873 std::move(request));
874 }
875
SetIamPolicy(std::string const & instance_id,google::cloud::IamBindings const & iam_bindings,std::string const & etag)876 StatusOr<google::cloud::IamPolicy> InstanceAdmin::SetIamPolicy(
877 std::string const& instance_id,
878 google::cloud::IamBindings const& iam_bindings, std::string const& etag) {
879 grpc::Status status;
880 auto rpc_policy = clone_rpc_retry_policy();
881 auto backoff_policy = clone_rpc_backoff_policy();
882
883 ::google::iam::v1::Policy policy;
884 policy.set_etag(etag);
885 auto role_bindings = iam_bindings.bindings();
886 for (auto& binding : role_bindings) {
887 auto new_binding = policy.add_bindings();
888 new_binding->set_role(binding.first);
889 for (auto& member : binding.second) {
890 new_binding->add_members(member);
891 }
892 }
893
894 ::google::iam::v1::SetIamPolicyRequest request;
895 auto resource = InstanceName(instance_id);
896 request.set_resource(resource);
897 *request.mutable_policy() = std::move(policy);
898
899 MetadataUpdatePolicy metadata_update_policy(resource,
900 MetadataParamTypes::RESOURCE);
901
902 auto proto = ClientUtils::MakeCall(
903 *(client_), *rpc_policy, *backoff_policy, metadata_update_policy,
904 &InstanceAdminClient::SetIamPolicy, request,
905 "InstanceAdmin::SetIamPolicy", status, true);
906
907 if (!status.ok()) {
908 return MakeStatusFromRpcError(status);
909 }
910
911 return ProtoToWrapper(std::move(proto));
912 }
913
SetIamPolicy(std::string const & instance_id,google::iam::v1::Policy const & iam_policy)914 StatusOr<google::iam::v1::Policy> InstanceAdmin::SetIamPolicy(
915 std::string const& instance_id, google::iam::v1::Policy const& iam_policy) {
916 grpc::Status status;
917 auto rpc_policy = clone_rpc_retry_policy();
918 auto backoff_policy = clone_rpc_backoff_policy();
919
920 ::google::iam::v1::SetIamPolicyRequest request;
921 auto resource = InstanceName(instance_id);
922 request.set_resource(resource);
923 *request.mutable_policy() = iam_policy;
924
925 MetadataUpdatePolicy metadata_update_policy(resource,
926 MetadataParamTypes::RESOURCE);
927
928 auto proto = ClientUtils::MakeCall(
929 *(client_), *rpc_policy, *backoff_policy, metadata_update_policy,
930 &InstanceAdminClient::SetIamPolicy, request,
931 "InstanceAdmin::SetIamPolicy", status, true);
932
933 if (!status.ok()) {
934 return MakeStatusFromRpcError(status);
935 }
936
937 return proto;
938 }
939
AsyncSetIamPolicy(CompletionQueue & cq,std::string const & instance_id,google::cloud::IamBindings const & iam_bindings,std::string const & etag)940 future<StatusOr<google::cloud::IamPolicy>> InstanceAdmin::AsyncSetIamPolicy(
941 CompletionQueue& cq, std::string const& instance_id,
942 google::cloud::IamBindings const& iam_bindings, std::string const& etag) {
943 ::google::iam::v1::Policy policy;
944 policy.set_etag(etag);
945 auto role_bindings = iam_bindings.bindings();
946 for (auto& binding : role_bindings) {
947 auto new_binding = policy.add_bindings();
948 new_binding->set_role(binding.first);
949 for (auto& member : binding.second) {
950 new_binding->add_members(member);
951 }
952 }
953
954 ::google::iam::v1::SetIamPolicyRequest request;
955 auto resource = InstanceName(instance_id);
956 request.set_resource(resource);
957 *request.mutable_policy() = std::move(policy);
958
959 std::shared_ptr<InstanceAdminClient> client(client_);
960 return google::cloud::internal::StartRetryAsyncUnaryRpc(
961 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
962 /*is_idempotent=*/false,
963 [client, resource](
964 grpc::ClientContext* context,
965 ::google::iam::v1::SetIamPolicyRequest const& request,
966 grpc::CompletionQueue* cq) {
967 MetadataUpdatePolicy(resource, MetadataParamTypes::RESOURCE)
968 .Setup(*context);
969 return client->AsyncSetIamPolicy(context, request, cq);
970 },
971 std::move(request))
972 .then([](future<StatusOr<::google::iam::v1::Policy>> response_fut)
973 -> StatusOr<google::cloud::IamPolicy> {
974 auto response = response_fut.get();
975 if (!response) {
976 return response.status();
977 }
978 return ProtoToWrapper(std::move(*response));
979 });
980 }
981
AsyncSetIamPolicy(CompletionQueue & cq,std::string const & instance_id,google::iam::v1::Policy const & iam_policy)982 future<StatusOr<google::iam::v1::Policy>> InstanceAdmin::AsyncSetIamPolicy(
983 CompletionQueue& cq, std::string const& instance_id,
984 google::iam::v1::Policy const& iam_policy) {
985 ::google::iam::v1::SetIamPolicyRequest request;
986 auto resource = InstanceName(instance_id);
987 request.set_resource(resource);
988 *request.mutable_policy() = iam_policy;
989
990 std::shared_ptr<InstanceAdminClient> client(client_);
991 return google::cloud::internal::StartRetryAsyncUnaryRpc(
992 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
993 /*is_idempotent=*/false,
994 [client, resource](grpc::ClientContext* context,
995 ::google::iam::v1::SetIamPolicyRequest const& request,
996 grpc::CompletionQueue* cq) {
997 MetadataUpdatePolicy(resource, MetadataParamTypes::RESOURCE)
998 .Setup(*context);
999 return client->AsyncSetIamPolicy(context, request, cq);
1000 },
1001 std::move(request));
1002 }
1003
TestIamPermissions(std::string const & instance_id,std::vector<std::string> const & permissions)1004 StatusOr<std::vector<std::string>> InstanceAdmin::TestIamPermissions(
1005 std::string const& instance_id,
1006 std::vector<std::string> const& permissions) {
1007 grpc::Status status;
1008 ::google::iam::v1::TestIamPermissionsRequest request;
1009 auto resource = InstanceName(instance_id);
1010 request.set_resource(resource);
1011
1012 // Copy the policies in effect for the operation.
1013 auto rpc_policy = clone_rpc_retry_policy();
1014 auto backoff_policy = clone_rpc_backoff_policy();
1015
1016 for (auto& permission : permissions) {
1017 request.add_permissions(permission);
1018 }
1019
1020 MetadataUpdatePolicy metadata_update_policy(resource,
1021 MetadataParamTypes::RESOURCE);
1022
1023 auto response = ClientUtils::MakeCall(
1024 *(client_), *rpc_policy, *backoff_policy, metadata_update_policy,
1025 &InstanceAdminClient::TestIamPermissions, request,
1026 "InstanceAdmin::TestIamPermissions", status, true);
1027
1028 std::vector<std::string> resource_permissions;
1029
1030 for (auto& permission : *response.mutable_permissions()) {
1031 resource_permissions.push_back(permission);
1032 }
1033
1034 if (!status.ok()) {
1035 return MakeStatusFromRpcError(status);
1036 }
1037
1038 return resource_permissions;
1039 }
1040
1041 future<StatusOr<std::vector<std::string>>>
AsyncTestIamPermissions(CompletionQueue & cq,std::string const & instance_id,std::vector<std::string> const & permissions)1042 InstanceAdmin::AsyncTestIamPermissions(
1043 CompletionQueue& cq, std::string const& instance_id,
1044 std::vector<std::string> const& permissions) {
1045 ::google::iam::v1::TestIamPermissionsRequest request;
1046 auto resource = InstanceName(instance_id);
1047 request.set_resource(resource);
1048 for (auto& permission : permissions) {
1049 request.add_permissions(permission);
1050 }
1051
1052 auto client = client_;
1053 return google::cloud::internal::StartRetryAsyncUnaryRpc(
1054 cq, __func__, clone_rpc_retry_policy(), clone_rpc_backoff_policy(),
1055 /*is_idempotent=*/true,
1056 [client, resource](
1057 grpc::ClientContext* context,
1058 ::google::iam::v1::TestIamPermissionsRequest const& request,
1059 grpc::CompletionQueue* cq) {
1060 MetadataUpdatePolicy(resource, MetadataParamTypes::RESOURCE)
1061 .Setup(*context);
1062 return client->AsyncTestIamPermissions(context, request, cq);
1063 },
1064 std::move(request))
1065 .then([](future<StatusOr<::google::iam::v1::TestIamPermissionsResponse>>
1066 response_fut) -> StatusOr<std::vector<std::string>> {
1067 auto response = response_fut.get();
1068 if (!response) {
1069 return response.status();
1070 }
1071 std::vector<std::string> res;
1072 res.reserve(response->permissions_size());
1073 std::move(response->mutable_permissions()->begin(),
1074 response->mutable_permissions()->end(),
1075 std::back_inserter(res));
1076 return res;
1077 });
1078 }
1079
ProtoToWrapper(google::iam::v1::Policy proto)1080 StatusOr<google::cloud::IamPolicy> InstanceAdmin::ProtoToWrapper(
1081 google::iam::v1::Policy proto) {
1082 google::cloud::IamPolicy result;
1083 result.version = proto.version();
1084 result.etag = std::move(*proto.mutable_etag());
1085 for (auto& binding : *proto.mutable_bindings()) {
1086 std::vector<google::protobuf::FieldDescriptor const*> field_descs;
1087 // On newer versions of Protobuf (circa 3.9.1) `GetReflection()` changed
1088 // from a virtual member function to a static member function. clang-tidy
1089 // warns (and we turn all warnings to errors) about using the static
1090 // version via the object, as we do below. Disable the warning because that
1091 // seems like the only portable solution.
1092 // NOLINTNEXTLINE(readability-static-accessed-through-instance)
1093 binding.GetReflection()->ListFields(binding, &field_descs);
1094 for (auto field_desc : field_descs) {
1095 if (field_desc->name() != "members" && field_desc->name() != "role") {
1096 std::stringstream os;
1097 os << "IamBinding field \"" << field_desc->name()
1098 << "\" is unknown to Bigtable C++ client. Please use "
1099 "[Async]GetNativeIamPolicy() and their respective "
1100 "[Async]SetIamPolicy() overloads.";
1101 return Status(StatusCode::kUnimplemented, os.str());
1102 }
1103 }
1104 for (auto& member : *binding.mutable_members()) {
1105 result.bindings.AddMember(binding.role(), std::move(member));
1106 }
1107 }
1108 return result;
1109 }
1110
1111 } // namespace BIGTABLE_CLIENT_NS
1112 } // namespace bigtable
1113 } // namespace cloud
1114 } // namespace google
1115