Skip to content

Commit e80700e

Browse files
melinathanoopkverma-google
authored andcommitted
Implemented simpler IAM bootstrapping logic (GoogleCloudPlatform#12796)
1 parent 800b65a commit e80700e

File tree

2 files changed

+65
-37
lines changed

2 files changed

+65
-37
lines changed

mmv1/third_party/terraform/acctest/bootstrap_iam_test_utils.go

+40-19
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,25 @@ package acctest
33
import (
44
"fmt"
55
"log"
6+
"strconv"
7+
"strings"
68
"testing"
9+
"time"
710

811
"github.com/hashicorp/terraform-provider-google/google/envvar"
912
"github.com/hashicorp/terraform-provider-google/google/tpgiamresource"
1013
cloudresourcemanager "google.golang.org/api/cloudresourcemanager/v1"
1114
)
1215

13-
// BootstrapAllPSARoles ensures that the given project's IAM
14-
// policy grants the given service agents the given roles.
15-
// prefix is usually "service-" and indicates the service agent should have the
16-
// given prefix before the project number.
17-
// This is important to bootstrap because using iam policy resources means that
18-
// deleting them removes permissions for concurrent tests.
19-
// Return whether the bindings changed.
20-
func BootstrapAllPSARoles(t *testing.T, prefix string, agentNames, roles []string) bool {
16+
type IamMember struct {
17+
Member, Role string
18+
}
19+
20+
// BootstrapIamMembers ensures that a given set of member/role pairs exist in the default
21+
// test project. This should be used to avoid race conditions that can happen on the
22+
// default project due to parallel tests managing the same member/role pairings. Members
23+
// will have `{project_number}` replaced with the default test project's project number.
24+
func BootstrapIamMembers(t *testing.T, members []IamMember) {
2125
config := BootstrapConfig(t)
2226
if config == nil {
2327
t.Fatal("Could not bootstrap a config for BootstrapAllPSARoles.")
@@ -36,17 +40,12 @@ func BootstrapAllPSARoles(t *testing.T, prefix string, agentNames, roles []strin
3640
t.Fatalf("Error getting project iam policy: %v", err)
3741
}
3842

39-
members := make([]string, len(agentNames))
40-
for i, agentName := range agentNames {
41-
members[i] = fmt.Sprintf("serviceAccount:%s%d@%s.iam.gserviceaccount.com", prefix, project.ProjectNumber, agentName)
42-
}
43-
4443
// Create the bindings we need to add to the policy.
4544
var newBindings []*cloudresourcemanager.Binding
46-
for _, role := range roles {
45+
for _, member := range members {
4746
newBindings = append(newBindings, &cloudresourcemanager.Binding{
48-
Role: role,
49-
Members: members,
47+
Role: member.Role,
48+
Members: []string{strings.ReplaceAll(member.Member, "{project_number}", strconv.FormatInt(project.ProjectNumber, 10))},
5049
})
5150
}
5251

@@ -68,10 +67,32 @@ func BootstrapAllPSARoles(t *testing.T, prefix string, agentNames, roles []strin
6867
for _, binding := range addedBindings {
6968
msg += fmt.Sprintf("Members: %q, Role: %q\n", binding.Members, binding.Role)
7069
}
71-
msg += "Retry the test in a few minutes."
72-
t.Error(msg)
73-
return true
70+
msg += "Waiting for IAM to propagate."
71+
t.Log(msg)
72+
time.Sleep(3 * time.Minute)
73+
}
74+
}
75+
76+
// BootstrapAllPSARoles ensures that the given project's IAM
77+
// policy grants the given service agents the given roles.
78+
// prefix is usually "service-" and indicates the service agent should have the
79+
// given prefix before the project number.
80+
// This is important to bootstrap because using iam policy resources means that
81+
// deleting them removes permissions for concurrent tests.
82+
// Return whether the bindings changed.
83+
func BootstrapAllPSARoles(t *testing.T, prefix string, agentNames, roles []string) bool {
84+
var members []IamMember
85+
for _, agentName := range agentNames {
86+
member := fmt.Sprintf("serviceAccount:%s{project_number}@%s.iam.gserviceaccount.com", prefix, agentName)
87+
for _, role := range roles {
88+
members = append(members, IamMember{
89+
Member: member,
90+
Role: role,
91+
})
92+
}
7493
}
94+
BootstrapIamMembers(t, members)
95+
// Always return false because we now wait for IAM propagation.
7596
return false
7697
}
7798

mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.tmpl

+25-18
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,29 @@ const testComposerNetworkPrefix = "tf-test-composer-net"
2222
const testComposerBucketPrefix = "tf-test-composer-bucket"
2323
const testComposerNetworkAttachmentPrefix = "tf-test-composer-nta"
2424

25-
func allComposerServiceAgents() []string {
26-
return []string{
27-
"cloudcomposer-accounts",
28-
"compute-system",
29-
"container-engine-robot",
30-
"gcp-sa-artifactregistry",
31-
"gcp-sa-pubsub",
32-
}
25+
func bootstrapComposerServiceAgents(t *testing.T) {
26+
acctest.BootstrapIamMembers(t, []acctest.IamMember{
27+
{
28+
Member: "serviceAccount:service-{project_number}@cloudcomposer-accounts.iam.gserviceaccount.com",
29+
Role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
30+
},
31+
{
32+
Member: "serviceAccount:service-{project_number}@compute-system.iam.gserviceaccount.com",
33+
Role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
34+
},
35+
{
36+
Member: "serviceAccount:service-{project_number}@container-engine-robot.iam.gserviceaccount.com",
37+
Role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
38+
},
39+
{
40+
Member: "serviceAccount:service-{project_number}@gcp-sa-artifactregistry.iam.gserviceaccount.com",
41+
Role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
42+
},
43+
{
44+
Member: "serviceAccount:service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com",
45+
Role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
46+
},
47+
})
3348
}
3449

3550
// Checks environment creation with minimum required information.
@@ -245,7 +260,7 @@ func TestAccComposerEnvironment_withEncryptionConfigComposer1(t *testing.T) {
245260

246261
kms := acctest.BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", "us-central1", "tf-bootstrap-composer1-key1")
247262
pid := envvar.GetTestProjectFromEnv()
248-
grantServiceAgentsRole(t, "service-", allComposerServiceAgents(), "roles/cloudkms.cryptoKeyEncrypterDecrypter")
263+
bootstrapComposerServiceAgents(t)
249264
envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t))
250265
network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t))
251266
subnetwork := network + "-1"
@@ -282,7 +297,7 @@ func TestAccComposerEnvironment_withEncryptionConfigComposer2(t *testing.T) {
282297

283298
kms := acctest.BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", "us-central1", "tf-bootstrap-composer2-key1")
284299
pid := envvar.GetTestProjectFromEnv()
285-
grantServiceAgentsRole(t, "service-", allComposerServiceAgents(), "roles/cloudkms.cryptoKeyEncrypterDecrypter")
300+
bootstrapComposerServiceAgents(t)
286301
envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t))
287302
network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t))
288303
subnetwork := network + "-1"
@@ -970,14 +985,6 @@ func TestAccComposerEnvironment_fixPyPiPackages(t *testing.T) {
970985
})
971986
}
972987

973-
// This bootstraps the IAM roles needed for the service agents.
974-
func grantServiceAgentsRole(t *testing.T, prefix string, agentNames []string, role string) {
975-
if acctest.BootstrapAllPSARole(t, prefix, agentNames, role) {
976-
// Fail this test run because the policy needs time to reconcile.
977-
t.Fatal("Stopping test because permissions were added.")
978-
}
979-
}
980-
981988
func testAccComposerEnvironmentDestroyProducer(t *testing.T) func(s *terraform.State) error {
982989
return func(s *terraform.State) error {
983990
config := acctest.GoogleProviderConfig(t)

0 commit comments

Comments
 (0)