261 lines
8.2 KiB
Go
261 lines
8.2 KiB
Go
package circonus
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/circonus-labs/circonus-gometrics/api/config"
|
|
"github.com/hashicorp/errwrap"
|
|
"github.com/hashicorp/terraform/helper/hashcode"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
const (
|
|
// circonus_check.cloudwatch.* resource attribute names
|
|
checkCloudWatchAPIKeyAttr = "api_key"
|
|
checkCloudWatchAPISecretAttr = "api_secret"
|
|
checkCloudWatchDimmensionsAttr = "dimmensions"
|
|
checkCloudWatchMetricAttr = "metric"
|
|
checkCloudWatchNamespaceAttr = "namespace"
|
|
checkCloudWatchURLAttr = "url"
|
|
checkCloudWatchVersionAttr = "version"
|
|
)
|
|
|
|
var checkCloudWatchDescriptions = attrDescrs{
|
|
checkCloudWatchAPIKeyAttr: "The AWS API Key",
|
|
checkCloudWatchAPISecretAttr: "The AWS API Secret",
|
|
checkCloudWatchDimmensionsAttr: "The dimensions to query for the metric",
|
|
checkCloudWatchMetricAttr: "One or more CloudWatch Metric attributes",
|
|
checkCloudWatchNamespaceAttr: "The namespace to pull telemetry from",
|
|
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.",
|
|
checkCloudWatchVersionAttr: "The version of the Cloudwatch API to use.",
|
|
}
|
|
|
|
var schemaCheckCloudWatch = &schema.Schema{
|
|
Type: schema.TypeSet,
|
|
Optional: true,
|
|
MaxItems: 1,
|
|
MinItems: 1,
|
|
Set: hashCheckCloudWatch,
|
|
Elem: &schema.Resource{
|
|
Schema: convertToHelperSchema(checkCloudWatchDescriptions, map[schemaAttr]*schema.Schema{
|
|
checkCloudWatchAPIKeyAttr: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
Sensitive: true,
|
|
ValidateFunc: validateRegexp(checkCloudWatchAPIKeyAttr, `[\S]+`),
|
|
DefaultFunc: schema.EnvDefaultFunc("AWS_ACCESS_KEY_ID", ""),
|
|
},
|
|
checkCloudWatchAPISecretAttr: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
Sensitive: true,
|
|
ValidateFunc: validateRegexp(checkCloudWatchAPISecretAttr, `[\S]+`),
|
|
DefaultFunc: schema.EnvDefaultFunc("AWS_SECRET_ACCESS_KEY", ""),
|
|
},
|
|
checkCloudWatchDimmensionsAttr: &schema.Schema{
|
|
Type: schema.TypeMap,
|
|
Required: true,
|
|
Elem: schema.TypeString,
|
|
ValidateFunc: validateCheckCloudWatchDimmensions,
|
|
},
|
|
checkCloudWatchMetricAttr: &schema.Schema{
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
MinItems: 1,
|
|
Set: schema.HashString,
|
|
Elem: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
ValidateFunc: validateRegexp(checkCloudWatchMetricAttr, `^([\S]+)$`),
|
|
},
|
|
},
|
|
checkCloudWatchNamespaceAttr: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ValidateFunc: validateRegexp(checkCloudWatchNamespaceAttr, `.+`),
|
|
},
|
|
checkCloudWatchURLAttr: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
ValidateFunc: validateHTTPURL(checkCloudWatchURLAttr, urlIsAbs),
|
|
},
|
|
checkCloudWatchVersionAttr: &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Default: defaultCheckCloudWatchVersion,
|
|
ValidateFunc: validateRegexp(checkCloudWatchVersionAttr, `^[\d]{4}-[\d]{2}-[\d]{2}$`),
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
|
|
// checkAPIToStateCloudWatch reads the Config data out of circonusCheck.CheckBundle into the
|
|
// statefile.
|
|
func checkAPIToStateCloudWatch(c *circonusCheck, d *schema.ResourceData) error {
|
|
cloudwatchConfig := make(map[string]interface{}, len(c.Config))
|
|
|
|
// swamp is a sanity check: it must be empty by the time this method returns
|
|
swamp := make(map[config.Key]string, len(c.Config))
|
|
for k, v := range c.Config {
|
|
swamp[k] = v
|
|
}
|
|
|
|
saveStringConfigToState := func(apiKey config.Key, attrName schemaAttr) {
|
|
if v, ok := c.Config[apiKey]; ok {
|
|
cloudwatchConfig[string(attrName)] = v
|
|
}
|
|
|
|
delete(swamp, apiKey)
|
|
}
|
|
|
|
saveStringConfigToState(config.APIKey, checkCloudWatchAPIKeyAttr)
|
|
saveStringConfigToState(config.APISecret, checkCloudWatchAPISecretAttr)
|
|
|
|
dimmensions := make(map[string]interface{}, len(c.Config))
|
|
dimmensionPrefixLen := len(config.DimPrefix)
|
|
for k, v := range c.Config {
|
|
if len(k) <= dimmensionPrefixLen {
|
|
continue
|
|
}
|
|
|
|
if strings.Compare(string(k[:dimmensionPrefixLen]), string(config.DimPrefix)) == 0 {
|
|
key := k[dimmensionPrefixLen:]
|
|
dimmensions[string(key)] = v
|
|
}
|
|
delete(swamp, k)
|
|
}
|
|
cloudwatchConfig[string(checkCloudWatchDimmensionsAttr)] = dimmensions
|
|
|
|
metricSet := schema.NewSet(schema.HashString, nil)
|
|
metricList := strings.Split(c.Config[config.CloudwatchMetrics], ",")
|
|
for _, m := range metricList {
|
|
metricSet.Add(m)
|
|
}
|
|
cloudwatchConfig[string(checkCloudWatchMetricAttr)] = metricSet
|
|
|
|
saveStringConfigToState(config.Namespace, checkCloudWatchNamespaceAttr)
|
|
saveStringConfigToState(config.URL, checkCloudWatchURLAttr)
|
|
saveStringConfigToState(config.Version, checkCloudWatchVersionAttr)
|
|
|
|
whitelistedConfigKeys := map[config.Key]struct{}{
|
|
config.ReverseSecretKey: struct{}{},
|
|
config.SubmissionURL: struct{}{},
|
|
}
|
|
|
|
for k := range swamp {
|
|
if _, ok := whitelistedConfigKeys[k]; ok {
|
|
delete(c.Config, k)
|
|
}
|
|
|
|
if _, ok := whitelistedConfigKeys[k]; !ok {
|
|
log.Printf("[ERROR]: PROVIDER BUG: API Config not empty: %#v", swamp)
|
|
}
|
|
}
|
|
|
|
if err := d.Set(checkCloudWatchAttr, schema.NewSet(hashCheckCloudWatch, []interface{}{cloudwatchConfig})); err != nil {
|
|
return errwrap.Wrapf(fmt.Sprintf("Unable to store check %q attribute: {{err}}", checkCloudWatchAttr), err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// hashCheckCloudWatch creates a stable hash of the normalized values
|
|
func hashCheckCloudWatch(v interface{}) int {
|
|
m := v.(map[string]interface{})
|
|
b := &bytes.Buffer{}
|
|
b.Grow(defaultHashBufSize)
|
|
|
|
writeString := func(attrName schemaAttr) {
|
|
if v, ok := m[string(attrName)]; ok && v.(string) != "" {
|
|
fmt.Fprint(b, strings.TrimSpace(v.(string)))
|
|
}
|
|
}
|
|
|
|
// Order writes to the buffer using lexically sorted list for easy visual
|
|
// reconciliation with other lists.
|
|
writeString(checkCloudWatchAPIKeyAttr)
|
|
writeString(checkCloudWatchAPISecretAttr)
|
|
|
|
if dimmensionsRaw, ok := m[string(checkCloudWatchDimmensionsAttr)]; ok {
|
|
dimmensionMap := dimmensionsRaw.(map[string]interface{})
|
|
dimmensions := make([]string, 0, len(dimmensionMap))
|
|
for k := range dimmensionMap {
|
|
dimmensions = append(dimmensions, k)
|
|
}
|
|
|
|
sort.Strings(dimmensions)
|
|
for i := range dimmensions {
|
|
fmt.Fprint(b, dimmensions[i])
|
|
}
|
|
}
|
|
|
|
if metricsRaw, ok := m[string(checkCloudWatchMetricAttr)]; ok {
|
|
metricListRaw := flattenSet(metricsRaw.(*schema.Set))
|
|
for i := range metricListRaw {
|
|
if metricListRaw[i] == nil {
|
|
continue
|
|
}
|
|
fmt.Fprint(b, *metricListRaw[i])
|
|
}
|
|
}
|
|
|
|
writeString(checkCloudWatchNamespaceAttr)
|
|
writeString(checkCloudWatchURLAttr)
|
|
writeString(checkCloudWatchVersionAttr)
|
|
|
|
s := b.String()
|
|
return hashcode.String(s)
|
|
}
|
|
|
|
func checkConfigToAPICloudWatch(c *circonusCheck, l interfaceList) error {
|
|
c.Type = string(apiCheckTypeCloudWatchAttr)
|
|
|
|
// Iterate over all `cloudwatch` attributes, even though we have a max of 1 in the
|
|
// schema.
|
|
for _, mapRaw := range l {
|
|
cloudwatchConfig := newInterfaceMap(mapRaw)
|
|
|
|
if v, found := cloudwatchConfig[checkCloudWatchAPIKeyAttr]; found {
|
|
c.Config[config.APIKey] = v.(string)
|
|
}
|
|
|
|
if v, found := cloudwatchConfig[checkCloudWatchAPISecretAttr]; found {
|
|
c.Config[config.APISecret] = v.(string)
|
|
}
|
|
|
|
if dimmensions := cloudwatchConfig.CollectMap(checkCloudWatchDimmensionsAttr); dimmensions != nil {
|
|
for k, v := range dimmensions {
|
|
dimKey := config.DimPrefix + config.Key(k)
|
|
c.Config[dimKey] = v
|
|
}
|
|
}
|
|
|
|
if v, found := cloudwatchConfig[checkCloudWatchMetricAttr]; found {
|
|
metricsRaw := v.(*schema.Set).List()
|
|
metrics := make([]string, 0, len(metricsRaw))
|
|
for _, m := range metricsRaw {
|
|
metrics = append(metrics, m.(string))
|
|
}
|
|
sort.Strings(metrics)
|
|
c.Config[config.CloudwatchMetrics] = strings.Join(metrics, ",")
|
|
}
|
|
|
|
if v, found := cloudwatchConfig[checkCloudWatchNamespaceAttr]; found {
|
|
c.Config[config.Namespace] = v.(string)
|
|
}
|
|
|
|
if v, found := cloudwatchConfig[checkCloudWatchURLAttr]; found {
|
|
c.Config[config.URL] = v.(string)
|
|
}
|
|
|
|
if v, found := cloudwatchConfig[checkCloudWatchVersionAttr]; found {
|
|
c.Config[config.Version] = v.(string)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|