forked from hashicorp/terraform
-
Notifications
You must be signed in to change notification settings - Fork 0
/
resource_consul_keys.go
249 lines (218 loc) · 6.17 KB
/
resource_consul_keys.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package consul
import (
"fmt"
"log"
"strconv"
"github.com/armon/consul-api"
"github.com/hashicorp/terraform/flatmap"
"github.com/hashicorp/terraform/helper/config"
"github.com/hashicorp/terraform/helper/diff"
"github.com/hashicorp/terraform/terraform"
)
func resource_consul_keys_validation() *config.Validator {
return &config.Validator{
Required: []string{
"key.*.name",
"key.*.path",
},
Optional: []string{
"datacenter",
"key.*.value",
"key.*.default",
"key.*.delete",
},
}
}
func resource_consul_keys_update(
s *terraform.ResourceState,
d *terraform.ResourceDiff,
meta interface{}) (*terraform.ResourceState, error) {
return resource_consul_keys_create(s, d, meta)
}
func resource_consul_keys_create(
s *terraform.ResourceState,
d *terraform.ResourceDiff,
meta interface{}) (*terraform.ResourceState, error) {
p := meta.(*ResourceProvider)
// Merge the diff into the state so that we have all the attributes
// properly.
rs := s.MergeDiff(d)
rs.ID = "consul"
// Check if the datacenter should be computed
dc := rs.Attributes["datacenter"]
if aDiff, ok := d.Attributes["datacenter"]; ok && aDiff.NewComputed {
var err error
dc, err = get_dc(p.client)
if err != nil {
return rs, fmt.Errorf("Failed to get agent datacenter: %v", err)
}
rs.Attributes["datacenter"] = dc
}
// Get the keys
keys, ok := flatmap.Expand(rs.Attributes, "key").([]interface{})
if !ok {
return rs, fmt.Errorf("Failed to unroll keys")
}
kv := p.client.KV()
qOpts := consulapi.QueryOptions{Datacenter: dc}
wOpts := consulapi.WriteOptions{Datacenter: dc}
for idx, raw := range keys {
key, path, sub, err := parse_key(raw)
if err != nil {
return rs, err
}
if valueRaw, ok := sub["value"]; ok {
value, ok := valueRaw.(string)
if !ok {
return rs, fmt.Errorf("Failed to get value for key '%s'", key)
}
log.Printf("[DEBUG] Setting key '%s' to '%v' in %s", path, value, dc)
pair := consulapi.KVPair{Key: path, Value: []byte(value)}
if _, err := kv.Put(&pair, &wOpts); err != nil {
return rs, fmt.Errorf("Failed to set Consul key '%s': %v", path, err)
}
rs.Attributes[fmt.Sprintf("var.%s", key)] = value
rs.Attributes[fmt.Sprintf("key.%d.value", idx)] = value
} else {
log.Printf("[DEBUG] Getting key '%s' in %s", path, dc)
pair, _, err := kv.Get(path, &qOpts)
if err != nil {
return rs, fmt.Errorf("Failed to get Consul key '%s': %v", path, err)
}
rs.Attributes[fmt.Sprintf("var.%s", key)] = attribute_value(sub, key, pair)
}
}
return rs, nil
}
func resource_consul_keys_destroy(
s *terraform.ResourceState,
meta interface{}) error {
p := meta.(*ResourceProvider)
client := p.client
kv := client.KV()
// Get the keys
keys, ok := flatmap.Expand(s.Attributes, "key").([]interface{})
if !ok {
return fmt.Errorf("Failed to unroll keys")
}
dc := s.Attributes["datacenter"]
wOpts := consulapi.WriteOptions{Datacenter: dc}
for _, raw := range keys {
_, path, sub, err := parse_key(raw)
if err != nil {
return err
}
// Ignore if the key is non-managed
shouldDelete, ok := sub["delete"].(bool)
if !ok || !shouldDelete {
continue
}
log.Printf("[DEBUG] Deleting key '%s' in %s", path, dc)
if _, err := kv.Delete(path, &wOpts); err != nil {
return fmt.Errorf("Failed to delete Consul key '%s': %v", path, err)
}
}
return nil
}
func resource_consul_keys_diff(
s *terraform.ResourceState,
c *terraform.ResourceConfig,
meta interface{}) (*terraform.ResourceDiff, error) {
// Determine the list of computed variables
var computed []string
keys, ok := flatmap.Expand(flatmap.Flatten(c.Config), "key").([]interface{})
if !ok {
goto AFTER
}
for _, sub := range keys {
key, _, _, err := parse_key(sub)
if err != nil {
continue
}
computed = append(computed, "var."+key)
}
AFTER:
b := &diff.ResourceBuilder{
Attrs: map[string]diff.AttrType{
"datacenter": diff.AttrTypeCreate,
"key": diff.AttrTypeUpdate,
},
ComputedAttrsUpdate: computed,
}
return b.Diff(s, c)
}
func resource_consul_keys_refresh(
s *terraform.ResourceState,
meta interface{}) (*terraform.ResourceState, error) {
p := meta.(*ResourceProvider)
client := p.client
kv := client.KV()
// Get the list of keys
keys, ok := flatmap.Expand(s.Attributes, "key").([]interface{})
if !ok {
return s, fmt.Errorf("Failed to unroll keys")
}
// Update each key
dc := s.Attributes["datacenter"]
opts := consulapi.QueryOptions{Datacenter: dc}
for idx, raw := range keys {
key, path, sub, err := parse_key(raw)
if err != nil {
return s, err
}
log.Printf("[DEBUG] Refreshing value of key '%s' in %s", path, dc)
pair, _, err := kv.Get(path, &opts)
if err != nil {
return s, fmt.Errorf("Failed to get value for path '%s' from Consul: %v", path, err)
}
setVal := attribute_value(sub, key, pair)
s.Attributes[fmt.Sprintf("var.%s", key)] = setVal
if _, ok := sub["value"]; ok {
s.Attributes[fmt.Sprintf("key.%d.value", idx)] = setVal
}
}
return s, nil
}
// parse_key is used to parse a key into a name, path, config or error
func parse_key(raw interface{}) (string, string, map[string]interface{}, error) {
sub, ok := raw.(map[string]interface{})
if !ok {
return "", "", nil, fmt.Errorf("Failed to unroll: %#v", raw)
}
key, ok := sub["name"].(string)
if !ok {
return "", "", nil, fmt.Errorf("Failed to expand key '%#v'", sub)
}
path, ok := sub["path"].(string)
if !ok {
return "", "", nil, fmt.Errorf("Failed to get path for key '%s'", key)
}
return key, path, sub, nil
}
// attribute_value determienes the value for a key
func attribute_value(sub map[string]interface{}, key string, pair *consulapi.KVPair) string {
// Use the value if given
if pair != nil {
return string(pair.Value)
}
// Use a default if given
if raw, ok := sub["default"]; ok {
switch def := raw.(type) {
case string:
return def
case bool:
return strconv.FormatBool(def)
}
}
// No value
return ""
}
// get_dc is used to get the datacenter of the local agent
func get_dc(client *consulapi.Client) (string, error) {
info, err := client.Agent().Self()
if err != nil {
return "", fmt.Errorf("Failed to get datacenter from Consul agent: %v", err)
}
dc := info["Config"]["Datacenter"].(string)
return dc, nil
}