github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/circonus/resource_circonus_check_http.go (about) 1 package circonus 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "net/url" 8 "sort" 9 "strconv" 10 "strings" 11 12 "github.com/circonus-labs/circonus-gometrics/api/config" 13 "github.com/hashicorp/errwrap" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 "github.com/hashicorp/terraform/helper/schema" 16 ) 17 18 const ( 19 // circonus_check.http.* resource attribute names 20 checkHTTPAuthMethodAttr = "auth_method" 21 checkHTTPAuthPasswordAttr = "auth_password" 22 checkHTTPAuthUserAttr = "auth_user" 23 checkHTTPBodyRegexpAttr = "body_regexp" 24 checkHTTPCAChainAttr = "ca_chain" 25 checkHTTPCertFileAttr = "certificate_file" 26 checkHTTPCiphersAttr = "ciphers" 27 checkHTTPCodeRegexpAttr = "code" 28 checkHTTPExtractAttr = "extract" 29 checkHTTPHeadersAttr = "headers" 30 checkHTTPKeyFileAttr = "key_file" 31 checkHTTPMethodAttr = "method" 32 checkHTTPPayloadAttr = "payload" 33 checkHTTPReadLimitAttr = "read_limit" 34 checkHTTPURLAttr = "url" 35 checkHTTPVersionAttr = "version" 36 ) 37 38 var checkHTTPDescriptions = attrDescrs{ 39 checkHTTPAuthMethodAttr: "The HTTP Authentication method", 40 checkHTTPAuthPasswordAttr: "The HTTP Authentication user password", 41 checkHTTPAuthUserAttr: "The HTTP Authentication user name", 42 checkHTTPBodyRegexpAttr: `This regular expression is matched against the body of the response. If a match is not found, the check will be marked as "bad.`, 43 checkHTTPCAChainAttr: "A path to a file containing all the certificate authorities that should be loaded to validate the remote certificate (for TLS checks)", 44 checkHTTPCodeRegexpAttr: `The HTTP code that is expected. If the code received does not match this regular expression, the check is marked as "bad."`, 45 checkHTTPCiphersAttr: "A list of ciphers to be used in the TLS protocol (for HTTPS checks)", 46 checkHTTPCertFileAttr: "A path to a file containing the client certificate that will be presented to the remote server (for TLS-enabled checks)", 47 checkHTTPExtractAttr: "This regular expression is matched against the body of the response globally. The first capturing match is the key and the second capturing match is the value. Each key/value extracted is registered as a metric for the check.", 48 checkHTTPHeadersAttr: "Map of HTTP Headers to send along with HTTP Requests", 49 checkHTTPKeyFileAttr: "A path to a file containing key to be used in conjunction with the cilent certificate (for TLS checks)", 50 checkHTTPMethodAttr: "The HTTP method to use", 51 checkHTTPPayloadAttr: "The information transferred as the payload of an HTTP request", 52 checkHTTPReadLimitAttr: "Sets an approximate limit on the data read (0 means no limit)", 53 checkHTTPURLAttr: "The URL to use as the target of the check", 54 checkHTTPVersionAttr: "Sets the HTTP version for the check to use", 55 } 56 57 var schemaCheckHTTP = &schema.Schema{ 58 Type: schema.TypeSet, 59 Optional: true, 60 MaxItems: 1, 61 MinItems: 1, 62 Set: hashCheckHTTP, 63 Elem: &schema.Resource{ 64 Schema: convertToHelperSchema(checkHTTPDescriptions, map[schemaAttr]*schema.Schema{ 65 checkHTTPAuthMethodAttr: &schema.Schema{ 66 Type: schema.TypeString, 67 Optional: true, 68 ValidateFunc: validateRegexp(checkHTTPAuthMethodAttr, `^(?:Basic|Digest|Auto)$`), 69 }, 70 checkHTTPAuthPasswordAttr: &schema.Schema{ 71 Type: schema.TypeString, 72 Optional: true, 73 Sensitive: true, 74 ValidateFunc: validateRegexp(checkHTTPAuthPasswordAttr, `^.*`), 75 }, 76 checkHTTPAuthUserAttr: &schema.Schema{ 77 Type: schema.TypeString, 78 Optional: true, 79 ValidateFunc: validateRegexp(checkHTTPAuthUserAttr, `[^:]+`), 80 }, 81 checkHTTPBodyRegexpAttr: &schema.Schema{ 82 Type: schema.TypeString, 83 Optional: true, 84 ValidateFunc: validateRegexp(checkHTTPBodyRegexpAttr, `.+`), 85 }, 86 checkHTTPCAChainAttr: &schema.Schema{ 87 Type: schema.TypeString, 88 Optional: true, 89 ValidateFunc: validateRegexp(checkHTTPCAChainAttr, `.+`), 90 }, 91 checkHTTPCertFileAttr: &schema.Schema{ 92 Type: schema.TypeString, 93 Optional: true, 94 ValidateFunc: validateRegexp(checkHTTPCertFileAttr, `.+`), 95 }, 96 checkHTTPCiphersAttr: &schema.Schema{ 97 Type: schema.TypeString, 98 Optional: true, 99 ValidateFunc: validateRegexp(checkHTTPCiphersAttr, `.+`), 100 }, 101 checkHTTPCodeRegexpAttr: &schema.Schema{ 102 Type: schema.TypeString, 103 Optional: true, 104 Default: defaultCheckHTTPCodeRegexp, 105 ValidateFunc: validateRegexp(checkHTTPCodeRegexpAttr, `.+`), 106 }, 107 checkHTTPExtractAttr: &schema.Schema{ 108 Type: schema.TypeString, 109 Optional: true, 110 ValidateFunc: validateRegexp(checkHTTPExtractAttr, `.+`), 111 }, 112 checkHTTPHeadersAttr: &schema.Schema{ 113 Type: schema.TypeMap, 114 Elem: schema.TypeString, 115 Optional: true, 116 ValidateFunc: validateHTTPHeaders, 117 }, 118 checkHTTPKeyFileAttr: &schema.Schema{ 119 Type: schema.TypeString, 120 Optional: true, 121 ValidateFunc: validateRegexp(checkHTTPKeyFileAttr, `.+`), 122 }, 123 checkHTTPMethodAttr: &schema.Schema{ 124 Type: schema.TypeString, 125 Optional: true, 126 Default: defaultCheckHTTPMethod, 127 ValidateFunc: validateRegexp(checkHTTPMethodAttr, `\S+`), 128 }, 129 checkHTTPPayloadAttr: &schema.Schema{ 130 Type: schema.TypeString, 131 Optional: true, 132 ValidateFunc: validateRegexp(checkHTTPPayloadAttr, `\S+`), 133 }, 134 checkHTTPReadLimitAttr: &schema.Schema{ 135 Type: schema.TypeInt, 136 Optional: true, 137 ValidateFunc: validateFuncs( 138 validateIntMin(checkHTTPReadLimitAttr, 0), 139 ), 140 }, 141 checkHTTPURLAttr: &schema.Schema{ 142 Type: schema.TypeString, 143 Required: true, 144 ValidateFunc: validateFuncs( 145 validateHTTPURL(checkHTTPURLAttr, urlIsAbs), 146 ), 147 }, 148 checkHTTPVersionAttr: &schema.Schema{ 149 Type: schema.TypeString, 150 Optional: true, 151 Default: defaultCheckHTTPVersion, 152 ValidateFunc: validateStringIn(checkHTTPVersionAttr, supportedHTTPVersions), 153 }, 154 }), 155 }, 156 } 157 158 // checkAPIToStateHTTP reads the Config data out of circonusCheck.CheckBundle into the 159 // statefile. 160 func checkAPIToStateHTTP(c *circonusCheck, d *schema.ResourceData) error { 161 httpConfig := make(map[string]interface{}, len(c.Config)) 162 163 // swamp is a sanity check: it must be empty by the time this method returns 164 swamp := make(map[config.Key]string, len(c.Config)) 165 for k, v := range c.Config { 166 swamp[k] = v 167 } 168 169 saveStringConfigToState := func(apiKey config.Key, attrName schemaAttr) { 170 if v, ok := c.Config[apiKey]; ok { 171 httpConfig[string(attrName)] = v 172 } 173 174 delete(swamp, apiKey) 175 } 176 177 saveIntConfigToState := func(apiKey config.Key, attrName schemaAttr) { 178 if v, ok := c.Config[apiKey]; ok { 179 i, err := strconv.ParseInt(v, 10, 64) 180 if err != nil { 181 log.Printf("[ERROR]: Unable to convert %s to an integer: %v", apiKey, err) 182 return 183 } 184 185 httpConfig[string(attrName)] = int(i) 186 } 187 188 delete(swamp, apiKey) 189 } 190 191 saveStringConfigToState(config.AuthMethod, checkHTTPAuthMethodAttr) 192 saveStringConfigToState(config.AuthPassword, checkHTTPAuthPasswordAttr) 193 saveStringConfigToState(config.AuthUser, checkHTTPAuthUserAttr) 194 saveStringConfigToState(config.Body, checkHTTPBodyRegexpAttr) 195 saveStringConfigToState(config.CAChain, checkHTTPCAChainAttr) 196 saveStringConfigToState(config.CertFile, checkHTTPCertFileAttr) 197 saveStringConfigToState(config.Ciphers, checkHTTPCiphersAttr) 198 saveStringConfigToState(config.Code, checkHTTPCodeRegexpAttr) 199 saveStringConfigToState(config.Extract, checkHTTPExtractAttr) 200 201 headers := make(map[string]interface{}, len(c.Config)) 202 headerPrefixLen := len(config.HeaderPrefix) 203 for k, v := range c.Config { 204 if len(k) <= headerPrefixLen { 205 continue 206 } 207 208 if strings.Compare(string(k[:headerPrefixLen]), string(config.HeaderPrefix)) == 0 { 209 key := k[headerPrefixLen:] 210 headers[string(key)] = v 211 } 212 delete(swamp, k) 213 } 214 httpConfig[string(checkHTTPHeadersAttr)] = headers 215 216 saveStringConfigToState(config.KeyFile, checkHTTPKeyFileAttr) 217 saveStringConfigToState(config.Method, checkHTTPMethodAttr) 218 saveStringConfigToState(config.Payload, checkHTTPPayloadAttr) 219 saveIntConfigToState(config.ReadLimit, checkHTTPReadLimitAttr) 220 saveStringConfigToState(config.URL, checkHTTPURLAttr) 221 saveStringConfigToState(config.HTTPVersion, checkHTTPVersionAttr) 222 223 whitelistedConfigKeys := map[config.Key]struct{}{ 224 config.ReverseSecretKey: struct{}{}, 225 config.SubmissionURL: struct{}{}, 226 } 227 228 for k := range swamp { 229 if _, ok := whitelistedConfigKeys[k]; ok { 230 delete(c.Config, k) 231 } 232 233 if _, ok := whitelistedConfigKeys[k]; !ok { 234 return fmt.Errorf("PROVIDER BUG: API Config not empty: %#v", swamp) 235 } 236 } 237 238 if err := d.Set(checkHTTPAttr, schema.NewSet(hashCheckHTTP, []interface{}{httpConfig})); err != nil { 239 return errwrap.Wrapf(fmt.Sprintf("Unable to store check %q attribute: {{err}}", checkHTTPAttr), err) 240 } 241 242 return nil 243 } 244 245 // hashCheckHTTP creates a stable hash of the normalized values 246 func hashCheckHTTP(v interface{}) int { 247 m := v.(map[string]interface{}) 248 b := &bytes.Buffer{} 249 b.Grow(defaultHashBufSize) 250 251 writeInt := func(attrName schemaAttr) { 252 if v, ok := m[string(attrName)]; ok { 253 fmt.Fprintf(b, "%x", v.(int)) 254 } 255 } 256 257 writeString := func(attrName schemaAttr) { 258 if v, ok := m[string(attrName)]; ok && v.(string) != "" { 259 fmt.Fprint(b, strings.TrimSpace(v.(string))) 260 } 261 } 262 263 // Order writes to the buffer using lexically sorted list for easy visual 264 // reconciliation with other lists. 265 writeString(checkHTTPAuthMethodAttr) 266 writeString(checkHTTPAuthPasswordAttr) 267 writeString(checkHTTPAuthUserAttr) 268 writeString(checkHTTPBodyRegexpAttr) 269 writeString(checkHTTPCAChainAttr) 270 writeString(checkHTTPCertFileAttr) 271 writeString(checkHTTPCiphersAttr) 272 writeString(checkHTTPCodeRegexpAttr) 273 writeString(checkHTTPExtractAttr) 274 275 if headersRaw, ok := m[string(checkHTTPHeadersAttr)]; ok { 276 headerMap := headersRaw.(map[string]interface{}) 277 headers := make([]string, 0, len(headerMap)) 278 for k := range headerMap { 279 headers = append(headers, k) 280 } 281 282 sort.Strings(headers) 283 for i := range headers { 284 fmt.Fprint(b, headers[i]) 285 fmt.Fprint(b, headerMap[headers[i]].(string)) 286 } 287 } 288 289 writeString(checkHTTPKeyFileAttr) 290 writeString(checkHTTPMethodAttr) 291 writeString(checkHTTPPayloadAttr) 292 writeInt(checkHTTPReadLimitAttr) 293 writeString(checkHTTPURLAttr) 294 writeString(checkHTTPVersionAttr) 295 296 s := b.String() 297 return hashcode.String(s) 298 } 299 300 func checkConfigToAPIHTTP(c *circonusCheck, l interfaceList) error { 301 c.Type = string(apiCheckTypeHTTP) 302 303 // Iterate over all `http` attributes, even though we have a max of 1 in the 304 // schema. 305 for _, mapRaw := range l { 306 httpConfig := newInterfaceMap(mapRaw) 307 308 if v, found := httpConfig[checkHTTPAuthMethodAttr]; found { 309 c.Config[config.AuthMethod] = v.(string) 310 } 311 312 if v, found := httpConfig[checkHTTPAuthPasswordAttr]; found { 313 c.Config[config.AuthPassword] = v.(string) 314 } 315 316 if v, found := httpConfig[checkHTTPAuthUserAttr]; found { 317 c.Config[config.AuthUser] = v.(string) 318 } 319 320 if v, found := httpConfig[checkHTTPBodyRegexpAttr]; found { 321 c.Config[config.Body] = v.(string) 322 } 323 324 if v, found := httpConfig[checkHTTPCAChainAttr]; found { 325 c.Config[config.CAChain] = v.(string) 326 } 327 328 if v, found := httpConfig[checkHTTPCertFileAttr]; found { 329 c.Config[config.CertFile] = v.(string) 330 } 331 332 if v, found := httpConfig[checkHTTPCiphersAttr]; found { 333 c.Config[config.Ciphers] = v.(string) 334 } 335 336 if v, found := httpConfig[checkHTTPCodeRegexpAttr]; found { 337 c.Config[config.Code] = v.(string) 338 } 339 340 if v, found := httpConfig[checkHTTPExtractAttr]; found { 341 c.Config[config.Extract] = v.(string) 342 } 343 344 if headers := httpConfig.CollectMap(checkHTTPHeadersAttr); headers != nil { 345 for k, v := range headers { 346 h := config.HeaderPrefix + config.Key(k) 347 c.Config[h] = v 348 } 349 } 350 351 if v, found := httpConfig[checkHTTPKeyFileAttr]; found { 352 c.Config[config.KeyFile] = v.(string) 353 } 354 355 if v, found := httpConfig[checkHTTPMethodAttr]; found { 356 c.Config[config.Method] = v.(string) 357 } 358 359 if v, found := httpConfig[checkHTTPPayloadAttr]; found { 360 c.Config[config.Payload] = v.(string) 361 } 362 363 if v, found := httpConfig[checkHTTPReadLimitAttr]; found { 364 c.Config[config.ReadLimit] = fmt.Sprintf("%d", v.(int)) 365 } 366 367 if v, found := httpConfig[checkHTTPURLAttr]; found { 368 c.Config[config.URL] = v.(string) 369 370 u, _ := url.Parse(v.(string)) 371 hostInfo := strings.SplitN(u.Host, ":", 2) 372 if len(c.Target) == 0 { 373 c.Target = hostInfo[0] 374 } 375 376 if len(hostInfo) > 1 && c.Config[config.Port] == "" { 377 c.Config[config.Port] = hostInfo[1] 378 } 379 } 380 381 if v, found := httpConfig[checkHTTPVersionAttr]; found { 382 c.Config[config.HTTPVersion] = v.(string) 383 } 384 } 385 386 return nil 387 }