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  }