github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/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  }