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