github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/builtin/providers/consul/resource_consul_keys.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "log" 6 "strconv" 7 8 "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" 13 ) 14 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", 26 }, 27 } 28 } 29 func resource_consul_keys_update( 30 s *terraform.ResourceState, 31 d *terraform.ResourceDiff, 32 meta interface{}) (*terraform.ResourceState, error) { 33 return resource_consul_keys_create(s, d, meta) 34 } 35 36 func resource_consul_keys_create( 37 s *terraform.ResourceState, 38 d *terraform.ResourceDiff, 39 meta interface{}) (*terraform.ResourceState, error) { 40 p := meta.(*ResourceProvider) 41 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" 46 47 // Check if the datacenter should be computed 48 dc := rs.Attributes["datacenter"] 49 if aDiff, ok := d.Attributes["datacenter"]; ok && aDiff.NewComputed { 50 var err error 51 dc, err = get_dc(p.client) 52 if err != nil { 53 return rs, fmt.Errorf("Failed to get agent datacenter: %v", err) 54 } 55 rs.Attributes["datacenter"] = dc 56 } 57 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() 65 qOpts := consulapi.QueryOptions{Datacenter: dc} 66 wOpts := consulapi.WriteOptions{Datacenter: dc} 67 for idx, raw := range keys { 68 key, path, sub, err := parse_key(raw) 69 if err != nil { 70 return rs, err 71 } 72 73 if valueRaw, ok := sub["value"]; ok { 74 value, ok := valueRaw.(string) 75 if !ok { 76 return rs, fmt.Errorf("Failed to get value for key '%s'", key) 77 } 78 79 log.Printf("[DEBUG] Setting key '%s' to '%v' in %s", path, value, dc) 80 pair := consulapi.KVPair{Key: path, Value: []byte(value)} 81 if _, err := kv.Put(&pair, &wOpts); err != nil { 82 return rs, fmt.Errorf("Failed to set Consul key '%s': %v", path, err) 83 } 84 rs.Attributes[fmt.Sprintf("var.%s", key)] = value 85 rs.Attributes[fmt.Sprintf("key.%d.value", idx)] = value 86 87 } else { 88 log.Printf("[DEBUG] Getting key '%s' in %s", path, dc) 89 pair, _, err := kv.Get(path, &qOpts) 90 if err != nil { 91 return rs, fmt.Errorf("Failed to get Consul key '%s': %v", path, err) 92 } 93 rs.Attributes[fmt.Sprintf("var.%s", key)] = attribute_value(sub, key, pair) 94 } 95 } 96 return rs, nil 97 } 98 99 func resource_consul_keys_destroy( 100 s *terraform.ResourceState, 101 meta interface{}) error { 102 p := meta.(*ResourceProvider) 103 client := p.client 104 kv := client.KV() 105 106 // Get the keys 107 keys, ok := flatmap.Expand(s.Attributes, "key").([]interface{}) 108 if !ok { 109 return fmt.Errorf("Failed to unroll keys") 110 } 111 112 dc := s.Attributes["datacenter"] 113 wOpts := consulapi.WriteOptions{Datacenter: dc} 114 for _, raw := range keys { 115 _, path, sub, err := parse_key(raw) 116 if err != nil { 117 return err 118 } 119 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.ResourceState, 136 c *terraform.ResourceConfig, 137 meta interface{}) (*terraform.ResourceDiff, 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) 147 if err != nil { 148 continue 149 } 150 computed = append(computed, "var."+key) 151 } 152 153 AFTER: 154 b := &diff.ResourceBuilder{ 155 Attrs: map[string]diff.AttrType{ 156 "datacenter": diff.AttrTypeCreate, 157 "key": diff.AttrTypeUpdate, 158 }, 159 ComputedAttrsUpdate: computed, 160 } 161 return b.Diff(s, c) 162 } 163 164 func resource_consul_keys_refresh( 165 s *terraform.ResourceState, 166 meta interface{}) (*terraform.ResourceState, error) { 167 p := meta.(*ResourceProvider) 168 client := p.client 169 kv := client.KV() 170 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") 175 } 176 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) 182 if err != nil { 183 return s, err 184 } 185 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) 190 } 191 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 196 } 197 } 198 return s, nil 199 } 200 201 // parse_key is used to parse a key into a name, path, config or error 202 func parse_key(raw interface{}) (string, string, map[string]interface{}, error) { 203 sub, ok := raw.(map[string]interface{}) 204 if !ok { 205 return "", "", nil, fmt.Errorf("Failed to unroll: %#v", raw) 206 } 207 208 key, ok := sub["name"].(string) 209 if !ok { 210 return "", "", nil, fmt.Errorf("Failed to expand key '%#v'", sub) 211 } 212 213 path, ok := sub["path"].(string) 214 if !ok { 215 return "", "", nil, fmt.Errorf("Failed to get path for key '%s'", key) 216 } 217 return key, path, sub, nil 218 } 219 220 // attribute_value determienes the value for a key 221 func attribute_value(sub map[string]interface{}, key string, pair *consulapi.KVPair) string { 222 // Use the value if given 223 if pair != nil { 224 return string(pair.Value) 225 } 226 227 // Use a default if given 228 if raw, ok := sub["default"]; ok { 229 switch def := raw.(type) { 230 case string: 231 return def 232 case bool: 233 return strconv.FormatBool(def) 234 } 235 } 236 237 // No value 238 return "" 239 } 240 241 // get_dc is used to get the datacenter of the local agent 242 func get_dc(client *consulapi.Client) (string, error) { 243 info, err := client.Agent().Self() 244 if err != nil { 245 return "", fmt.Errorf("Failed to get datacenter from Consul agent: %v", err) 246 } 247 dc := info["Config"]["Datacenter"].(string) 248 return dc, nil 249 }