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