forked from hashicorp/terraform-provider-google
-
Notifications
You must be signed in to change notification settings - Fork 0
/
metadata.go
151 lines (130 loc) · 4.05 KB
/
metadata.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package google
import (
"fmt"
"log"
"strings"
computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"
)
const FINGERPRINT_RETRIES = 10
var FINGERPRINT_FAIL_ERRORS = []string{"Invalid fingerprint.", "Supplied fingerprint does not match current metadata fingerprint."}
// Since the google compute API uses optimistic locking, there is a chance
// we need to resubmit our updated metadata. To do this, you need to provide
// an update function that attempts to submit your metadata
func MetadataRetryWrapper(update func() error) error {
attempt := 0
for attempt < FINGERPRINT_RETRIES {
err := update()
if err == nil {
return nil
}
// Check to see if the error matches any of our fingerprint-related failure messages
var fingerprintError bool
for _, msg := range FINGERPRINT_FAIL_ERRORS {
if strings.Contains(err.Error(), msg) {
fingerprintError = true
break
}
}
if !fingerprintError {
// Something else went wrong, don't retry
return err
}
attempt++
}
return fmt.Errorf("Failed to update metadata after %d retries", attempt)
}
// Update the metadata (serverMD) according to the provided diff (oldMDMap v
// newMDMap).
func MetadataUpdate(oldMDMap map[string]interface{}, newMDMap map[string]interface{}, serverMD *compute.Metadata) {
curMDMap := make(map[string]string)
// Load metadata on server into map
for _, kv := range serverMD.Items {
// If the server state has a key that we had in our old
// state, but not in our new state, we should delete it
_, okOld := oldMDMap[kv.Key]
_, okNew := newMDMap[kv.Key]
if okOld && !okNew {
continue
} else {
curMDMap[kv.Key] = *kv.Value
}
}
// Insert new metadata into existing metadata (overwriting when needed)
for key, val := range newMDMap {
curMDMap[key] = val.(string)
}
// Reformat old metadata into a list
serverMD.Items = nil
for key, val := range curMDMap {
v := val
serverMD.Items = append(serverMD.Items, &compute.MetadataItems{
Key: key,
Value: &v,
})
}
}
// Update the beta metadata (serverMD) according to the provided diff (oldMDMap v
// newMDMap).
func BetaMetadataUpdate(oldMDMap map[string]interface{}, newMDMap map[string]interface{}, serverMD *computeBeta.Metadata) {
curMDMap := make(map[string]string)
// Load metadata on server into map
for _, kv := range serverMD.Items {
// If the server state has a key that we had in our old
// state, but not in our new state, we should delete it
_, okOld := oldMDMap[kv.Key]
_, okNew := newMDMap[kv.Key]
if okOld && !okNew {
continue
} else {
curMDMap[kv.Key] = *kv.Value
}
}
// Insert new metadata into existing metadata (overwriting when needed)
for key, val := range newMDMap {
curMDMap[key] = val.(string)
}
// Reformat old metadata into a list
serverMD.Items = nil
for key, val := range curMDMap {
v := val
serverMD.Items = append(serverMD.Items, &computeBeta.MetadataItems{
Key: key,
Value: &v,
})
}
}
// flattenComputeMetadata transforms a list of MetadataItems (as returned via the GCP client) into a simple map from key
// to value.
func flattenComputeMetadata(metadata []*compute.MetadataItems) map[string]string {
m := map[string]string{}
for _, item := range metadata {
// check for duplicates
if item.Value == nil {
continue
}
if val, ok := m[item.Key]; ok {
// warn loudly!
log.Printf("[WARN] Key '%s' already has value '%s' when flattening - ignoring incoming value '%s'",
item.Key,
val,
*item.Value)
}
m[item.Key] = *item.Value
}
return m
}
// expandComputeMetadata transforms a map representing computing metadata into a list of compute.MetadataItems suitable
// for the GCP client.
func expandComputeMetadata(m map[string]string) []*compute.MetadataItems {
metadata := make([]*compute.MetadataItems, len(m))
idx := 0
for key, value := range m {
// Make a copy of value as we need a ptr type; if we directly use 'value' then all items will reference the same
// memory address
vtmp := value
metadata[idx] = &compute.MetadataItems{Key: key, Value: &vtmp}
idx++
}
return metadata
}