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

     1  package circonus
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     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.tcp.* resource attribute names
    18  	checkTCPBannerRegexpAttr = "banner_regexp"
    19  	checkTCPCAChainAttr      = "ca_chain"
    20  	checkTCPCertFileAttr     = "certificate_file"
    21  	checkTCPCiphersAttr      = "ciphers"
    22  	checkTCPHostAttr         = "host"
    23  	checkTCPKeyFileAttr      = "key_file"
    24  	checkTCPPortAttr         = "port"
    25  	checkTCPTLSAttr          = "tls"
    26  )
    27  
    28  var checkTCPDescriptions = attrDescrs{
    29  	checkTCPBannerRegexpAttr: `This regular expression is matched against the response banner. If a match is not found, the check will be marked as bad.`,
    30  	checkTCPCAChainAttr:      "A path to a file containing all the certificate authorities that should be loaded to validate the remote certificate (for TLS checks).",
    31  	checkTCPCertFileAttr:     "A path to a file containing the client certificate that will be presented to the remote server (for TLS checks).",
    32  	checkTCPCiphersAttr:      "A list of ciphers to be used when establishing a TLS connection",
    33  	checkTCPHostAttr:         "Specifies the host name or IP address to connect to for this TCP check",
    34  	checkTCPKeyFileAttr:      "A path to a file containing key to be used in conjunction with the cilent certificate (for TLS checks)",
    35  	checkTCPPortAttr:         "Specifies the port on which the management interface can be reached.",
    36  	checkTCPTLSAttr:          "Upgrade TCP connection to use TLS.",
    37  }
    38  
    39  var schemaCheckTCP = &schema.Schema{
    40  	Type:     schema.TypeSet,
    41  	Optional: true,
    42  	MaxItems: 1,
    43  	MinItems: 1,
    44  	Set:      hashCheckTCP,
    45  	Elem: &schema.Resource{
    46  		Schema: convertToHelperSchema(checkTCPDescriptions, map[schemaAttr]*schema.Schema{
    47  			checkTCPBannerRegexpAttr: &schema.Schema{
    48  				Type:         schema.TypeString,
    49  				Optional:     true,
    50  				ValidateFunc: validateRegexp(checkTCPBannerRegexpAttr, `.+`),
    51  			},
    52  			checkTCPCAChainAttr: &schema.Schema{
    53  				Type:         schema.TypeString,
    54  				Optional:     true,
    55  				ValidateFunc: validateRegexp(checkTCPCAChainAttr, `.+`),
    56  			},
    57  			checkTCPCertFileAttr: &schema.Schema{
    58  				Type:         schema.TypeString,
    59  				Optional:     true,
    60  				ValidateFunc: validateRegexp(checkTCPCertFileAttr, `.+`),
    61  			},
    62  			checkTCPCiphersAttr: &schema.Schema{
    63  				Type:         schema.TypeString,
    64  				Optional:     true,
    65  				ValidateFunc: validateRegexp(checkTCPCiphersAttr, `.+`),
    66  			},
    67  			checkTCPHostAttr: &schema.Schema{
    68  				Type:         schema.TypeString,
    69  				Required:     true,
    70  				ValidateFunc: validateRegexp(checkTCPHostAttr, `.+`),
    71  			},
    72  			checkTCPKeyFileAttr: &schema.Schema{
    73  				Type:         schema.TypeString,
    74  				Optional:     true,
    75  				ValidateFunc: validateRegexp(checkTCPKeyFileAttr, `.+`),
    76  			},
    77  			checkTCPPortAttr: &schema.Schema{
    78  				Type:     schema.TypeInt,
    79  				Required: true,
    80  				ValidateFunc: validateFuncs(
    81  					validateIntMin(checkTCPPortAttr, 0),
    82  					validateIntMax(checkTCPPortAttr, 65535),
    83  				),
    84  			},
    85  			checkTCPTLSAttr: &schema.Schema{
    86  				Type:     schema.TypeBool,
    87  				Optional: true,
    88  				Default:  false,
    89  			},
    90  		}),
    91  	},
    92  }
    93  
    94  // checkAPIToStateTCP reads the Config data out of circonusCheck.CheckBundle into the
    95  // statefile.
    96  func checkAPIToStateTCP(c *circonusCheck, d *schema.ResourceData) error {
    97  	tcpConfig := make(map[string]interface{}, len(c.Config))
    98  
    99  	// swamp is a sanity check: it must be empty by the time this method returns
   100  	swamp := make(map[config.Key]string, len(c.Config))
   101  	for k, v := range c.Config {
   102  		swamp[k] = v
   103  	}
   104  
   105  	saveBoolConfigToState := func(apiKey config.Key, attrName schemaAttr) {
   106  		if s, ok := c.Config[apiKey]; ok {
   107  			switch strings.ToLower(s) {
   108  			case "1", "true", "t", "yes", "y":
   109  				tcpConfig[string(attrName)] = true
   110  			case "0", "false", "f", "no", "n":
   111  				tcpConfig[string(attrName)] = false
   112  			default:
   113  				log.Printf("PROVIDER BUG: unsupported boolean: %q for API Config Key %q", s, string(apiKey))
   114  				return
   115  			}
   116  		}
   117  
   118  		delete(swamp, apiKey)
   119  	}
   120  
   121  	saveIntConfigToState := func(apiKey config.Key, attrName schemaAttr) {
   122  		if v, ok := c.Config[apiKey]; ok {
   123  			i, err := strconv.ParseInt(v, 10, 64)
   124  			if err != nil {
   125  				log.Printf("[ERROR]: Unable to convert %s to an integer: %v", apiKey, err)
   126  				return
   127  			}
   128  			tcpConfig[string(attrName)] = int(i)
   129  		}
   130  
   131  		delete(swamp, apiKey)
   132  	}
   133  
   134  	saveStringConfigToState := func(apiKey config.Key, attrName schemaAttr) {
   135  		if v, ok := c.Config[apiKey]; ok {
   136  			tcpConfig[string(attrName)] = v
   137  		}
   138  
   139  		delete(swamp, apiKey)
   140  	}
   141  
   142  	saveStringConfigToState(config.BannerMatch, checkTCPBannerRegexpAttr)
   143  	saveStringConfigToState(config.CAChain, checkTCPCAChainAttr)
   144  	saveStringConfigToState(config.CertFile, checkTCPCertFileAttr)
   145  	saveStringConfigToState(config.Ciphers, checkTCPCiphersAttr)
   146  	tcpConfig[string(checkTCPHostAttr)] = c.Target
   147  	saveStringConfigToState(config.KeyFile, checkTCPKeyFileAttr)
   148  	saveIntConfigToState(config.Port, checkTCPPortAttr)
   149  	saveBoolConfigToState(config.UseSSL, checkTCPTLSAttr)
   150  
   151  	whitelistedConfigKeys := map[config.Key]struct{}{
   152  		config.ReverseSecretKey: struct{}{},
   153  		config.SubmissionURL:    struct{}{},
   154  	}
   155  
   156  	for k := range swamp {
   157  		if _, ok := whitelistedConfigKeys[k]; ok {
   158  			delete(c.Config, k)
   159  		}
   160  
   161  		if _, ok := whitelistedConfigKeys[k]; !ok {
   162  			log.Printf("[ERROR]: PROVIDER BUG: API Config not empty: %#v", swamp)
   163  		}
   164  	}
   165  
   166  	if err := d.Set(checkTCPAttr, schema.NewSet(hashCheckTCP, []interface{}{tcpConfig})); err != nil {
   167  		return errwrap.Wrapf(fmt.Sprintf("Unable to store check %q attribute: {{err}}", checkTCPAttr), err)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  // hashCheckTCP creates a stable hash of the normalized values
   174  func hashCheckTCP(v interface{}) int {
   175  	m := v.(map[string]interface{})
   176  	b := &bytes.Buffer{}
   177  	b.Grow(defaultHashBufSize)
   178  
   179  	writeBool := func(attrName schemaAttr) {
   180  		if v, ok := m[string(attrName)]; ok {
   181  			fmt.Fprintf(b, "%t", v.(bool))
   182  		}
   183  	}
   184  
   185  	writeInt := func(attrName schemaAttr) {
   186  		if v, ok := m[string(attrName)]; ok {
   187  			fmt.Fprintf(b, "%x", v.(int))
   188  		}
   189  	}
   190  
   191  	writeString := func(attrName schemaAttr) {
   192  		if v, ok := m[string(attrName)]; ok && v.(string) != "" {
   193  			fmt.Fprint(b, strings.TrimSpace(v.(string)))
   194  		}
   195  	}
   196  
   197  	// Order writes to the buffer using lexically sorted list for easy visual
   198  	// reconciliation with other lists.
   199  	writeString(checkTCPBannerRegexpAttr)
   200  	writeString(checkTCPCAChainAttr)
   201  	writeString(checkTCPCertFileAttr)
   202  	writeString(checkTCPCiphersAttr)
   203  	writeString(checkTCPHostAttr)
   204  	writeString(checkTCPKeyFileAttr)
   205  	writeInt(checkTCPPortAttr)
   206  	writeBool(checkTCPTLSAttr)
   207  
   208  	s := b.String()
   209  	return hashcode.String(s)
   210  }
   211  
   212  func checkConfigToAPITCP(c *circonusCheck, l interfaceList) error {
   213  	c.Type = string(apiCheckTypeTCP)
   214  
   215  	// Iterate over all `tcp` attributes, even though we have a max of 1 in the
   216  	// schema.
   217  	for _, mapRaw := range l {
   218  		tcpConfig := newInterfaceMap(mapRaw)
   219  
   220  		if v, found := tcpConfig[checkTCPBannerRegexpAttr]; found {
   221  			c.Config[config.BannerMatch] = v.(string)
   222  		}
   223  
   224  		if v, found := tcpConfig[checkTCPCAChainAttr]; found {
   225  			c.Config[config.CAChain] = v.(string)
   226  		}
   227  
   228  		if v, found := tcpConfig[checkTCPCertFileAttr]; found {
   229  			c.Config[config.CertFile] = v.(string)
   230  		}
   231  
   232  		if v, found := tcpConfig[checkTCPCiphersAttr]; found {
   233  			c.Config[config.Ciphers] = v.(string)
   234  		}
   235  
   236  		if v, found := tcpConfig[checkTCPHostAttr]; found {
   237  			c.Target = v.(string)
   238  		}
   239  
   240  		if v, found := tcpConfig[checkTCPKeyFileAttr]; found {
   241  			c.Config[config.KeyFile] = v.(string)
   242  		}
   243  
   244  		if v, found := tcpConfig[checkTCPPortAttr]; found {
   245  			c.Config[config.Port] = fmt.Sprintf("%d", v.(int))
   246  		}
   247  
   248  		if v, found := tcpConfig[checkTCPTLSAttr]; found {
   249  			c.Config[config.UseSSL] = fmt.Sprintf("%t", v.(bool))
   250  		}
   251  	}
   252  
   253  	return nil
   254  }