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  }