Skip to content

Commit 41a9d6b

Browse files
rileykarsonmodular-magician
authored andcommitted
Retry instance metadata on fingerprint mismatch.
Signed-off-by: Modular Magician <[email protected]>
1 parent fe359ee commit 41a9d6b

File tree

3 files changed

+49
-23
lines changed

3 files changed

+49
-23
lines changed

google/metadata.go

+3-16
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,24 @@ package google
33
import (
44
"errors"
55
"fmt"
6-
"strings"
7-
86
computeBeta "google.golang.org/api/compute/v0.beta"
97
"google.golang.org/api/compute/v1"
108
)
119

12-
const FINGERPRINT_RETRIES = 10
13-
14-
var FINGERPRINT_FAIL_ERRORS = []string{"Invalid fingerprint.", "Supplied fingerprint does not match current metadata fingerprint."}
10+
const METADATA_FINGERPRINT_RETRIES = 10
1511

1612
// Since the google compute API uses optimistic locking, there is a chance
1713
// we need to resubmit our updated metadata. To do this, you need to provide
1814
// an update function that attempts to submit your metadata
1915
func MetadataRetryWrapper(update func() error) error {
2016
attempt := 0
21-
for attempt < FINGERPRINT_RETRIES {
17+
for attempt < METADATA_FINGERPRINT_RETRIES {
2218
err := update()
2319
if err == nil {
2420
return nil
2521
}
2622

27-
// Check to see if the error matches any of our fingerprint-related failure messages
28-
var fingerprintError bool
29-
for _, msg := range FINGERPRINT_FAIL_ERRORS {
30-
if strings.Contains(err.Error(), msg) {
31-
fingerprintError = true
32-
break
33-
}
34-
}
35-
36-
if !fingerprintError {
23+
if !isFingerprintError(err) {
3724
// Something else went wrong, don't retry
3825
return err
3926
}

google/resource_compute_instance.go

+27-7
Original file line numberDiff line numberDiff line change
@@ -938,14 +938,34 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
938938
return err
939939
}
940940

941-
op, err := config.clientCompute.Instances.SetMetadata(project, zone, d.Id(), metadataV1).Do()
942-
if err != nil {
943-
return fmt.Errorf("Error updating metadata: %s", err)
944-
}
941+
// We're retrying for an error 412 where the metadata fingerprint is out of date
942+
err = retry(
943+
func() error {
944+
// retrieve up-to-date metadata from the API in case several updates hit simultaneously. instances
945+
// sometimes but not always share metadata fingerprints.
946+
instance, err := config.clientComputeBeta.Instances.Get(project, zone, d.Id()).Do()
947+
if err != nil {
948+
return handleNotFoundError(err, d, fmt.Sprintf("Instance %s", d.Get("name").(string)))
949+
}
945950

946-
opErr := computeOperationWaitTime(config.clientCompute, op, project, "metadata to update", int(d.Timeout(schema.TimeoutUpdate).Minutes()))
947-
if opErr != nil {
948-
return opErr
951+
metadataV1.Fingerprint = instance.Metadata.Fingerprint
952+
953+
op, err := config.clientCompute.Instances.SetMetadata(project, zone, d.Id(), metadataV1).Do()
954+
if err != nil {
955+
return fmt.Errorf("Error updating metadata: %s", err)
956+
}
957+
958+
opErr := computeOperationWaitTime(config.clientCompute, op, project, "metadata to update", int(d.Timeout(schema.TimeoutUpdate).Minutes()))
959+
if opErr != nil {
960+
return opErr
961+
}
962+
963+
return nil
964+
},
965+
)
966+
967+
if err != nil {
968+
return err
949969
}
950970

951971
d.SetPartial("metadata")

google/utils.go

+19
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,20 @@ func isFailedPreconditionError(err error) bool {
125125
return false
126126
}
127127

128+
var FINGERPRINT_FAIL_ERRORS = []string{"Invalid fingerprint.", "Supplied fingerprint does not match current metadata fingerprint."}
129+
130+
// We've encountered a few common fingerprint-related strings; if this is one of
131+
// them, we're confident this is an error due to fingerprints.
132+
func isFingerprintError(err error) bool {
133+
for _, msg := range FINGERPRINT_FAIL_ERRORS {
134+
if strings.Contains(err.Error(), msg) {
135+
return true
136+
}
137+
}
138+
139+
return false
140+
}
141+
128142
func isConflictError(err error) bool {
129143
if e, ok := err.(*googleapi.Error); ok && e.Code == 409 {
130144
return true
@@ -369,6 +383,11 @@ func isRetryableError(err error) bool {
369383
return true
370384
}
371385

386+
if gerr, ok := err.(*googleapi.Error); ok && (gerr.Code == 412) && isFingerprintError(err) {
387+
log.Printf("[DEBUG] Dismissed an error as retryable as a fingerprint mismatch: %s", err)
388+
return true
389+
}
390+
372391
return false
373392
}
374393

0 commit comments

Comments
 (0)