Skip to content

Commit bd2e47f

Browse files
chasevedderBBBmau
authored andcommitted
Update service account creation to be more resilient to eventual cons… (GoogleCloudPlatform#14547)
1 parent 1eb17ac commit bd2e47f

File tree

1 file changed

+38
-27
lines changed

1 file changed

+38
-27
lines changed

mmv1/third_party/terraform/services/resourcemanager/resource_google_service_account.go

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -122,54 +122,61 @@ func resourceGoogleServiceAccountCreate(d *schema.ResourceData, meta interface{}
122122
ServiceAccount: sa,
123123
}
124124

125-
sa, err = config.NewIamClient(userAgent).Projects.ServiceAccounts.Create("projects/"+project, r).Do()
125+
iamClient := config.NewIamClient(userAgent)
126+
sa, err = iamClient.Projects.ServiceAccounts.Create("projects/"+project, r).Do()
126127
if err != nil {
127128
gerr, ok := err.(*googleapi.Error)
128129
alreadyExists := ok && gerr.Code == 409 && d.Get("create_ignore_already_exists").(bool)
129130
if alreadyExists {
130-
sa = &iam.ServiceAccount{
131-
Name: fmt.Sprintf("projects/%s/serviceAccounts/%s@%s.iam.gserviceaccount.com", project, aid, project),
132-
}
131+
fullServiceAccountName := fmt.Sprintf("projects/%s/serviceAccounts/%s@%s.iam.gserviceaccount.com", project, aid, project)
132+
err = transport_tpg.Retry(transport_tpg.RetryOptions{
133+
RetryFunc: func() (operr error) {
134+
sa, saerr := iamClient.Projects.ServiceAccounts.Get(fullServiceAccountName).Do()
135+
136+
if saerr != nil {
137+
return saerr
138+
}
139+
140+
d.SetId(sa.Name)
141+
return populateResourceData(d, sa)
142+
},
143+
Timeout: d.Timeout(schema.TimeoutCreate),
144+
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{
145+
transport_tpg.IsNotFoundRetryableError("service account creation"),
146+
},
147+
})
148+
149+
return nil
133150
} else {
134151
return fmt.Errorf("Error creating service account: %s", err)
135152
}
136153
}
137154

138155
d.SetId(sa.Name)
139-
140-
err = transport_tpg.Retry(transport_tpg.RetryOptions{
141-
RetryFunc: func() (operr error) {
142-
_, saerr := config.NewIamClient(userAgent).Projects.ServiceAccounts.Get(d.Id()).Do()
143-
return saerr
144-
},
145-
Timeout: d.Timeout(schema.TimeoutCreate),
146-
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{
147-
transport_tpg.IsNotFoundRetryableError("service account creation"),
148-
transport_tpg.IsForbiddenIamServiceAccountRetryableError("service account creation"),
149-
},
150-
})
151-
152-
if err != nil {
153-
return fmt.Errorf("Error reading service account after creation: %s", err)
154-
}
156+
populateResourceData(d, sa)
155157

156158
// We poll until the resource is found due to eventual consistency issue
157-
// on part of the api https://cloud.google.com/iam/docs/overview#consistency
159+
// on part of the api https://cloud.google.com/iam/docs/overview#consistency.
160+
// Wait for at least 3 successful responses in a row to ensure result is consistent.
158161
// IAM API returns 403 when the queried SA is not found, so we must ignore both 404 & 403 errors
159-
err = transport_tpg.PollingWaitTime(resourceServiceAccountPollRead(d, meta), transport_tpg.PollCheckForExistenceWith403, "Creating Service Account", d.Timeout(schema.TimeoutCreate), 1)
160-
161-
if err != nil {
162-
return err
163-
}
162+
transport_tpg.PollingWaitTime(
163+
resourceServiceAccountPollRead(d, meta),
164+
transport_tpg.PollCheckForExistence,
165+
"Creating Service Account",
166+
d.Timeout(schema.TimeoutCreate),
167+
3, // Number of consecutive occurences.
168+
)
164169

165170
// We can't guarantee complete consistency even after polling,
166171
// so sleep for some additional time to reduce the likelihood of
167172
// eventual consistency failures.
168173
time.Sleep(10 * time.Second)
169174

170-
return resourceGoogleServiceAccountRead(d, meta)
175+
return nil
171176
}
172177

178+
// PollReadFunc for checking Service Account existence.
179+
// If resourceData is not nil, it will be updated with the response.
173180
func resourceServiceAccountPollRead(d *schema.ResourceData, meta interface{}) transport_tpg.PollReadFunc {
174181
return func() (map[string]interface{}, error) {
175182
config := meta.(*transport_tpg.Config)
@@ -201,6 +208,10 @@ func resourceGoogleServiceAccountRead(d *schema.ResourceData, meta interface{})
201208
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("Service Account %q", d.Id()))
202209
}
203210

211+
return populateResourceData(d, sa)
212+
}
213+
214+
func populateResourceData(d *schema.ResourceData, sa *iam.ServiceAccount) error {
204215
if err := d.Set("email", sa.Email); err != nil {
205216
return fmt.Errorf("Error setting email: %s", err)
206217
}

0 commit comments

Comments
 (0)