github.com/erriapo/terraform@v0.6.12-0.20160203182612-0340ea72354f/builtin/providers/consul/resource_consul_keys.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "log" 6 "strconv" 7 8 consulapi "github.com/hashicorp/consul/api" 9 "github.com/hashicorp/terraform/helper/schema" 10 ) 11 12 func resourceConsulKeys() *schema.Resource { 13 return &schema.Resource{ 14 Create: resourceConsulKeysCreate, 15 Update: resourceConsulKeysCreate, 16 Read: resourceConsulKeysRead, 17 Delete: resourceConsulKeysDelete, 18 19 SchemaVersion: 1, 20 MigrateState: resourceConsulKeysMigrateState, 21 22 Schema: map[string]*schema.Schema{ 23 "datacenter": &schema.Schema{ 24 Type: schema.TypeString, 25 Optional: true, 26 Computed: true, 27 ForceNew: true, 28 }, 29 30 "token": &schema.Schema{ 31 Type: schema.TypeString, 32 Optional: true, 33 }, 34 35 "key": &schema.Schema{ 36 Type: schema.TypeSet, 37 Optional: true, 38 Elem: &schema.Resource{ 39 Schema: map[string]*schema.Schema{ 40 "name": &schema.Schema{ 41 Type: schema.TypeString, 42 Required: true, 43 }, 44 45 "path": &schema.Schema{ 46 Type: schema.TypeString, 47 Required: true, 48 }, 49 50 "value": &schema.Schema{ 51 Type: schema.TypeString, 52 Optional: true, 53 Computed: true, 54 }, 55 56 "default": &schema.Schema{ 57 Type: schema.TypeString, 58 Optional: true, 59 }, 60 61 "delete": &schema.Schema{ 62 Type: schema.TypeBool, 63 Optional: true, 64 Default: false, 65 }, 66 }, 67 }, 68 }, 69 70 "var": &schema.Schema{ 71 Type: schema.TypeMap, 72 Computed: true, 73 }, 74 }, 75 } 76 } 77 78 func resourceConsulKeysCreate(d *schema.ResourceData, meta interface{}) error { 79 client := meta.(*consulapi.Client) 80 kv := client.KV() 81 token := d.Get("token").(string) 82 dc, err := getDC(d, client) 83 if err != nil { 84 return err 85 } 86 87 // Setup the operations using the datacenter 88 qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token} 89 wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token} 90 91 // Store the computed vars 92 vars := make(map[string]string) 93 94 // Extract the keys 95 keys := d.Get("key").(*schema.Set).List() 96 for _, raw := range keys { 97 key, path, sub, err := parseKey(raw) 98 if err != nil { 99 return err 100 } 101 102 value := sub["value"].(string) 103 if value != "" { 104 log.Printf("[DEBUG] Setting key '%s' to '%v' in %s", path, value, dc) 105 pair := consulapi.KVPair{Key: path, Value: []byte(value)} 106 if _, err := kv.Put(&pair, &wOpts); err != nil { 107 return fmt.Errorf("Failed to set Consul key '%s': %v", path, err) 108 } 109 vars[key] = value 110 } else { 111 log.Printf("[DEBUG] Getting key '%s' in %s", path, dc) 112 pair, _, err := kv.Get(path, &qOpts) 113 if err != nil { 114 return fmt.Errorf("Failed to get Consul key '%s': %v", path, err) 115 } 116 value := attributeValue(sub, key, pair) 117 vars[key] = value 118 } 119 } 120 121 // The ID doesn't matter, since we use provider config, datacenter, 122 // and key paths to address consul properly. So we just need to fill it in 123 // with some value to indicate the resource has been created. 124 d.SetId("consul") 125 126 // Set the vars we collected above 127 if err := d.Set("var", vars); err != nil { 128 return err 129 } 130 // Store the datacenter on this resource, which can be helpful for reference 131 // in case it was read from the provider 132 d.Set("datacenter", dc) 133 134 return nil 135 } 136 137 func resourceConsulKeysRead(d *schema.ResourceData, meta interface{}) error { 138 client := meta.(*consulapi.Client) 139 kv := client.KV() 140 token := d.Get("token").(string) 141 dc, err := getDC(d, client) 142 if err != nil { 143 return err 144 } 145 146 // Setup the operations using the datacenter 147 qOpts := consulapi.QueryOptions{Datacenter: dc, Token: token} 148 149 // Store the computed vars 150 vars := make(map[string]string) 151 152 // Extract the keys 153 keys := d.Get("key").(*schema.Set).List() 154 for _, raw := range keys { 155 key, path, sub, err := parseKey(raw) 156 if err != nil { 157 return err 158 } 159 160 log.Printf("[DEBUG] Refreshing value of key '%s' in %s", path, dc) 161 pair, _, err := kv.Get(path, &qOpts) 162 if err != nil { 163 return fmt.Errorf("Failed to get value for path '%s' from Consul: %v", path, err) 164 } 165 166 value := attributeValue(sub, key, pair) 167 vars[key] = value 168 } 169 170 // Update the resource 171 if err := d.Set("var", vars); err != nil { 172 return err 173 } 174 // Store the datacenter on this resource, which can be helpful for reference 175 // in case it was read from the provider 176 d.Set("datacenter", dc) 177 178 return nil 179 } 180 181 func resourceConsulKeysDelete(d *schema.ResourceData, meta interface{}) error { 182 client := meta.(*consulapi.Client) 183 kv := client.KV() 184 token := d.Get("token").(string) 185 dc, err := getDC(d, client) 186 if err != nil { 187 return err 188 } 189 190 // Setup the operations using the datacenter 191 wOpts := consulapi.WriteOptions{Datacenter: dc, Token: token} 192 193 // Extract the keys 194 keys := d.Get("key").(*schema.Set).List() 195 for _, raw := range keys { 196 _, path, sub, err := parseKey(raw) 197 if err != nil { 198 return err 199 } 200 201 // Ignore if the key is non-managed 202 shouldDelete, ok := sub["delete"].(bool) 203 if !ok || !shouldDelete { 204 continue 205 } 206 207 log.Printf("[DEBUG] Deleting key '%s' in %s", path, dc) 208 if _, err := kv.Delete(path, &wOpts); err != nil { 209 return fmt.Errorf("Failed to delete Consul key '%s': %v", path, err) 210 } 211 } 212 213 // Clear the ID 214 d.SetId("") 215 return nil 216 } 217 218 // parseKey is used to parse a key into a name, path, config or error 219 func parseKey(raw interface{}) (string, string, map[string]interface{}, error) { 220 sub, ok := raw.(map[string]interface{}) 221 if !ok { 222 return "", "", nil, fmt.Errorf("Failed to unroll: %#v", raw) 223 } 224 225 key, ok := sub["name"].(string) 226 if !ok { 227 return "", "", nil, fmt.Errorf("Failed to expand key '%#v'", sub) 228 } 229 230 path, ok := sub["path"].(string) 231 if !ok { 232 return "", "", nil, fmt.Errorf("Failed to get path for key '%s'", key) 233 } 234 return key, path, sub, nil 235 } 236 237 // attributeValue determines the value for a key, potentially 238 // using a default value if provided. 239 func attributeValue(sub map[string]interface{}, key string, pair *consulapi.KVPair) string { 240 // Use the value if given 241 if pair != nil { 242 return string(pair.Value) 243 } 244 245 // Use a default if given 246 if raw, ok := sub["default"]; ok { 247 switch def := raw.(type) { 248 case string: 249 return def 250 case bool: 251 return strconv.FormatBool(def) 252 } 253 } 254 255 // No value 256 return "" 257 } 258 259 // getDC is used to get the datacenter of the local agent 260 func getDC(d *schema.ResourceData, client *consulapi.Client) (string, error) { 261 if v, ok := d.GetOk("datacenter"); ok { 262 return v.(string), nil 263 } 264 info, err := client.Agent().Self() 265 if err != nil { 266 return "", fmt.Errorf("Failed to get datacenter from Consul agent: %v", err) 267 } 268 return info["Config"]["Datacenter"].(string), nil 269 }