Skip to content
This repository was archived by the owner on Dec 8, 2021. It is now read-only.

Commit 10eca21

Browse files
authored
feat: implement InstanceAdminClient::GetIamPolicy (#609)
Add the function, the usual integration test, unit tests, and a simple example.
1 parent 3c53269 commit 10eca21

14 files changed

+180
-1
lines changed

google/cloud/spanner/instance_admin_client.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ ListInstancesRange InstanceAdminClient::ListInstances(std::string project_id,
2828
return conn_->ListInstances({std::move(project_id), std::move(filter)});
2929
}
3030

31+
StatusOr<google::iam::v1::Policy> InstanceAdminClient::GetIamPolicy(
32+
Instance const& in) {
33+
return conn_->GetIamPolicy({in.FullName()});
34+
}
35+
3136
StatusOr<google::iam::v1::TestIamPermissionsResponse>
3237
InstanceAdminClient::TestIamPermissions(Instance const& in,
3338
std::vector<std::string> permissions) {

google/cloud/spanner/instance_admin_client.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,29 @@ class InstanceAdminClient {
112112
*/
113113
ListInstancesRange ListInstances(std::string project_id, std::string filter);
114114

115+
/**
116+
* Get the IAM policy in effect for the given instance.
117+
*
118+
* This function retrieves the IAM policy configured in the given instance,
119+
* that is, which roles are enabled in the instance, and what entities are
120+
* members of each role.
121+
*
122+
* @par Idempotency
123+
* This is a read-only operation and therefore it is always treated as
124+
* idempotent.
125+
*
126+
* @par Example
127+
* @snippet samples.cc instance-get-iam-policy
128+
*
129+
* @see The [Cloud Spanner
130+
* documentation](https://cloud.google.com/spanner/docs/iam) for a
131+
* description of the roles and permissions supported by Cloud Spanner.
132+
* @see [IAM Overview](https://cloud.google.com/iam/docs/overview#permissions)
133+
* for an introduction to Identity and Access Management in Google Cloud
134+
* Platform.
135+
*/
136+
StatusOr<google::iam::v1::Policy> GetIamPolicy(Instance const& in);
137+
115138
/**
116139
* Get the subset of the permissions the caller has on the given instance.
117140
*

google/cloud/spanner/instance_admin_client_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ TEST(InstanceAdminClientTest, ListInstances) {
9393
EXPECT_EQ(StatusCode::kPermissionDenied, begin->status().code());
9494
}
9595

96+
TEST(InstanceAdminClientTest, GetIamPolicy) {
97+
auto mock = std::make_shared<MockInstanceAdminConnection>();
98+
EXPECT_CALL(*mock, GetIamPolicy(_))
99+
.WillOnce([](InstanceAdminConnection::GetIamPolicyParams const& p) {
100+
EXPECT_EQ("projects/test-project/instances/test-instance",
101+
p.instance_name);
102+
return Status(StatusCode::kPermissionDenied, "uh-oh");
103+
});
104+
105+
InstanceAdminClient client(mock);
106+
auto actual = client.GetIamPolicy(Instance("test-project", "test-instance"));
107+
EXPECT_EQ(StatusCode::kPermissionDenied, actual.status().code());
108+
}
109+
96110
TEST(InstanceAdminClientTest, TestIamPermissions) {
97111
auto mock = std::make_shared<MockInstanceAdminConnection>();
98112
EXPECT_CALL(*mock, TestIamPermissions(_))

google/cloud/spanner/instance_admin_connection.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ class InstanceAdminConnectionImpl : public InstanceAdminConnection {
5656
});
5757
}
5858

59+
StatusOr<google::iam::v1::Policy> GetIamPolicy(
60+
GetIamPolicyParams p) override {
61+
google::iam::v1::GetIamPolicyRequest request;
62+
request.set_resource(std::move(p.instance_name));
63+
grpc::ClientContext context;
64+
return stub_->GetIamPolicy(context, request);
65+
}
66+
5967
StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
6068
TestIamPermissionsParams p) override {
6169
google::iam::v1::TestIamPermissionsRequest request;

google/cloud/spanner/instance_admin_connection.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ class InstanceAdminConnection {
9595
std::string filter;
9696
};
9797

98+
/// Wrap the arguments for `GetIamPolicy()`.
99+
struct GetIamPolicyParams {
100+
std::string instance_name;
101+
};
102+
98103
/// Wrap the arguments for `TestIamPermissions()`.
99104
struct TestIamPermissionsParams {
100105
std::string instance_name;
@@ -112,6 +117,11 @@ class InstanceAdminConnection {
112117
*/
113118
virtual ListInstancesRange ListInstances(ListInstancesParams params) = 0;
114119

120+
/// Define the interface for a
121+
/// google.spanner.v1.DatabaseAdmin.GetIamPolicy RPC.
122+
virtual StatusOr<google::iam::v1::Policy> GetIamPolicy(
123+
GetIamPolicyParams) = 0;
124+
115125
/// Define the interface for a
116126
/// google.spanner.v1.DatabaseAdmin.TestIamPermissions RPC.
117127
virtual StatusOr<google::iam::v1::TestIamPermissionsResponse>

google/cloud/spanner/instance_admin_connection_test.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,58 @@ TEST(InstanceAdminConnectionTest, ListInstances_TooManyTransients) {
170170
EXPECT_EQ(StatusCode::kUnavailable, begin->status().code());
171171
}
172172

173+
TEST(InstanceAdminConnectionTest, GetIamPolicy_Success) {
174+
std::string const expected_name =
175+
"projects/test-project/instances/test-instance";
176+
177+
auto mock = std::make_shared<spanner_testing::MockInstanceAdminStub>();
178+
EXPECT_CALL(*mock, GetIamPolicy(_, _))
179+
.WillOnce(
180+
Invoke([&expected_name](grpc::ClientContext&,
181+
giam::GetIamPolicyRequest const& request) {
182+
EXPECT_EQ(expected_name, request.resource());
183+
return Status(StatusCode::kUnavailable, "try-again");
184+
}))
185+
.WillOnce(
186+
Invoke([&expected_name](grpc::ClientContext&,
187+
giam::GetIamPolicyRequest const& request) {
188+
EXPECT_EQ(expected_name, request.resource());
189+
giam::Policy response;
190+
auto& binding = *response.add_bindings();
191+
binding.set_role("roles/spanner.databaseReader");
192+
binding.add_members("user:[email protected]");
193+
return response;
194+
}));
195+
196+
auto conn = MakeTestConnection(mock);
197+
auto actual = conn->GetIamPolicy({expected_name});
198+
ASSERT_STATUS_OK(actual);
199+
ASSERT_EQ(1, actual->bindings_size());
200+
EXPECT_EQ("roles/spanner.databaseReader", actual->bindings(0).role());
201+
ASSERT_EQ(1, actual->bindings(0).members_size());
202+
ASSERT_EQ("user:[email protected]", actual->bindings(0).members(0));
203+
}
204+
205+
TEST(InstanceAdminConnectionTest, GetIamPolicy_PermanentFailure) {
206+
auto mock = std::make_shared<spanner_testing::MockInstanceAdminStub>();
207+
EXPECT_CALL(*mock, GetIamPolicy(_, _))
208+
.WillOnce(Return(Status(StatusCode::kPermissionDenied, "uh-oh")));
209+
210+
auto conn = MakeTestConnection(mock);
211+
auto actual = conn->GetIamPolicy({"test-instance-name"});
212+
EXPECT_EQ(StatusCode::kPermissionDenied, actual.status().code());
213+
}
214+
215+
TEST(InstanceAdminConnectionTest, GetIamPolicy_TooManyTransients) {
216+
auto mock = std::make_shared<spanner_testing::MockInstanceAdminStub>();
217+
EXPECT_CALL(*mock, GetIamPolicy(_, _))
218+
.WillRepeatedly(Return(Status(StatusCode::kUnavailable, "try-again")));
219+
220+
auto conn = MakeTestConnection(mock);
221+
auto actual = conn->GetIamPolicy({"test-instance-name"});
222+
EXPECT_EQ(StatusCode::kUnavailable, actual.status().code());
223+
}
224+
173225
TEST(InstanceAdminConnectionTest, TestIamPermissions_Success) {
174226
std::string const expected_name =
175227
"projects/test-project/instances/test-instance";

google/cloud/spanner/integration_tests/instance_admin_integration_test.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ TEST(InstanceAdminClient, InstanceIam) {
7171
Instance in(project_id, instance_id);
7272

7373
InstanceAdminClient client(MakeInstanceAdminConnection());
74+
75+
auto actual_policy = client.GetIamPolicy(in);
76+
ASSERT_STATUS_OK(actual_policy);
77+
EXPECT_FALSE(actual_policy->etag().empty());
78+
7479
auto actual = client.TestIamPermissions(
7580
in, {"spanner.databases.list", "spanner.databases.get"});
7681
ASSERT_STATUS_OK(actual);

google/cloud/spanner/internal/instance_admin_retry.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ StatusOr<gcsa::ListInstancesResponse> InstanceAdminRetry::ListInstances(
8585
context, request, __func__);
8686
}
8787

88+
StatusOr<google::iam::v1::Policy> InstanceAdminRetry::GetIamPolicy(
89+
grpc::ClientContext& context,
90+
google::iam::v1::GetIamPolicyRequest const& request) {
91+
return RetryLoop(
92+
retry_policy_->clone(), backoff_policy_->clone(), true,
93+
[this](grpc::ClientContext& context,
94+
giam::GetIamPolicyRequest const& request) {
95+
return child_->GetIamPolicy(context, request);
96+
},
97+
context, request, __func__);
98+
}
99+
88100
StatusOr<giam::TestIamPermissionsResponse>
89101
InstanceAdminRetry::TestIamPermissions(
90102
grpc::ClientContext& context,

google/cloud/spanner/internal/instance_admin_retry.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ class InstanceAdminRetry : public InstanceAdminStub {
5454
grpc::ClientContext&,
5555
google::spanner::admin::instance::v1::ListInstancesRequest const&)
5656
override;
57+
StatusOr<google::iam::v1::Policy> GetIamPolicy(
58+
grpc::ClientContext&,
59+
google::iam::v1::GetIamPolicyRequest const&) override;
5760
StatusOr<google::iam::v1::TestIamPermissionsResponse> TestIamPermissions(
5861
grpc::ClientContext&,
5962
google::iam::v1::TestIamPermissionsRequest const&) override;

google/cloud/spanner/internal/instance_admin_stub.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ class DefaultInstanceAdminStub : public InstanceAdminStub {
5959
return response;
6060
}
6161

62+
StatusOr<giam::Policy> GetIamPolicy(
63+
grpc::ClientContext& context,
64+
giam::GetIamPolicyRequest const& request) override {
65+
giam::Policy response;
66+
auto status = instance_admin_->GetIamPolicy(&context, request, &response);
67+
if (!status.ok()) {
68+
return grpc_utils::MakeStatusFromRpcError(status);
69+
}
70+
return response;
71+
}
72+
6273
StatusOr<giam::TestIamPermissionsResponse> TestIamPermissions(
6374
grpc::ClientContext& context,
6475
giam::TestIamPermissionsRequest const& request) override {

0 commit comments

Comments
 (0)