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 }