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

     1  package circonus
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"regexp"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/circonus-labs/circonus-gometrics/api"
    11  	"github.com/circonus-labs/circonus-gometrics/api/config"
    12  	"github.com/hashicorp/errwrap"
    13  )
    14  
    15  var knownCheckTypes map[circonusCheckType]struct{}
    16  var knownContactMethods map[contactMethods]struct{}
    17  
    18  var userContactMethods map[contactMethods]struct{}
    19  var externalContactMethods map[contactMethods]struct{}
    20  var supportedHTTPVersions = validStringValues{"0.9", "1.0", "1.1", "2.0"}
    21  var supportedMetricClusterTypes = validStringValues{
    22  	"average", "count", "counter", "counter2", "counter2_stddev",
    23  	"counter_stddev", "derive", "derive2", "derive2_stddev", "derive_stddev",
    24  	"histogram", "stddev", "text",
    25  }
    26  
    27  func init() {
    28  	checkTypes := []circonusCheckType{
    29  		"caql", "cim", "circonuswindowsagent", "circonuswindowsagent,nad",
    30  		"collectd", "composite", "dcm", "dhcp", "dns", "elasticsearch",
    31  		"external", "ganglia", "googleanalytics", "haproxy", "http",
    32  		"http,apache", "httptrap", "imap", "jmx", "json", "json,couchdb",
    33  		"json,mongodb", "json,nad", "json,riak", "ldap", "memcached",
    34  		"munin", "mysql", "newrelic_rpm", "nginx", "nrpe", "ntp",
    35  		"oracle", "ping_icmp", "pop3", "postgres", "redis", "resmon",
    36  		"smtp", "snmp", "snmp,momentum", "sqlserver", "ssh2", "statsd",
    37  		"tcp", "varnish", "keynote", "keynote_pulse", "cloudwatch",
    38  		"ec_console", "mongodb",
    39  	}
    40  
    41  	knownCheckTypes = make(map[circonusCheckType]struct{}, len(checkTypes))
    42  	for _, k := range checkTypes {
    43  		knownCheckTypes[k] = struct{}{}
    44  	}
    45  
    46  	userMethods := []contactMethods{"email", "sms", "xmpp"}
    47  	externalMethods := []contactMethods{"slack"}
    48  
    49  	knownContactMethods = make(map[contactMethods]struct{}, len(externalContactMethods)+len(userContactMethods))
    50  
    51  	externalContactMethods = make(map[contactMethods]struct{}, len(externalMethods))
    52  	for _, k := range externalMethods {
    53  		knownContactMethods[k] = struct{}{}
    54  		externalContactMethods[k] = struct{}{}
    55  	}
    56  
    57  	userContactMethods = make(map[contactMethods]struct{}, len(userMethods))
    58  	for _, k := range userMethods {
    59  		knownContactMethods[k] = struct{}{}
    60  		userContactMethods[k] = struct{}{}
    61  	}
    62  }
    63  
    64  func validateCheckType(v interface{}, key string) (warnings []string, errors []error) {
    65  	if _, ok := knownCheckTypes[circonusCheckType(v.(string))]; !ok {
    66  		warnings = append(warnings, fmt.Sprintf("Possibly unsupported check type: %s", v.(string)))
    67  	}
    68  
    69  	return warnings, errors
    70  }
    71  
    72  func validateCheckCloudWatchDimmensions(v interface{}, key string) (warnings []string, errors []error) {
    73  	validDimmensionName := regexp.MustCompile(`^[\S]+$`)
    74  	validDimmensionValue := regexp.MustCompile(`^[\S]+$`)
    75  
    76  	dimmensions := v.(map[string]interface{})
    77  	for k, vRaw := range dimmensions {
    78  		if !validDimmensionName.MatchString(k) {
    79  			errors = append(errors, fmt.Errorf("Invalid CloudWatch Dimmension Name specified: %q", k))
    80  			continue
    81  		}
    82  
    83  		v := vRaw.(string)
    84  		if !validDimmensionValue.MatchString(v) {
    85  			errors = append(errors, fmt.Errorf("Invalid value for CloudWatch Dimmension %q specified: %q", k, v))
    86  		}
    87  	}
    88  
    89  	return warnings, errors
    90  }
    91  
    92  func validateContactGroup(cg *api.ContactGroup) error {
    93  	for i := range cg.Reminders {
    94  		if cg.Reminders[i] != 0 && cg.AggregationWindow > cg.Reminders[i] {
    95  			return fmt.Errorf("severity %d reminder (%ds) is shorter than the aggregation window (%ds)", i+1, cg.Reminders[i], cg.AggregationWindow)
    96  		}
    97  	}
    98  
    99  	for severityIndex := range cg.Escalations {
   100  		switch {
   101  		case cg.Escalations[severityIndex] == nil:
   102  			continue
   103  		case cg.Escalations[severityIndex].After > 0 && cg.Escalations[severityIndex].ContactGroupCID == "",
   104  			cg.Escalations[severityIndex].After == 0 && cg.Escalations[severityIndex].ContactGroupCID != "":
   105  			return fmt.Errorf("severity %d escalation requires both and %s and %s be set", severityIndex+1, contactEscalateToAttr, contactEscalateAfterAttr)
   106  		}
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func validateContactGroupCID(attrName schemaAttr) func(v interface{}, key string) (warnings []string, errors []error) {
   113  	return func(v interface{}, key string) (warnings []string, errors []error) {
   114  		validContactGroupCID := regexp.MustCompile(config.ContactGroupCIDRegex)
   115  
   116  		if !validContactGroupCID.MatchString(v.(string)) {
   117  			errors = append(errors, fmt.Errorf("Invalid %s specified (%q)", attrName, v.(string)))
   118  		}
   119  
   120  		return warnings, errors
   121  	}
   122  }
   123  
   124  func validateDurationMin(attrName schemaAttr, minDuration string) func(v interface{}, key string) (warnings []string, errors []error) {
   125  	var min time.Duration
   126  	{
   127  		var err error
   128  		min, err = time.ParseDuration(minDuration)
   129  		if err != nil {
   130  			return func(interface{}, string) (warnings []string, errors []error) {
   131  				errors = []error{errwrap.Wrapf(fmt.Sprintf("Invalid time +%q: {{err}}", minDuration), err)}
   132  				return warnings, errors
   133  			}
   134  		}
   135  	}
   136  
   137  	return func(v interface{}, key string) (warnings []string, errors []error) {
   138  		d, err := time.ParseDuration(v.(string))
   139  		switch {
   140  		case err != nil:
   141  			errors = append(errors, errwrap.Wrapf(fmt.Sprintf("Invalid %s specified (%q): {{err}}", attrName, v.(string)), err))
   142  		case d < min:
   143  			errors = append(errors, fmt.Errorf("Invalid %s specified (%q): minimum value must be %s", attrName, v.(string), min))
   144  		}
   145  
   146  		return warnings, errors
   147  	}
   148  }
   149  
   150  func validateDurationMax(attrName schemaAttr, maxDuration string) func(v interface{}, key string) (warnings []string, errors []error) {
   151  	var max time.Duration
   152  	{
   153  		var err error
   154  		max, err = time.ParseDuration(maxDuration)
   155  		if err != nil {
   156  			return func(interface{}, string) (warnings []string, errors []error) {
   157  				errors = []error{errwrap.Wrapf(fmt.Sprintf("Invalid time +%q: {{err}}", maxDuration), err)}
   158  				return warnings, errors
   159  			}
   160  		}
   161  	}
   162  
   163  	return func(v interface{}, key string) (warnings []string, errors []error) {
   164  		d, err := time.ParseDuration(v.(string))
   165  		switch {
   166  		case err != nil:
   167  			errors = append(errors, errwrap.Wrapf(fmt.Sprintf("Invalid %s specified (%q): {{err}}", attrName, v.(string)), err))
   168  		case d > max:
   169  			errors = append(errors, fmt.Errorf("Invalid %s specified (%q): maximum value must be less than or equal to %s", attrName, v.(string), max))
   170  		}
   171  
   172  		return warnings, errors
   173  	}
   174  }
   175  
   176  func validateFloatMin(attrName schemaAttr, min float64) func(v interface{}, key string) (warnings []string, errors []error) {
   177  	return func(v interface{}, key string) (warnings []string, errors []error) {
   178  		if v.(float64) < min {
   179  			errors = append(errors, fmt.Errorf("Invalid %s specified (%f): minimum value must be %f", attrName, v.(float64), min))
   180  		}
   181  
   182  		return warnings, errors
   183  	}
   184  }
   185  
   186  func validateFloatMax(attrName schemaAttr, max float64) func(v interface{}, key string) (warnings []string, errors []error) {
   187  	return func(v interface{}, key string) (warnings []string, errors []error) {
   188  		if v.(float64) > max {
   189  			errors = append(errors, fmt.Errorf("Invalid %s specified (%f): maximum value must be %f", attrName, v.(float64), max))
   190  		}
   191  
   192  		return warnings, errors
   193  	}
   194  }
   195  
   196  // validateFuncs takes a list of functions and runs them in serial until either
   197  // a warning or error is returned from the first validation function argument.
   198  func validateFuncs(fns ...func(v interface{}, key string) (warnings []string, errors []error)) func(v interface{}, key string) (warnings []string, errors []error) {
   199  	return func(v interface{}, key string) (warnings []string, errors []error) {
   200  		for _, fn := range fns {
   201  			warnings, errors = fn(v, key)
   202  			if len(warnings) > 0 || len(errors) > 0 {
   203  				break
   204  			}
   205  		}
   206  		return warnings, errors
   207  	}
   208  }
   209  
   210  func validateHTTPHeaders(v interface{}, key string) (warnings []string, errors []error) {
   211  	validHTTPHeader := regexp.MustCompile(`.+`)
   212  	validHTTPValue := regexp.MustCompile(`.+`)
   213  
   214  	headers := v.(map[string]interface{})
   215  	for k, vRaw := range headers {
   216  		if !validHTTPHeader.MatchString(k) {
   217  			errors = append(errors, fmt.Errorf("Invalid HTTP Header specified: %q", k))
   218  			continue
   219  		}
   220  
   221  		v := vRaw.(string)
   222  		if !validHTTPValue.MatchString(v) {
   223  			errors = append(errors, fmt.Errorf("Invalid value for HTTP Header %q specified: %q", k, v))
   224  		}
   225  	}
   226  
   227  	return warnings, errors
   228  }
   229  
   230  func validateGraphAxisOptions(v interface{}, key string) (warnings []string, errors []error) {
   231  	axisOptionsMap := v.(map[string]interface{})
   232  	validOpts := map[schemaAttr]struct{}{
   233  		graphAxisLogarithmicAttr: struct{}{},
   234  		graphAxisMaxAttr:         struct{}{},
   235  		graphAxisMinAttr:         struct{}{},
   236  	}
   237  
   238  	for k := range axisOptionsMap {
   239  		if _, ok := validOpts[schemaAttr(k)]; !ok {
   240  			errors = append(errors, fmt.Errorf("Invalid axis option specified: %q", k))
   241  			continue
   242  		}
   243  	}
   244  
   245  	return warnings, errors
   246  }
   247  
   248  func validateIntMin(attrName schemaAttr, min int) func(v interface{}, key string) (warnings []string, errors []error) {
   249  	return func(v interface{}, key string) (warnings []string, errors []error) {
   250  		if v.(int) < min {
   251  			errors = append(errors, fmt.Errorf("Invalid %s specified (%d): minimum value must be %d", attrName, v.(int), min))
   252  		}
   253  
   254  		return warnings, errors
   255  	}
   256  }
   257  
   258  func validateIntMax(attrName schemaAttr, max int) func(v interface{}, key string) (warnings []string, errors []error) {
   259  	return func(v interface{}, key string) (warnings []string, errors []error) {
   260  		if v.(int) > max {
   261  			errors = append(errors, fmt.Errorf("Invalid %s specified (%d): maximum value must be %d", attrName, v.(int), max))
   262  		}
   263  
   264  		return warnings, errors
   265  	}
   266  }
   267  
   268  func validateMetricType(v interface{}, key string) (warnings []string, errors []error) {
   269  	value := v.(string)
   270  	switch value {
   271  	case "caql", "composite", "histogram", "numeric", "text":
   272  	default:
   273  		errors = append(errors, fmt.Errorf("unsupported metric type %s", value))
   274  	}
   275  
   276  	return warnings, errors
   277  }
   278  
   279  func validateRegexp(attrName schemaAttr, reString string) func(v interface{}, key string) (warnings []string, errors []error) {
   280  	re := regexp.MustCompile(reString)
   281  
   282  	return func(v interface{}, key string) (warnings []string, errors []error) {
   283  		if !re.MatchString(v.(string)) {
   284  			errors = append(errors, fmt.Errorf("Invalid %s specified (%q): regexp failed to match string", attrName, v.(string)))
   285  		}
   286  
   287  		return warnings, errors
   288  	}
   289  }
   290  
   291  func validateTag(v interface{}, key string) (warnings []string, errors []error) {
   292  	tag := v.(string)
   293  	if !strings.ContainsRune(tag, ':') {
   294  		errors = append(errors, fmt.Errorf("tag %q is missing a category", tag))
   295  	}
   296  
   297  	return warnings, errors
   298  }
   299  
   300  func validateUserCID(attrName string) func(v interface{}, key string) (warnings []string, errors []error) {
   301  	return func(v interface{}, key string) (warnings []string, errors []error) {
   302  		valid := regexp.MustCompile(config.UserCIDRegex)
   303  
   304  		if !valid.MatchString(v.(string)) {
   305  			errors = append(errors, fmt.Errorf("Invalid %s specified (%q)", attrName, v.(string)))
   306  		}
   307  
   308  		return warnings, errors
   309  	}
   310  }
   311  
   312  type urlParseFlags int
   313  
   314  const (
   315  	urlIsAbs urlParseFlags = 1 << iota
   316  	urlOptional
   317  	urlWithoutPath
   318  	urlWithoutPort
   319  	urlWithoutSchema
   320  )
   321  
   322  const urlBasicCheck urlParseFlags = 0
   323  
   324  func validateHTTPURL(attrName schemaAttr, checkFlags urlParseFlags) func(v interface{}, key string) (warnings []string, errors []error) {
   325  	return func(v interface{}, key string) (warnings []string, errors []error) {
   326  		s := v.(string)
   327  		if checkFlags&urlOptional != 0 && s == "" {
   328  			return warnings, errors
   329  		}
   330  
   331  		u, err := url.Parse(v.(string))
   332  		switch {
   333  		case err != nil:
   334  			errors = append(errors, errwrap.Wrapf(fmt.Sprintf("Invalid %s specified (%q): {{err}}", attrName, v.(string)), err))
   335  		case u.Host == "":
   336  			errors = append(errors, fmt.Errorf("Invalid %s specified: host can not be empty", attrName))
   337  		case !(u.Scheme == "http" || u.Scheme == "https"):
   338  			errors = append(errors, fmt.Errorf("Invalid %s specified: scheme unsupported (only support http and https)", attrName))
   339  		}
   340  
   341  		if checkFlags&urlIsAbs != 0 && !u.IsAbs() {
   342  			errors = append(errors, fmt.Errorf("Schema is missing from URL %q (HINT: https://%s)", v.(string), v.(string)))
   343  		}
   344  
   345  		if checkFlags&urlWithoutSchema != 0 && u.IsAbs() {
   346  			errors = append(errors, fmt.Errorf("Schema is present on URL %q (HINT: drop the https://%s)", v.(string), v.(string)))
   347  		}
   348  
   349  		if checkFlags&urlWithoutPath != 0 && u.Path != "" {
   350  			errors = append(errors, fmt.Errorf("Path is present on URL %q (HINT: drop the %s)", v.(string), u.Path))
   351  		}
   352  
   353  		if checkFlags&urlWithoutPort != 0 {
   354  			hostParts := strings.SplitN(u.Host, ":", 2)
   355  			if len(hostParts) != 1 {
   356  				errors = append(errors, fmt.Errorf("Port is present on URL %q (HINT: drop the :%s)", v.(string), hostParts[1]))
   357  			}
   358  		}
   359  
   360  		return warnings, errors
   361  	}
   362  }
   363  
   364  func validateStringIn(attrName schemaAttr, valid validStringValues) func(v interface{}, key string) (warnings []string, errors []error) {
   365  	return func(v interface{}, key string) (warnings []string, errors []error) {
   366  		s := v.(string)
   367  		var found bool
   368  		for i := range valid {
   369  			if s == string(valid[i]) {
   370  				found = true
   371  				break
   372  			}
   373  		}
   374  
   375  		if !found {
   376  			errors = append(errors, fmt.Errorf("Invalid %q specified: %q not found in list %#v", string(attrName), s, valid))
   377  		}
   378  
   379  		return warnings, errors
   380  	}
   381  }