github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/builtin/providers/consul/resource_consul_keys.go (about) 1 package consul 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "strconv" 8 9 "github.com/armon/consul-api" 10 "github.com/hashicorp/terraform/helper/hashcode" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 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 "token": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 }, 33 34 "keys": &schema.Schema{ 35 Type: schema.TypeSet, 36 Optional: true, 37 Elem: &schema.Resource{ 38 Schema: map[string]*schema.Schema{ 39 "name": &schema.Schema{ 40 Type: schema.TypeString, 41 Required: true, 42 }, 43 44 "path": &schema.Schema{ 45 Type: schema.TypeString, 46 Required: true, 47 }, 48 49 "value": &schema.Schema{ 50 Type: schema.TypeString, 51 Optional: true, 52 Computed: true, 53 }, 54 55 "default": &schema.Schema{ 56 Type: schema.TypeString, 57 Optional: true, 58 }, 59 60 "delete": &schema.Schema{ 61 Type: schema.TypeBool, 62 Optional: true, 63 }, 64 }, 65 }, 66 Set: resourceConsulKeysHash, 67 }, 68 69 "var": &schema.Schema{ 70 Type: schema.TypeMap, 71 Computed: true, 72 }, 73 }, 74 } 75 } 76 77 func resourceConsulKeysHash(v interface{}) int { 78 var buf bytes.Buffer 79 m := v.(map[string]interface{}) 80 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 81 buf.WriteString(fmt.Sprintf("%s-", m["path"].(string))) 82 return hashcode.String(buf.String()) 83 } 84 85 func resourceConsulKeysCreate(d *schema.ResourceData, meta interface{}) error { 86 client := meta.(*consulapi.Client) 87 kv := client.KV() 88 89 // Resolve the datacenter first, all the other keys are dependent 90 // on this. 91 var dc string 92 if v, ok := d.GetOk("datacenter"); ok { 93 dc = v.(string) 94 log.Printf("[DEBUG] Consul datacenter: %s", dc) 95 } else { 96 log.Printf("[DEBUG] Resolving Consul datacenter...") 97 var err error 98 dc, err = get_dc(client) 99 if err != nil { 100 return err 101 } 102 } 103 var token string 104 if v, ok := d.GetOk("token"); ok { 105 token = v.(string) 106 } 107 108 // Setup the operations using the datacenter 109 qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token} 110 wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token} 111 112 // Store the computed vars 113 vars := make(map[string]string) 114 115 // Extract the keys 116 keys := d.Get("keys").(*schema.Set).List() 117 for _, raw := range keys { 118 key, path, sub, err := parse_key(raw) 119 if err != nil { 120 return err 121 } 122 123 if valueRaw, ok := sub["value"]; ok { 124 value, ok := valueRaw.(string) 125 if !ok { 126 return fmt.Errorf("Failed to get value for key '%s'", key) 127 } 128 129 log.Printf("[DEBUG] Setting key '%s' to '%v' in %s", path, value, dc) 130 pair := consulapi.KVPair{Key: path, Value: []byte(value)} 131 if _, err := kv.Put(&pair, &wOpts); err != nil { 132 return fmt.Errorf("Failed to set Consul key '%s': %v", path, err) 133 } 134 vars[key] = value 135 sub["value"] = value 136 137 } else { 138 log.Printf("[DEBUG] Getting key '%s' in %s", path, dc) 139 pair, _, err := kv.Get(path, &qOpts) 140 if err != nil { 141 return fmt.Errorf("Failed to get Consul key '%s': %v", path, err) 142 } 143 value := attribute_value(sub, key, pair) 144 vars[key] = value 145 sub["value"] = value 146 } 147 } 148 149 // Update the resource 150 d.SetId("consul") 151 d.Set("datacenter", dc) 152 d.Set("keys", keys) 153 d.Set("var", vars) 154 return nil 155 } 156 157 func resourceConsulKeysRead(d *schema.ResourceData, meta interface{}) error { 158 client := meta.(*consulapi.Client) 159 kv := client.KV() 160 161 // Get the DC, error if not available. 162 var dc string 163 if v, ok := d.GetOk("datacenter"); ok { 164 dc = v.(string) 165 log.Printf("[DEBUG] Consul datacenter: %s", dc) 166 } else { 167 return fmt.Errorf("Missing datacenter configuration") 168 } 169 var token string 170 if v, ok := d.GetOk("token"); ok { 171 token = v.(string) 172 } 173 174 // Setup the operations using the datacenter 175 qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token} 176 177 // Store the computed vars 178 vars := make(map[string]string) 179 180 // Extract the keys 181 keys := d.Get("keys").(*schema.Set).List() 182 for _, raw := range keys { 183 key, path, sub, err := parse_key(raw) 184 if err != nil { 185 return err 186 } 187 188 log.Printf("[DEBUG] Refreshing value of key '%s' in %s", path, dc) 189 pair, _, err := kv.Get(path, &qOpts) 190 if err != nil { 191 return fmt.Errorf("Failed to get value for path '%s' from Consul: %v", path, err) 192 } 193 194 value := attribute_value(sub, key, pair) 195 vars[key] = value 196 sub["value"] = value 197 } 198 199 // Update the resource 200 d.Set("keys", keys) 201 d.Set("var", vars) 202 return nil 203 } 204 205 func resourceConsulKeysDelete(d *schema.ResourceData, meta interface{}) error { 206 client := meta.(*consulapi.Client) 207 kv := client.KV() 208 209 // Get the DC, error if not available. 210 var dc string 211 if v, ok := d.GetOk("datacenter"); ok { 212 dc = v.(string) 213 log.Printf("[DEBUG] Consul datacenter: %s", dc) 214 } else { 215 return fmt.Errorf("Missing datacenter configuration") 216 } 217 var token string 218 if v, ok := d.GetOk("token"); ok { 219 token = v.(string) 220 } 221 222 // Setup the operations using the datacenter 223 wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token} 224 225 // Extract the keys 226 keys := d.Get("keys").(*schema.Set).List() 227 for _, raw := range keys { 228 _, path, sub, err := parse_key(raw) 229 if err != nil { 230 return err 231 } 232 233 // Ignore if the key is non-managed 234 shouldDelete, ok := sub["delete"].(bool) 235 if !ok || !shouldDelete { 236 continue 237 } 238 239 log.Printf("[DEBUG] Deleting key '%s' in %s", path, dc) 240 if _, err := kv.Delete(path, &wOpts); err != nil { 241 return fmt.Errorf("Failed to delete Consul key '%s': %v", path, err) 242 } 243 } 244 245 // Clear the ID 246 d.SetId("") 247 return nil 248 } 249 250 // parse_key is used to parse a key into a name, path, config or error 251 func parse_key(raw interface{}) (string, string, map[string]interface{}, error) { 252 sub, ok := raw.(map[string]interface{}) 253 if !ok { 254 return "", "", nil, fmt.Errorf("Failed to unroll: %#v", raw) 255 } 256 257 key, ok := sub["name"].(string) 258 if !ok { 259 return "", "", nil, fmt.Errorf("Failed to expand key '%#v'", sub) 260 } 261 262 path, ok := sub["path"].(string) 263 if !ok { 264 return "", "", nil, fmt.Errorf("Failed to get path for key '%s'", key) 265 } 266 return key, path, sub, nil 267 } 268 269 // attribute_value determines the value for a key, potentially 270 // using a default value if provided. 271 func attribute_value(sub map[string]interface{}, key string, pair *consulapi.KVPair) string { 272 // Use the value if given 273 if pair != nil { 274 return string(pair.Value) 275 } 276 277 // Use a default if given 278 if raw, ok := sub["default"]; ok { 279 switch def := raw.(type) { 280 case string: 281 return def 282 case bool: 283 return strconv.FormatBool(def) 284 } 285 } 286 287 // No value 288 return "" 289 } 290 291 // get_dc is used to get the datacenter of the local agent 292 func get_dc(client *consulapi.Client) (string, error) { 293 info, err := client.Agent().Self() 294 if err != nil { 295 return "", fmt.Errorf("Failed to get datacenter from Consul agent: %v", err) 296 } 297 dc := info["Config"]["Datacenter"].(string) 298 return dc, nil 299 }