Skip to content

Commit

Permalink
Merge pull request #8578 from kwilczynski/feature/health-check-target…
Browse files Browse the repository at this point in the history
…-validation-aws_elb

provider/aws: Add validation of Health Check target to aws_elb.
  • Loading branch information
stack72 authored Sep 3, 2016
2 parents 64c8aef + bcaac02 commit 9ad4e84
Show file tree
Hide file tree
Showing 2 changed files with 323 additions and 24 deletions.
170 changes: 146 additions & 24 deletions builtin/providers/aws/resource_aws_elb.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"bytes"
"fmt"
"log"
"regexp"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -92,9 +94,10 @@ func resourceAwsElb() *schema.Resource {
},

"idle_timeout": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 60,
Type: schema.TypeInt,
Optional: true,
Default: 60,
ValidateFunc: validateIntegerInRange(1, 3600),
},

"connection_draining": &schema.Schema{
Expand All @@ -115,9 +118,10 @@ func resourceAwsElb() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"interval": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 60,
Type: schema.TypeInt,
Optional: true,
Default: 60,
ValidateFunc: validateAccessLogsInterval,
},
"bucket": &schema.Schema{
Type: schema.TypeString,
Expand All @@ -142,23 +146,27 @@ func resourceAwsElb() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"instance_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validateIntegerInRange(1, 65535),
},

"instance_protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validateListenerProtocol,
},

"lb_port": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validateIntegerInRange(1, 65535),
},

"lb_protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validateListenerProtocol,
},

"ssl_certificate_id": &schema.Schema{
Expand All @@ -178,28 +186,33 @@ func resourceAwsElb() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"healthy_threshold": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validateIntegerInRange(2, 10),
},

"unhealthy_threshold": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validateIntegerInRange(2, 10),
},

"target": &schema.Schema{
Type: schema.TypeString,
Required: true,
Type: schema.TypeString,
Required: true,
ValidateFunc: validateHeathCheckTarget,
},

"interval": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validateIntegerInRange(5, 300),
},

"timeout": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Required: true,
ValidateFunc: validateIntegerInRange(2, 60),
},
},
},
Expand Down Expand Up @@ -807,3 +820,112 @@ func sourceSGIdByName(meta interface{}, sg, vpcId string) (string, error) {
group := resp.SecurityGroups[0]
return *group.GroupId, nil
}

func validateAccessLogsInterval(v interface{}, k string) (ws []string, errors []error) {
value := v.(int)

// Check if the value is either 5 or 60 (minutes).
if value != 5 && value != 60 {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Access Logs interval \"%d\". "+
"Valid intervals are either 5 or 60 (minutes).",
k, value))
}
return
}

func validateHeathCheckTarget(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)

// Parse the Health Check target value.
matches := regexp.MustCompile(`\A(\w+):(\d+)(.+)?\z`).FindStringSubmatch(value)

// Check if the value contains a valid target.
if matches == nil || len(matches) < 1 {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Health Check: %s",
k, value))

// Invalid target? Return immediately,
// there is no need to collect other
// errors.
return
}

// Check if the value contains a valid protocol.
if !isValidProtocol(matches[1]) {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Health Check protocol %q. "+
"Valid protocols are either %q, %q, %q, or %q.",
k, matches[1], "TCP", "SSL", "HTTP", "HTTPS"))
}

// Check if the value contains a valid port range.
port, _ := strconv.Atoi(matches[2])
if port < 1 || port > 65535 {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Health Check target port \"%d\". "+
"Valid port is in the range from 1 to 65535 inclusive.",
k, port))
}

switch strings.ToLower(matches[1]) {
case "tcp", "ssl":
// Check if value is in the form <PROTOCOL>:<PORT> for TCP and/or SSL.
if matches[3] != "" {
errors = append(errors, fmt.Errorf(
"%q cannot contain a path in the Health Check target: %s",
k, value))
}
break
case "http", "https":
// Check if value is in the form <PROTOCOL>:<PORT>/<PATH> for HTTP and/or HTTPS.
if matches[3] == "" {
errors = append(errors, fmt.Errorf(
"%q must contain a path in the Health Check target: %s",
k, value))
}

// Cannot be longer than 1024 multibyte characters.
if len([]rune(matches[3])) > 1024 {
errors = append(errors, fmt.Errorf("%q cannot contain a path longer "+
"than 1024 characters in the Health Check target: %s",
k, value))
}
break
}

return
}

func validateListenerProtocol(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)

if !isValidProtocol(value) {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Listener protocol %q. "+
"Valid protocols are either %q, %q, %q, or %q.",
k, value, "TCP", "SSL", "HTTP", "HTTPS"))
}
return
}

func isValidProtocol(s string) bool {
if s == "" {
return false
}
s = strings.ToLower(s)

validProtocols := map[string]bool{
"http": true,
"https": true,
"ssl": true,
"tcp": true,
}

if _, ok := validProtocols[s]; !ok {
return false
}

return true
}
Loading

0 comments on commit 9ad4e84

Please sign in to comment.