11package consul
22
33import (
4+ "bytes"
45 "fmt"
56 "log"
67 "strconv"
78
89 "github.com/armon/consul-api"
9- "github.com/hashicorp/terraform/flatmap"
10- "github.com/hashicorp/terraform/helper/config"
11- "github.com/hashicorp/terraform/helper/diff"
12- "github.com/hashicorp/terraform/terraform"
10+ "github.com/hashicorp/terraform/helper/hashcode"
11+ "github.com/hashicorp/terraform/helper/schema"
1312)
1413
15- func resource_consul_keys_validation () * config.Validator {
16- return & config.Validator {
17- Required : []string {
18- "key.*.name" ,
19- "key.*.path" ,
20- },
21- Optional : []string {
22- "datacenter" ,
23- "key.*.value" ,
24- "key.*.default" ,
25- "key.*.delete" ,
14+ func resourceConsulKeys () * schema.Resource {
15+ return & schema.Resource {
16+ Create : resourceConsulKeysCreate ,
17+ Update : resourceConsulKeysCreate ,
18+ Read : resourceConsulKeysRead ,
19+ Delete : resourceConsulKeysDelete ,
20+
21+ Schema : map [string ]* schema.Schema {
22+ "datacenter" : & schema.Schema {
23+ Type : schema .TypeString ,
24+ Optional : true ,
25+ Computed : true ,
26+ ForceNew : true ,
27+ },
28+
29+ "keys" : & schema.Schema {
30+ Type : schema .TypeSet ,
31+ Optional : true ,
32+ Elem : & schema.Resource {
33+ Schema : map [string ]* schema.Schema {
34+ "name" : & schema.Schema {
35+ Type : schema .TypeString ,
36+ Required : true ,
37+ },
38+
39+ "path" : & schema.Schema {
40+ Type : schema .TypeString ,
41+ Required : true ,
42+ },
43+
44+ "value" : & schema.Schema {
45+ Type : schema .TypeString ,
46+ Optional : true ,
47+ Computed : true ,
48+ },
49+
50+ "default" : & schema.Schema {
51+ Type : schema .TypeString ,
52+ Optional : true ,
53+ },
54+
55+ "delete" : & schema.Schema {
56+ Type : schema .TypeBool ,
57+ Optional : true ,
58+ },
59+ },
60+ },
61+ Set : resourceConsulKeysHash ,
62+ },
63+
64+ "var" : & schema.Schema {
65+ Type : schema .TypeMap ,
66+ Computed : true ,
67+ },
2668 },
2769 }
2870}
29- func resource_consul_keys_update (
30- s * terraform.InstanceState ,
31- d * terraform.InstanceDiff ,
32- meta interface {}) (* terraform.InstanceState , error ) {
33- return resource_consul_keys_create (s , d , meta )
34- }
3571
36- func resource_consul_keys_create (
37- s * terraform.InstanceState ,
38- d * terraform.InstanceDiff ,
39- meta interface {}) (* terraform.InstanceState , error ) {
40- p := meta .(* ResourceProvider )
72+ func resourceConsulKeysHash (v interface {}) int {
73+ var buf bytes.Buffer
74+ m := v .(map [string ]interface {})
75+ buf .WriteString (fmt .Sprintf ("%s-" , m ["name" ].(string )))
76+ buf .WriteString (fmt .Sprintf ("%s-" , m ["path" ].(string )))
77+ return hashcode .String (buf .String ())
78+ }
4179
42- // Merge the diff into the state so that we have all the attributes
43- // properly.
44- rs := s .MergeDiff (d )
45- rs .ID = "consul"
80+ func resourceConsulKeysCreate (d * schema.ResourceData , meta interface {}) error {
81+ client := meta .(* consulapi.Client )
82+ kv := client .KV ()
4683
47- // Check if the datacenter should be computed
48- dc := rs .Attributes ["datacenter" ]
49- if aDiff , ok := d .Attributes ["datacenter" ]; ok && aDiff .NewComputed {
84+ // Resolve the datacenter first, all the other keys are dependent
85+ // on this.
86+ var dc string
87+ if v := d .Get ("datacenter" ); v != nil {
88+ dc = v .(string )
89+ log .Printf ("[DEBUG] Consul datacenter: %s" , dc )
90+ } else {
91+ log .Printf ("[DEBUG] Resolving Consul datacenter..." )
5092 var err error
51- dc , err = get_dc (p . client )
93+ dc , err = get_dc (client )
5294 if err != nil {
53- return rs , fmt . Errorf ( "Failed to get agent datacenter: %v" , err )
95+ return err
5496 }
55- rs .Attributes ["datacenter" ] = dc
5697 }
5798
58- // Get the keys
59- keys , ok := flatmap .Expand (rs .Attributes , "key" ).([]interface {})
60- if ! ok {
61- return rs , fmt .Errorf ("Failed to unroll keys" )
62- }
63-
64- kv := p .client .KV ()
99+ // Setup the operations using the datacenter
65100 qOpts := consulapi.QueryOptions {Datacenter : dc }
66101 wOpts := consulapi.WriteOptions {Datacenter : dc }
67- for idx , raw := range keys {
102+
103+ // Store the computed vars
104+ vars := make (map [string ]string )
105+
106+ // Extract the keys
107+ keys := d .Get ("keys" ).(* schema.Set ).List ()
108+ for _ , raw := range keys {
68109 key , path , sub , err := parse_key (raw )
69110 if err != nil {
70- return rs , err
111+ return err
71112 }
72113
73114 if valueRaw , ok := sub ["value" ]; ok {
74115 value , ok := valueRaw .(string )
75116 if ! ok {
76- return rs , fmt .Errorf ("Failed to get value for key '%s'" , key )
117+ return fmt .Errorf ("Failed to get value for key '%s'" , key )
77118 }
78119
79120 log .Printf ("[DEBUG] Setting key '%s' to '%v' in %s" , path , value , dc )
80121 pair := consulapi.KVPair {Key : path , Value : []byte (value )}
81122 if _ , err := kv .Put (& pair , & wOpts ); err != nil {
82- return rs , fmt .Errorf ("Failed to set Consul key '%s': %v" , path , err )
123+ return fmt .Errorf ("Failed to set Consul key '%s': %v" , path , err )
83124 }
84- rs . Attributes [ fmt . Sprintf ( "var.%s" , key ) ] = value
85- rs . Attributes [ fmt . Sprintf ( "key.%d. value", idx ) ] = value
125+ vars [ key ] = value
126+ sub [ " value" ] = value
86127
87128 } else {
88129 log .Printf ("[DEBUG] Getting key '%s' in %s" , path , dc )
89130 pair , _ , err := kv .Get (path , & qOpts )
90131 if err != nil {
91- return rs , fmt .Errorf ("Failed to get Consul key '%s': %v" , path , err )
132+ return fmt .Errorf ("Failed to get Consul key '%s': %v" , path , err )
92133 }
93- rs .Attributes [fmt .Sprintf ("var.%s" , key )] = attribute_value (sub , key , pair )
134+ value := attribute_value (sub , key , pair )
135+ vars [key ] = value
136+ sub ["value" ] = value
94137 }
95138 }
96- return rs , nil
139+
140+ // Update the resource
141+ d .SetId ("consul" )
142+ d .Set ("datacenter" , dc )
143+ d .Set ("keys" , keys )
144+ d .Set ("var" , vars )
145+ return nil
97146}
98147
99- func resource_consul_keys_destroy (
100- s * terraform.InstanceState ,
101- meta interface {}) error {
102- p := meta .(* ResourceProvider )
103- client := p .client
148+ func resourceConsulKeysRead (d * schema.ResourceData , meta interface {}) error {
149+ client := meta .(* consulapi.Client )
104150 kv := client .KV ()
105151
106- // Get the keys
107- keys , ok := flatmap .Expand (s .Attributes , "key" ).([]interface {})
108- if ! ok {
109- return fmt .Errorf ("Failed to unroll keys" )
152+ // Get the DC, error if not available.
153+ var dc string
154+ if v := d .Get ("datacenter" ); v != nil {
155+ dc = v .(string )
156+ log .Printf ("[DEBUG] Consul datacenter: %s" , dc )
157+ } else {
158+ return fmt .Errorf ("Missing datacenter configuration" )
110159 }
111160
112- dc := s .Attributes ["datacenter" ]
113- wOpts := consulapi.WriteOptions {Datacenter : dc }
161+ // Setup the operations using the datacenter
162+ qOpts := consulapi.QueryOptions {Datacenter : dc }
163+
164+ // Store the computed vars
165+ vars := make (map [string ]string )
166+
167+ // Extract the keys
168+ keys := d .Get ("keys" ).(* schema.Set ).List ()
114169 for _ , raw := range keys {
115- _ , path , sub , err := parse_key (raw )
170+ key , path , sub , err := parse_key (raw )
116171 if err != nil {
117172 return err
118173 }
119174
120- // Ignore if the key is non-managed
121- shouldDelete , ok := sub ["delete" ].(bool )
122- if ! ok || ! shouldDelete {
123- continue
124- }
125-
126- log .Printf ("[DEBUG] Deleting key '%s' in %s" , path , dc )
127- if _ , err := kv .Delete (path , & wOpts ); err != nil {
128- return fmt .Errorf ("Failed to delete Consul key '%s': %v" , path , err )
129- }
130- }
131- return nil
132- }
133-
134- func resource_consul_keys_diff (
135- s * terraform.InstanceState ,
136- c * terraform.ResourceConfig ,
137- meta interface {}) (* terraform.InstanceDiff , error ) {
138-
139- // Determine the list of computed variables
140- var computed []string
141- keys , ok := flatmap .Expand (flatmap .Flatten (c .Config ), "key" ).([]interface {})
142- if ! ok {
143- goto AFTER
144- }
145- for _ , sub := range keys {
146- key , _ , _ , err := parse_key (sub )
175+ log .Printf ("[DEBUG] Refreshing value of key '%s' in %s" , path , dc )
176+ pair , _ , err := kv .Get (path , & qOpts )
147177 if err != nil {
148- continue
178+ return fmt . Errorf ( "Failed to get value for path '%s' from Consul: %v" , path , err )
149179 }
150- computed = append (computed , "var." + key )
151- }
152180
153- AFTER:
154- b := & diff.ResourceBuilder {
155- Attrs : map [string ]diff.AttrType {
156- "datacenter" : diff .AttrTypeCreate ,
157- "key" : diff .AttrTypeUpdate ,
158- },
159- ComputedAttrsUpdate : computed ,
181+ value := attribute_value (sub , key , pair )
182+ vars [key ] = value
183+ sub ["value" ] = value
160184 }
161- return b .Diff (s , c )
185+
186+ // Update the resource
187+ d .Set ("keys" , keys )
188+ d .Set ("var" , vars )
189+ return nil
162190}
163191
164- func resource_consul_keys_refresh (
165- s * terraform.InstanceState ,
166- meta interface {}) (* terraform.InstanceState , error ) {
167- p := meta .(* ResourceProvider )
168- client := p .client
192+ func resourceConsulKeysDelete (d * schema.ResourceData , meta interface {}) error {
193+ client := meta .(* consulapi.Client )
169194 kv := client .KV ()
170195
171- // Get the list of keys
172- keys , ok := flatmap .Expand (s .Attributes , "key" ).([]interface {})
173- if ! ok {
174- return s , fmt .Errorf ("Failed to unroll keys" )
196+ // Get the DC, error if not available.
197+ var dc string
198+ if v := d .Get ("datacenter" ); v != nil {
199+ dc = v .(string )
200+ log .Printf ("[DEBUG] Consul datacenter: %s" , dc )
201+ } else {
202+ return fmt .Errorf ("Missing datacenter configuration" )
175203 }
176204
177- // Update each key
178- dc := s .Attributes ["datacenter" ]
179- opts := consulapi.QueryOptions {Datacenter : dc }
180- for idx , raw := range keys {
181- key , path , sub , err := parse_key (raw )
205+ // Setup the operations using the datacenter
206+ wOpts := consulapi.WriteOptions {Datacenter : dc }
207+
208+ // Extract the keys
209+ keys := d .Get ("keys" ).(* schema.Set ).List ()
210+ for _ , raw := range keys {
211+ _ , path , sub , err := parse_key (raw )
182212 if err != nil {
183- return s , err
213+ return err
184214 }
185215
186- log . Printf ( "[DEBUG] Refreshing value of key '%s' in %s" , path , dc )
187- pair , _ , err := kv . Get ( path , & opts )
188- if err != nil {
189- return s , fmt . Errorf ( "Failed to get value for path '%s' from Consul: %v" , path , err )
216+ // Ignore if the key is non-managed
217+ shouldDelete , ok := sub [ "delete" ].( bool )
218+ if ! ok || ! shouldDelete {
219+ continue
190220 }
191221
192- setVal := attribute_value (sub , key , pair )
193- s .Attributes [fmt .Sprintf ("var.%s" , key )] = setVal
194- if _ , ok := sub ["value" ]; ok {
195- s .Attributes [fmt .Sprintf ("key.%d.value" , idx )] = setVal
222+ log .Printf ("[DEBUG] Deleting key '%s' in %s" , path , dc )
223+ if _ , err := kv .Delete (path , & wOpts ); err != nil {
224+ return fmt .Errorf ("Failed to delete Consul key '%s': %v" , path , err )
196225 }
197226 }
198- return s , nil
227+
228+ // Clear the ID
229+ d .SetId ("" )
230+ return nil
199231}
200232
201233// parse_key is used to parse a key into a name, path, config or error
@@ -217,7 +249,8 @@ func parse_key(raw interface{}) (string, string, map[string]interface{}, error)
217249 return key , path , sub , nil
218250}
219251
220- // attribute_value determienes the value for a key
252+ // attribute_value determines the value for a key, potentially
253+ // using a default value if provided.
221254func attribute_value (sub map [string ]interface {}, key string , pair * consulapi.KVPair ) string {
222255 // Use the value if given
223256 if pair != nil {
0 commit comments