Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bigtable): Backup Level IAM #3222

Merged
merged 17 commits into from Dec 29, 2020
Merged
51 changes: 51 additions & 0 deletions bigtable/admin.go
Expand Up @@ -39,6 +39,7 @@ import (
"google.golang.org/api/option"
gtransport "google.golang.org/api/transport/grpc"
btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/metadata"
)
Expand Down Expand Up @@ -1663,3 +1664,53 @@ func (ac *AdminClient) UpdateBackup(ctx context.Context, cluster, backup string,
_, err = ac.tClient.UpdateBackup(ctx, req)
return err
}

// GetIamPolicy gets the IAM access control policy for the specified backup.
func (ac *AdminClient) GetIamPolicy(ctx context.Context, cluster, backup string) (*iampb.Policy, error) {
AlisskaPie marked this conversation as resolved.
Show resolved Hide resolved
AlisskaPie marked this conversation as resolved.
Show resolved Hide resolved
ctx = mergeOutgoingMetadata(ctx, ac.md)
prefix := ac.instancePrefix()
backupPath := prefix + "/clusters/" + cluster + "/backups/" + backup

req := iampb.GetIamPolicyRequest{Resource: backupPath}
AlisskaPie marked this conversation as resolved.
Show resolved Hide resolved
policy, err := ac.tClient.GetIamPolicy(ctx, &req)
if err != nil {
return nil, err
}
return policy, nil
}

// SetIamPolicy sets the IAM access control policy for the specified backup.
// Replaces any existing policy.
func (ac *AdminClient) SetIamPolicy(ctx context.Context, cluster, backup string, policy *iampb.Policy) (*iampb.Policy, error) {
AlisskaPie marked this conversation as resolved.
Show resolved Hide resolved
ctx = mergeOutgoingMetadata(ctx, ac.md)
prefix := ac.instancePrefix()
backupPath := prefix + "/clusters/" + cluster + "/backups/" + backup

req := iampb.SetIamPolicyRequest{
Resource: backupPath,
Policy: policy,
}
policy, err := ac.tClient.SetIamPolicy(ctx, &req)
if err != nil {
return nil, err
}
return policy, nil
}

// TestIamPermissions tests whether the caller has the given permissions for this backup.
// Returns the permissions that the caller has.
func (ac *AdminClient) TestIamPermissions(ctx context.Context, cluster, backup string, permissions []string) ([]string, error) {
ctx = mergeOutgoingMetadata(ctx, ac.md)
prefix := ac.instancePrefix()
backupPath := prefix + "/clusters/" + cluster + "/backups/" + backup

req := iampb.TestIamPermissionsRequest{
Resource: backupPath,
Permissions: permissions,
}
resp, err := ac.tClient.TestIamPermissions(ctx, &req)
if err != nil {
return nil, err
}
return resp.GetPermissions(), nil
}
78 changes: 71 additions & 7 deletions bigtable/integration_test.go
Expand Up @@ -37,6 +37,8 @@ import (
gax "github.com/googleapis/gax-go/v2"
"google.golang.org/api/iterator"
btapb "google.golang.org/genproto/googleapis/bigtable/admin/v2"
iampb "google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc"
)

