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