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