github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_alb_target_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/aws/awserr"
    12  	"github.com/aws/aws-sdk-go/service/elbv2"
    13  	"github.com/hashicorp/errwrap"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceAwsAlbTargetGroup() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsAlbTargetGroupCreate,
    20  		Read:   resourceAwsAlbTargetGroupRead,
    21  		Update: resourceAwsAlbTargetGroupUpdate,
    22  		Delete: resourceAwsAlbTargetGroupDelete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: schema.ImportStatePassthrough,
    25  		},
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"arn": {
    29  				Type:     schema.TypeString,
    30  				Computed: true,
    31  			},
    32  
    33  			"name": {
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  				ForceNew: true,
    37  			},
    38  
    39  			"port": {
    40  				Type:         schema.TypeInt,
    41  				Required:     true,
    42  				ForceNew:     true,
    43  				ValidateFunc: validateAwsAlbTargetGroupPort,
    44  			},
    45  
    46  			"protocol": {
    47  				Type:         schema.TypeString,
    48  				Required:     true,
    49  				ForceNew:     true,
    50  				ValidateFunc: validateAwsAlbTargetGroupProtocol,
    51  			},
    52  
    53  			"vpc_id": {
    54  				Type:     schema.TypeString,
    55  				Required: true,
    56  				ForceNew: true,
    57  			},
    58  
    59  			"deregistration_delay": {
    60  				Type:         schema.TypeInt,
    61  				Optional:     true,
    62  				Default:      300,
    63  				ValidateFunc: validateAwsAlbTargetGroupDeregistrationDelay,
    64  			},
    65  
    66  			"stickiness": {
    67  				Type:     schema.TypeList,
    68  				Optional: true,
    69  				MaxItems: 1,
    70  				Elem: &schema.Resource{
    71  					Schema: map[string]*schema.Schema{
    72  						"type": {
    73  							Type:         schema.TypeString,
    74  							Required:     true,
    75  							ValidateFunc: validateAwsAlbTargetGroupStickinessType,
    76  						},
    77  						"cookie_duration": {
    78  							Type:         schema.TypeInt,
    79  							Optional:     true,
    80  							Default:      86400,
    81  							ValidateFunc: validateAwsAlbTargetGroupStickinessCookieDuration,
    82  						},
    83  					},
    84  				},
    85  			},
    86  
    87  			"health_check": {
    88  				Type:     schema.TypeList,
    89  				Optional: true,
    90  				Computed: true,
    91  				MaxItems: 1,
    92  				Elem: &schema.Resource{
    93  					Schema: map[string]*schema.Schema{
    94  						"interval": {
    95  							Type:     schema.TypeInt,
    96  							Optional: true,
    97  							Default:  30,
    98  						},
    99  
   100  						"path": {
   101  							Type:         schema.TypeString,
   102  							Optional:     true,
   103  							Default:      "/",
   104  							ValidateFunc: validateAwsAlbTargetGroupHealthCheckPath,
   105  						},
   106  
   107  						"port": {
   108  							Type:         schema.TypeString,
   109  							Optional:     true,
   110  							Default:      "traffic-port",
   111  							ValidateFunc: validateAwsAlbTargetGroupHealthCheckPort,
   112  						},
   113  
   114  						"protocol": {
   115  							Type:     schema.TypeString,
   116  							Optional: true,
   117  							Default:  "HTTP",
   118  							StateFunc: func(v interface{}) string {
   119  								return strings.ToUpper(v.(string))
   120  							},
   121  							ValidateFunc: validateAwsAlbTargetGroupHealthCheckProtocol,
   122  						},
   123  
   124  						"timeout": {
   125  							Type:         schema.TypeInt,
   126  							Optional:     true,
   127  							Default:      5,
   128  							ValidateFunc: validateAwsAlbTargetGroupHealthCheckTimeout,
   129  						},
   130  
   131  						"healthy_threshold": {
   132  							Type:         schema.TypeInt,
   133  							Optional:     true,
   134  							Default:      5,
   135  							ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold,
   136  						},
   137  
   138  						"matcher": {
   139  							Type:     schema.TypeString,
   140  							Optional: true,
   141  							Default:  "200",
   142  						},
   143  
   144  						"unhealthy_threshold": {
   145  							Type:         schema.TypeInt,
   146  							Optional:     true,
   147  							Default:      2,
   148  							ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold,
   149  						},
   150  					},
   151  				},
   152  			},
   153  
   154  			"tags": tagsSchema(),
   155  		},
   156  	}
   157  }
   158  
   159  func resourceAwsAlbTargetGroupCreate(d *schema.ResourceData, meta interface{}) error {
   160  	elbconn := meta.(*AWSClient).elbv2conn
   161  
   162  	params := &elbv2.CreateTargetGroupInput{
   163  		Name:     aws.String(d.Get("name").(string)),
   164  		Port:     aws.Int64(int64(d.Get("port").(int))),
   165  		Protocol: aws.String(d.Get("protocol").(string)),
   166  		VpcId:    aws.String(d.Get("vpc_id").(string)),
   167  	}
   168  
   169  	if healthChecks := d.Get("health_check").([]interface{}); len(healthChecks) == 1 {
   170  		healthCheck := healthChecks[0].(map[string]interface{})
   171  
   172  		params.HealthCheckIntervalSeconds = aws.Int64(int64(healthCheck["interval"].(int)))
   173  		params.HealthCheckPath = aws.String(healthCheck["path"].(string))
   174  		params.HealthCheckPort = aws.String(healthCheck["port"].(string))
   175  		params.HealthCheckProtocol = aws.String(healthCheck["protocol"].(string))
   176  		params.HealthCheckTimeoutSeconds = aws.Int64(int64(healthCheck["timeout"].(int)))
   177  		params.HealthyThresholdCount = aws.Int64(int64(healthCheck["healthy_threshold"].(int)))
   178  		params.UnhealthyThresholdCount = aws.Int64(int64(healthCheck["unhealthy_threshold"].(int)))
   179  		params.Matcher = &elbv2.Matcher{
   180  			HttpCode: aws.String(healthCheck["matcher"].(string)),
   181  		}
   182  	}
   183  
   184  	resp, err := elbconn.CreateTargetGroup(params)
   185  	if err != nil {
   186  		return errwrap.Wrapf("Error creating ALB Target Group: {{err}}", err)
   187  	}
   188  
   189  	if len(resp.TargetGroups) == 0 {
   190  		return errors.New("Error creating ALB Target Group: no groups returned in response")
   191  	}
   192  
   193  	targetGroupArn := resp.TargetGroups[0].TargetGroupArn
   194  	d.SetId(*targetGroupArn)
   195  
   196  	return resourceAwsAlbTargetGroupUpdate(d, meta)
   197  }
   198  
   199  func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) error {
   200  	elbconn := meta.(*AWSClient).elbv2conn
   201  
   202  	resp, err := elbconn.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{
   203  		TargetGroupArns: []*string{aws.String(d.Id())},
   204  	})
   205  	if err != nil {
   206  		if isTargetGroupNotFound(err) {
   207  			log.Printf("[DEBUG] DescribeTargetGroups - removing %s from state", d.Id())
   208  			d.SetId("")
   209  			return nil
   210  		}
   211  		return errwrap.Wrapf("Error retrieving Target Group: {{err}}", err)
   212  	}
   213  
   214  	if len(resp.TargetGroups) != 1 {
   215  		return fmt.Errorf("Error retrieving Target Group %q", d.Id())
   216  	}
   217  
   218  	targetGroup := resp.TargetGroups[0]
   219  
   220  	d.Set("arn", targetGroup.TargetGroupArn)
   221  	d.Set("name", targetGroup.TargetGroupName)
   222  	d.Set("port", targetGroup.Port)
   223  	d.Set("protocol", targetGroup.Protocol)
   224  	d.Set("vpc_id", targetGroup.VpcId)
   225  
   226  	healthCheck := make(map[string]interface{})
   227  	healthCheck["interval"] = *targetGroup.HealthCheckIntervalSeconds
   228  	healthCheck["path"] = *targetGroup.HealthCheckPath
   229  	healthCheck["port"] = *targetGroup.HealthCheckPort
   230  	healthCheck["protocol"] = *targetGroup.HealthCheckProtocol
   231  	healthCheck["timeout"] = *targetGroup.HealthCheckTimeoutSeconds
   232  	healthCheck["healthy_threshold"] = *targetGroup.HealthyThresholdCount
   233  	healthCheck["unhealthy_threshold"] = *targetGroup.UnhealthyThresholdCount
   234  	healthCheck["matcher"] = *targetGroup.Matcher.HttpCode
   235  	d.Set("health_check", []interface{}{healthCheck})
   236  
   237  	attrResp, err := elbconn.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{
   238  		TargetGroupArn: aws.String(d.Id()),
   239  	})
   240  	if err != nil {
   241  		return errwrap.Wrapf("Error retrieving Target Group Attributes: {{err}}", err)
   242  	}
   243  
   244  	stickinessMap := map[string]interface{}{}
   245  	for _, attr := range attrResp.Attributes {
   246  		switch *attr.Key {
   247  		case "stickiness.type":
   248  			stickinessMap["type"] = *attr.Value
   249  		case "stickiness.lb_cookie.duration_seconds":
   250  			stickinessMap["cookie_duration"] = *attr.Value
   251  		case "deregistration_delay.timeout_seconds":
   252  			timeout, err := strconv.Atoi(*attr.Value)
   253  			if err != nil {
   254  				return fmt.Errorf("Error converting deregistration_delay.timeout_seconds to int: %s", *attr.Value)
   255  			}
   256  			d.Set("deregistration_delay", timeout)
   257  		}
   258  	}
   259  	d.Set("stickiness", []interface{}{stickinessMap})
   260  
   261  	return nil
   262  }
   263  
   264  func resourceAwsAlbTargetGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   265  	elbconn := meta.(*AWSClient).elbv2conn
   266  
   267  	if err := setElbV2Tags(elbconn, d); err != nil {
   268  		return errwrap.Wrapf("Error Modifying Tags on ALB Target Group: {{err}}", err)
   269  	}
   270  
   271  	if d.HasChange("health_check") {
   272  		healthChecks := d.Get("health_check").([]interface{})
   273  
   274  		var params *elbv2.ModifyTargetGroupInput
   275  		if len(healthChecks) == 1 {
   276  			healthCheck := healthChecks[0].(map[string]interface{})
   277  
   278  			params = &elbv2.ModifyTargetGroupInput{
   279  				TargetGroupArn:             aws.String(d.Id()),
   280  				HealthCheckIntervalSeconds: aws.Int64(int64(healthCheck["interval"].(int))),
   281  				HealthCheckPath:            aws.String(healthCheck["path"].(string)),
   282  				HealthCheckPort:            aws.String(healthCheck["port"].(string)),
   283  				HealthCheckProtocol:        aws.String(healthCheck["protocol"].(string)),
   284  				HealthCheckTimeoutSeconds:  aws.Int64(int64(healthCheck["timeout"].(int))),
   285  				HealthyThresholdCount:      aws.Int64(int64(healthCheck["healthy_threshold"].(int))),
   286  				UnhealthyThresholdCount:    aws.Int64(int64(healthCheck["unhealthy_threshold"].(int))),
   287  				Matcher: &elbv2.Matcher{
   288  					HttpCode: aws.String(healthCheck["matcher"].(string)),
   289  				},
   290  			}
   291  		} else {
   292  			params = &elbv2.ModifyTargetGroupInput{
   293  				TargetGroupArn: aws.String(d.Id()),
   294  			}
   295  		}
   296  
   297  		_, err := elbconn.ModifyTargetGroup(params)
   298  		if err != nil {
   299  			return errwrap.Wrapf("Error modifying Target Group: {{err}}", err)
   300  		}
   301  	}
   302  
   303  	var attrs []*elbv2.TargetGroupAttribute
   304  
   305  	if d.HasChange("deregistration_delay") {
   306  		attrs = append(attrs, &elbv2.TargetGroupAttribute{
   307  			Key:   aws.String("deregistration_delay.timeout_seconds"),
   308  			Value: aws.String(fmt.Sprintf("%d", d.Get("deregistration_delay").(int))),
   309  		})
   310  	}
   311  
   312  	if d.HasChange("stickiness") {
   313  		stickinessBlocks := d.Get("stickiness").([]interface{})
   314  		if len(stickinessBlocks) == 1 {
   315  			stickiness := stickinessBlocks[0].(map[string]interface{})
   316  
   317  			attrs = append(attrs,
   318  				&elbv2.TargetGroupAttribute{
   319  					Key:   aws.String("stickiness.enabled"),
   320  					Value: aws.String("true"),
   321  				},
   322  				&elbv2.TargetGroupAttribute{
   323  					Key:   aws.String("stickiness.type"),
   324  					Value: aws.String(stickiness["type"].(string)),
   325  				},
   326  				&elbv2.TargetGroupAttribute{
   327  					Key:   aws.String("stickiness.lb_cookie.duration_seconds"),
   328  					Value: aws.String(fmt.Sprintf("%d", stickiness["cookie_duration"].(int))),
   329  				})
   330  		} else if len(stickinessBlocks) == 0 {
   331  			attrs = append(attrs, &elbv2.TargetGroupAttribute{
   332  				Key:   aws.String("stickiness.enabled"),
   333  				Value: aws.String("false"),
   334  			})
   335  		}
   336  	}
   337  
   338  	if len(attrs) > 0 {
   339  		params := &elbv2.ModifyTargetGroupAttributesInput{
   340  			TargetGroupArn: aws.String(d.Id()),
   341  			Attributes:     attrs,
   342  		}
   343  
   344  		_, err := elbconn.ModifyTargetGroupAttributes(params)
   345  		if err != nil {
   346  			return errwrap.Wrapf("Error modifying Target Group Attributes: {{err}}", err)
   347  		}
   348  	}
   349  
   350  	return resourceAwsAlbTargetGroupRead(d, meta)
   351  }
   352  
   353  func resourceAwsAlbTargetGroupDelete(d *schema.ResourceData, meta interface{}) error {
   354  	elbconn := meta.(*AWSClient).elbv2conn
   355  
   356  	_, err := elbconn.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{
   357  		TargetGroupArn: aws.String(d.Id()),
   358  	})
   359  	if err != nil {
   360  		return errwrap.Wrapf("Error deleting Target Group: {{err}}", err)
   361  	}
   362  
   363  	return nil
   364  }
   365  
   366  func isTargetGroupNotFound(err error) bool {
   367  	elberr, ok := err.(awserr.Error)
   368  	return ok && elberr.Code() == "TargetGroupNotFound"
   369  }
   370  
   371  func validateAwsAlbTargetGroupHealthCheckPath(v interface{}, k string) (ws []string, errors []error) {
   372  	value := v.(string)
   373  	if len(value) > 1024 {
   374  		errors = append(errors, fmt.Errorf(
   375  			"%q cannot be longer than 1024 characters: %q", k, value))
   376  	}
   377  	return
   378  }
   379  
   380  func validateAwsAlbTargetGroupHealthCheckPort(v interface{}, k string) (ws []string, errors []error) {
   381  	value := v.(string)
   382  
   383  	if value == "traffic-port" {
   384  		return
   385  	}
   386  
   387  	port, err := strconv.Atoi(value)
   388  	if err != nil {
   389  		errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port"))
   390  	}
   391  
   392  	if port < 1 || port > 65536 {
   393  		errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port"))
   394  	}
   395  
   396  	return
   397  }
   398  
   399  func validateAwsAlbTargetGroupHealthCheckHealthyThreshold(v interface{}, k string) (ws []string, errors []error) {
   400  	value := v.(int)
   401  	if value < 2 || value > 10 {
   402  		errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 10", k))
   403  	}
   404  	return
   405  }
   406  
   407  func validateAwsAlbTargetGroupHealthCheckTimeout(v interface{}, k string) (ws []string, errors []error) {
   408  	value := v.(int)
   409  	if value < 2 || value > 60 {
   410  		errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 60", k))
   411  	}
   412  	return
   413  }
   414  
   415  func validateAwsAlbTargetGroupHealthCheckProtocol(v interface{}, k string) (ws []string, errors []error) {
   416  	value := strings.ToLower(v.(string))
   417  	if value == "http" || value == "https" {
   418  		return
   419  	}
   420  
   421  	errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS"))
   422  	return
   423  }
   424  
   425  func validateAwsAlbTargetGroupPort(v interface{}, k string) (ws []string, errors []error) {
   426  	port := v.(int)
   427  	if port < 1 || port > 65536 {
   428  		errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536)", k))
   429  	}
   430  	return
   431  }
   432  
   433  func validateAwsAlbTargetGroupProtocol(v interface{}, k string) (ws []string, errors []error) {
   434  	protocol := strings.ToLower(v.(string))
   435  	if protocol == "http" || protocol == "https" {
   436  		return
   437  	}
   438  
   439  	errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS"))
   440  	return
   441  }
   442  
   443  func validateAwsAlbTargetGroupDeregistrationDelay(v interface{}, k string) (ws []string, errors []error) {
   444  	delay := v.(int)
   445  	if delay < 0 || delay > 3600 {
   446  		errors = append(errors, fmt.Errorf("%q must be in the range 0-3600 seconds", k))
   447  	}
   448  	return
   449  }
   450  
   451  func validateAwsAlbTargetGroupStickinessType(v interface{}, k string) (ws []string, errors []error) {
   452  	stickinessType := v.(string)
   453  	if stickinessType != "lb_cookie" {
   454  		errors = append(errors, fmt.Errorf("%q must have the value %q", k, "lb_cookie"))
   455  	}
   456  	return
   457  }
   458  
   459  func validateAwsAlbTargetGroupStickinessCookieDuration(v interface{}, k string) (ws []string, errors []error) {
   460  	duration := v.(int)
   461  	if duration < 1 || duration > 604800 {
   462  		errors = append(errors, fmt.Errorf("%q must be a between 1 second and 1 week (1-604800 seconds))", k))
   463  	}
   464  	return
   465  }