var (
Expand Down Expand Up @@ -2045,9 +2047,10 @@ func TestIntegration_AdminBackup(t *testing.T) {
}

// Create backup
backupName := "mybackup"
defer adminClient.DeleteBackup(ctx, cluster, "mybackup")

if err = adminClient.CreateBackup(ctx, table, cluster, "mybackup", time.Now().Add(8*time.Hour)); err != nil {
if err = adminClient.CreateBackup(ctx, table, cluster, backupName, time.Now().Add(8*time.Hour)); err != nil {
t.Fatalf("Creating backup: %v", err)
}

Expand All @@ -2059,7 +2062,7 @@ func TestIntegration_AdminBackup(t *testing.T) {
if got, want := len(backups), 1; got != want {
t.Fatalf("Listing backup count: %d, want: %d", got, want)
}
if got, want := backups[0].Name, "mybackup"; got != want {
if got, want := backups[0].Name, backupName; got != want {
t.Fatalf("Backup name: %s, want: %s", got, want)
}
if got, want := backups[0].SourceTable, table; got != want {
Expand All @@ -2070,23 +2073,65 @@ func TestIntegration_AdminBackup(t *testing.T) {
}

// Get backup
backup, err := adminClient.BackupInfo(ctx, cluster, "mybackup")
backup, err := adminClient.BackupInfo(ctx, cluster, backupName)
if err != nil {
t.Fatalf("BackupInfo: %v", backup)
}
if got, want := *backup, *backups[0]; got != want {
t.Fatalf("BackupInfo: %v, want: %v", got, want)
}

// Mock setup.
policy := &iampb.Policy{}
role := "roles/bigtable.viewer"
members := []string{"serviceAccount:service_acc1@test.com", "user:user1@test.com"}
policy.Version = 1
policy.Etag = []byte("etag_v1")
policy.Bindings = append(policy.Bindings, &iampb.Binding{
Role: role,
Members: members,
})
permissions := []string{"bigtable.backups.create", "bigtable.backups.list"}

tClient := adminClient.tClient
adminClient.tClient = &adminClientMock{
Policy: policy,
Permissions: permissions,
}
// Set backup policy.
newPolicy, err := adminClient.SetIamPolicy(ctx, cluster, backupName, policy)
if err != nil {
t.Fatalf("SetIAMPolicy: %v", err)
}
if diff := testutil.Diff(newPolicy, policy); diff != "" {
t.Fatalf("SetIAMPolicy: got - want +\n%s", diff)
}
// Get backup policy.
gotPolicy, err := adminClient.GetIamPolicy(ctx, cluster, backupName)
if err != nil {
t.Fatalf("GetIAMPolicy: %v", err)
}
if diff := testutil.Diff(gotPolicy, policy); diff != "" {
t.Fatalf("GetIAMPolicy: got - want +\n%s", diff)
}
// Test backup permissions.
gotPermissions, err := adminClient.TestIamPermissions(ctx, cluster, backupName, permissions)
if err != nil {
t.Fatalf("TestIAMPermissions: %v", err)
}
if !testutil.Equal(gotPermissions, permissions) {
t.Fatalf("TestIAMPermissions: got %#v\nwant %#v", gotPermissions, permissions)
}
// Update backup
adminClient.tClient = tClient
newExpireTime := time.Now().Add(10 * time.Hour)
err = adminClient.UpdateBackup(ctx, cluster, "mybackup", newExpireTime)
err = adminClient.UpdateBackup(ctx, cluster, backupName, newExpireTime)
if err != nil {
t.Fatalf("UpdateBackup failed: %v", err)
}

// Check that updated backup has the correct expire time
updatedBackup, err := adminClient.BackupInfo(ctx, cluster, "mybackup")
updatedBackup, err := adminClient.BackupInfo(ctx, cluster, backupName)
if err != nil {
t.Fatalf("BackupInfo: %v", err)
}
Expand All @@ -2099,15 +2144,15 @@ func TestIntegration_AdminBackup(t *testing.T) {
// Restore backup
restoredTable := table + "-restored"
defer deleteTable(ctx, t, adminClient, restoredTable)
if err = adminClient.RestoreTable(ctx, restoredTable, cluster, "mybackup"); err != nil {
if err = adminClient.RestoreTable(ctx, restoredTable, cluster, backupName); err != nil {
t.Fatalf("RestoreTable: %v", err)
}
if _, err := adminClient.TableInfo(ctx, restoredTable); err != nil {
t.Fatalf("Restored TableInfo: %v", err)
}

// Delete backup
if err = adminClient.DeleteBackup(ctx, cluster, "mybackup"); err != nil {
if err = adminClient.DeleteBackup(ctx, cluster, backupName); err != nil {
t.Fatalf("DeleteBackup: %v", err)
}
backups, err = list(cluster)
Expand Down Expand Up @@ -2212,3 +2257,22 @@ func deleteTable(ctx context.Context, t *testing.T, ac *AdminClient, name string
t.Logf("DeleteTable: %v", err)
}
}

// adminClientMock is used to test backup level IAM.
type adminClientMock struct {
btapb.BigtableTableAdminClient
Policy *iampb.Policy
Permissions []string
}

func (acm *adminClientMock) GetIamPolicy(ctx context.Context, in *iampb.GetIamPolicyRequest, opts ...grpc.CallOption) (*iampb.Policy, error) {
return acm.Policy, nil
}

func (acm *adminClientMock) SetIamPolicy(ctx context.Context, in *iampb.SetIamPolicyRequest, opts ...grpc.CallOption) (*iampb.Policy, error) {
return acm.Policy, nil
}

func (acm *adminClientMock) TestIamPermissions(ctx context.Context, in *iampb.TestIamPermissionsRequest, opts ...grpc.CallOption) (*iampb.TestIamPermissionsResponse, error) {
return &iampb.TestIamPermissionsResponse{Permissions: acm.Permissions}, nil
}