github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/circonus/resource_circonus_check_cloudwatch.go (about)

     1  package circonus
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"sort"
     8  	"strings"
     9  
    10  	"github.com/circonus-labs/circonus-gometrics/api/config"
    11  	"github.com/hashicorp/errwrap"
    12  	"github.com/hashicorp/terraform/helper/hashcode"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  const (
    17  	// circonus_check.cloudwatch.* resource attribute names
    18  	checkCloudWatchAPIKeyAttr      = "api_key"
    19  	checkCloudWatchAPISecretAttr   = "api_secret"
    20  	checkCloudWatchDimmensionsAttr = "dimmensions"
    21  	checkCloudWatchMetricAttr      = "metric"
    22  	checkCloudWatchNamespaceAttr   = "namespace"
    23  	checkCloudWatchURLAttr         = "url"
    24  	checkCloudWatchVersionAttr     = "version"
    25  )
    26  
    27  var checkCloudWatchDescriptions = attrDescrs{
    28  	checkCloudWatchAPIKeyAttr:      "The AWS API Key",
    29  	checkCloudWatchAPISecretAttr:   "The AWS API Secret",
    30  	checkCloudWatchDimmensionsAttr: "The dimensions to query for the metric",
    31  	checkCloudWatchMetricAttr:      "One or more CloudWatch Metric attributes",
    32  	checkCloudWatchNamespaceAttr:   "The namespace to pull telemetry from",
    33  	checkCloudWatchURLAttr:         "The URL including schema and hostname for the Cloudwatch monitoring server. This value will be used to specify the region - for example, to pull from us-east-1, the URL would be https://monitoring.us-east-1.amazonaws.com.",
    34  	checkCloudWatchVersionAttr:     "The version of the Cloudwatch API to use.",
    35  }
    36  
    37  var schemaCheckCloudWatch = &schema.Schema{
    38  	Type:     schema.TypeSet,
    39  	Optional: true,
    40  	MaxItems: 1,
    41  	MinItems: 1,
    42  	Set:      hashCheckCloudWatch,
    43  	Elem: &schema.Resource{
    44  		Schema: convertToHelperSchema(checkCloudWatchDescriptions, map[schemaAttr]*schema.Schema{
    45  			checkCloudWatchAPIKeyAttr: &schema.Schema{
    46  				Type:         schema.TypeString,
    47  				Required:     true,
    48  				Sensitive:    true,
    49  				ValidateFunc: validateRegexp(checkCloudWatchAPIKeyAttr, `[\S]+`),
    50  				DefaultFunc:  schema.EnvDefaultFunc("AWS_ACCESS_KEY_ID", ""),
    51  			},
    52  			checkCloudWatchAPISecretAttr: &schema.Schema{
    53  				Type:         schema.TypeString,
    54  				Required:     true,
    55  				Sensitive:    true,
    56  				ValidateFunc: validateRegexp(checkCloudWatchAPISecretAttr, `[\S]+`),
    57  				DefaultFunc:  schema.EnvDefaultFunc("AWS_SECRET_ACCESS_KEY", ""),
    58  			},
    59  			checkCloudWatchDimmensionsAttr: &schema.Schema{
    60  				Type:         schema.TypeMap,
    61  				Required:     true,
    62  				Elem:         schema.TypeString,
    63  				ValidateFunc: validateCheckCloudWatchDimmensions,
    64  			},
    65  			checkCloudWatchMetricAttr: &schema.Schema{
    66  				Type:     schema.TypeSet,
    67  				Required: true,
    68  				MinItems: 1,
    69  				Set:      schema.HashString,
    70  				Elem: &schema.Schema{
    71  					Type:         schema.TypeString,
    72  					ValidateFunc: validateRegexp(checkCloudWatchMetricAttr, `^([\S]+)$`),
    73  				},
    74  			},
    75  			checkCloudWatchNamespaceAttr: &schema.Schema{
    76  				Type:         schema.TypeString,
    77  				Required:     true,
    78  				ValidateFunc: validateRegexp(checkCloudWatchNamespaceAttr, `.+`),
    79  			},
    80  			checkCloudWatchURLAttr: &schema.Schema{
    81  				Type:         schema.TypeString,
    82  				Required:     true,
    83  				ValidateFunc: validateHTTPURL(checkCloudWatchURLAttr, urlIsAbs),
    84  			},
    85  			checkCloudWatchVersionAttr: &schema.Schema{
    86  				Type:         schema.TypeString,
    87  				Optional:     true,
    88  				Default:      defaultCheckCloudWatchVersion,
    89  				ValidateFunc: validateRegexp(checkCloudWatchVersionAttr, `^[\d]{4}-[\d]{2}-[\d]{2}$`),
    90  			},
    91  		}),
    92  	},
    93  }
    94  
    95  // checkAPIToStateCloudWatch reads the Config data out of circonusCheck.CheckBundle into the
    96  // statefile.
    97  func checkAPIToStateCloudWatch(c *circonusCheck, d *schema.ResourceData) error {
    98  	cloudwatchConfig := make(map[string]interface{}, len(c.Config))
    99  
   100  	// swamp is a sanity check: it must be empty by the time this method returns
   101  	swamp := make(map[config.Key]string, len(c.Config))
   102  	for k, v := range c.Config {
   103  		swamp[k] = v
   104  	}
   105  
   106  	saveStringConfigToState := func(apiKey config.Key, attrName schemaAttr) {
   107  		if v, ok := c.Config[apiKey]; ok {
   108  			cloudwatchConfig[string(attrName)] = v
   109  		}
   110  
   111  		delete(swamp, apiKey)
   112  	}
   113  
   114  	saveStringConfigToState(config.APIKey, checkCloudWatchAPIKeyAttr)
   115  	saveStringConfigToState(config.APISecret, checkCloudWatchAPISecretAttr)
   116  
   117  	dimmensions := make(map[string]interface{}, len(c.Config))
   118  	dimmensionPrefixLen := len(config.DimPrefix)
   119  	for k, v := range c.Config {
   120  		if len(k) <= dimmensionPrefixLen {
   121  			continue
   122  		}
   123  
   124  		if strings.Compare(string(k[:dimmensionPrefixLen]), string(config.DimPrefix)) == 0 {
   125  			key := k[dimmensionPrefixLen:]
   126  			dimmensions[string(key)] = v
   127  		}
   128  		delete(swamp, k)
   129  	}
   130  	cloudwatchConfig[string(checkCloudWatchDimmensionsAttr)] = dimmensions
   131  
   132  	metricSet := schema.NewSet(schema.HashString, nil)
   133  	metricList := strings.Split(c.Config[config.CloudwatchMetrics], ",")
   134  	for _, m := range metricList {
   135  		metricSet.Add(m)
   136  	}
   137  	cloudwatchConfig[string(checkCloudWatchMetricAttr)] = metricSet
   138  
   139  	saveStringConfigToState(config.Namespace, checkCloudWatchNamespaceAttr)
   140  	saveStringConfigToState(config.URL, checkCloudWatchURLAttr)
   141  	saveStringConfigToState(config.Version, checkCloudWatchVersionAttr)
   142  
   143  	whitelistedConfigKeys := map[config.Key]struct{}{
   144  		config.ReverseSecretKey: struct{}{},
   145  		config.SubmissionURL:    struct{}{},
   146  	}
   147  
   148  	for k := range swamp {
   149  		if _, ok := whitelistedConfigKeys[k]; ok {
   150  			delete(c.Config, k)
   151  		}
   152  
   153  		if _, ok := whitelistedConfigKeys[k]; !ok {
   154  			log.Printf("[ERROR]: PROVIDER BUG: API Config not empty: %#v", swamp)
   155  		}
   156  	}
   157  
   158  	if err := d.Set(checkCloudWatchAttr, schema.NewSet(hashCheckCloudWatch, []interface{}{cloudwatchConfig})); err != nil {
   159  		return errwrap.Wrapf(fmt.Sprintf("Unable to store check %q attribute: {{err}}", checkCloudWatchAttr), err)
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  // hashCheckCloudWatch creates a stable hash of the normalized values
   166  func hashCheckCloudWatch(v interface{}) int {
   167  	m := v.(map[string]interface{})
   168  	b := &bytes.Buffer{}
   169  	b.Grow(defaultHashBufSize)
   170  
   171  	writeString := func(attrName schemaAttr) {
   172  		if v, ok := m[string(attrName)]; ok && v.(string) != "" {
   173  			fmt.Fprint(b, strings.TrimSpace(v.(string)))
   174  		}
   175  	}
   176  
   177  	// Order writes to the buffer using lexically sorted list for easy visual
   178  	// reconciliation with other lists.
   179  	writeString(checkCloudWatchAPIKeyAttr)
   180  	writeString(checkCloudWatchAPISecretAttr)
   181  
   182  	if dimmensionsRaw, ok := m[string(checkCloudWatchDimmensionsAttr)]; ok {
   183  		dimmensionMap := dimmensionsRaw.(map[string]interface{})
   184  		dimmensions := make([]string, 0, len(dimmensionMap))
   185  		for k := range dimmensionMap {
   186  			dimmensions = append(dimmensions, k)
   187  		}
   188  
   189  		sort.Strings(dimmensions)
   190  		for i := range dimmensions {
   191  			fmt.Fprint(b, dimmensions[i])
   192  		}
   193  	}
   194  
   195  	if metricsRaw, ok := m[string(checkCloudWatchMetricAttr)]; ok {
   196  		metricListRaw := flattenSet(metricsRaw.(*schema.Set))
   197  		for i := range metricListRaw {
   198  			if metricListRaw[i] == nil {
   199  				continue
   200  			}
   201  			fmt.Fprint(b, *metricListRaw[i])
   202  		}
   203  	}
   204  
   205  	writeString(checkCloudWatchNamespaceAttr)
   206  	writeString(checkCloudWatchURLAttr)
   207  	writeString(checkCloudWatchVersionAttr)
   208  
   209  	s := b.String()
   210  	return hashcode.String(s)
   211  }
   212  
   213  func checkConfigToAPICloudWatch(c *circonusCheck, l interfaceList) error {
   214  	c.Type = string(apiCheckTypeCloudWatchAttr)
   215  
   216  	// Iterate over all `cloudwatch` attributes, even though we have a max of 1 in the
   217  	// schema.
   218  	for _, mapRaw := range l {
   219  		cloudwatchConfig := newInterfaceMap(mapRaw)
   220  
   221  		if v, found := cloudwatchConfig[checkCloudWatchAPIKeyAttr]; found {
   222  			c.Config[config.APIKey] = v.(string)
   223  		}
   224  
   225  		if v, found := cloudwatchConfig[checkCloudWatchAPISecretAttr]; found {
   226  			c.Config[config.APISecret] = v.(string)
   227  		}
   228  
   229  		if dimmensions := cloudwatchConfig.CollectMap(checkCloudWatchDimmensionsAttr); dimmensions != nil {
   230  			for k, v := range dimmensions {
   231  				dimKey := config.DimPrefix + config.Key(k)
   232  				c.Config[dimKey] = v
   233  			}
   234  		}
   235  
   236  		if v, found := cloudwatchConfig[checkCloudWatchMetricAttr]; found {
   237  			metricsRaw := v.(*schema.Set).List()
   238  			metrics := make([]string, 0, len(metricsRaw))
   239  			for _, m := range metricsRaw {
   240  				metrics = append(metrics, m.(string))
   241  			}
   242  			sort.Strings(metrics)
   243  			c.Config[config.CloudwatchMetrics] = strings.Join(metrics, ",")
   244  		}
   245  
   246  		if v, found := cloudwatchConfig[checkCloudWatchNamespaceAttr]; found {
   247  			c.Config[config.Namespace] = v.(string)
   248  		}
   249  
   250  		if v, found := cloudwatchConfig[checkCloudWatchURLAttr]; found {
   251  			c.Config[config.URL] = v.(string)
   252  		}
   253  
   254  		if v, found := cloudwatchConfig[checkCloudWatchVersionAttr]; found {
   255  			c.Config[config.Version] = v.(string)
   256  		}
   257  	}
   258  
   259  	return nil
   260  }