github.com/erriapo/terraform@v0.6.12-0.20160203182612-0340ea72354f/builtin/providers/aws/resource_aws_redshift_security_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"regexp"
     8  	"time"
     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/redshift"
    13  	"github.com/hashicorp/go-multierror"
    14  	"github.com/hashicorp/terraform/helper/hashcode"
    15  	"github.com/hashicorp/terraform/helper/resource"
    16  	"github.com/hashicorp/terraform/helper/schema"
    17  )
    18  
    19  func resourceAwsRedshiftSecurityGroup() *schema.Resource {
    20  	return &schema.Resource{
    21  		Create: resourceAwsRedshiftSecurityGroupCreate,
    22  		Read:   resourceAwsRedshiftSecurityGroupRead,
    23  		Delete: resourceAwsRedshiftSecurityGroupDelete,
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"name": &schema.Schema{
    27  				Type:         schema.TypeString,
    28  				Required:     true,
    29  				ForceNew:     true,
    30  				ValidateFunc: validateRedshiftSecurityGroupName,
    31  			},
    32  
    33  			"description": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  				ForceNew: true,
    37  			},
    38  
    39  			"ingress": &schema.Schema{
    40  				Type:     schema.TypeSet,
    41  				Required: true,
    42  				ForceNew: true,
    43  				Elem: &schema.Resource{
    44  					Schema: map[string]*schema.Schema{
    45  						"cidr": &schema.Schema{
    46  							Type:     schema.TypeString,
    47  							Optional: true,
    48  						},
    49  
    50  						"security_group_name": &schema.Schema{
    51  							Type:     schema.TypeString,
    52  							Optional: true,
    53  							Computed: true,
    54  						},
    55  
    56  						"security_group_owner_id": &schema.Schema{
    57  							Type:     schema.TypeString,
    58  							Optional: true,
    59  							Computed: true,
    60  						},
    61  					},
    62  				},
    63  				Set: resourceAwsRedshiftSecurityGroupIngressHash,
    64  			},
    65  		},
    66  	}
    67  }
    68  
    69  func resourceAwsRedshiftSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error {
    70  	conn := meta.(*AWSClient).redshiftconn
    71  
    72  	var err error
    73  	var errs []error
    74  
    75  	name := d.Get("name").(string)
    76  	desc := d.Get("description").(string)
    77  	sgInput := &redshift.CreateClusterSecurityGroupInput{
    78  		ClusterSecurityGroupName: aws.String(name),
    79  		Description:              aws.String(desc),
    80  	}
    81  	log.Printf("[DEBUG] Redshift security group create: name: %s, description: %s", name, desc)
    82  	_, err = conn.CreateClusterSecurityGroup(sgInput)
    83  	if err != nil {
    84  		return fmt.Errorf("Error creating RedshiftSecurityGroup: %s", err)
    85  	}
    86  
    87  	d.SetId(d.Get("name").(string))
    88  
    89  	log.Printf("[INFO] Redshift Security Group ID: %s", d.Id())
    90  	sg, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	ingresses := d.Get("ingress").(*schema.Set)
    96  	for _, ing := range ingresses.List() {
    97  		err := resourceAwsRedshiftSecurityGroupAuthorizeRule(ing, *sg.ClusterSecurityGroupName, conn)
    98  		if err != nil {
    99  			errs = append(errs, err)
   100  		}
   101  	}
   102  
   103  	if len(errs) > 0 {
   104  		return &multierror.Error{Errors: errs}
   105  	}
   106  
   107  	log.Println("[INFO] Waiting for Redshift Security Group Ingress Authorizations to be authorized")
   108  	stateConf := &resource.StateChangeConf{
   109  		Pending: []string{"authorizing"},
   110  		Target:  []string{"authorized"},
   111  		Refresh: resourceAwsRedshiftSecurityGroupStateRefreshFunc(d, meta),
   112  		Timeout: 10 * time.Minute,
   113  	}
   114  
   115  	_, err = stateConf.WaitForState()
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	return resourceAwsRedshiftSecurityGroupRead(d, meta)
   121  }
   122  
   123  func resourceAwsRedshiftSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
   124  	sg, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	rules := &schema.Set{
   130  		F: resourceAwsRedshiftSecurityGroupIngressHash,
   131  	}
   132  
   133  	for _, v := range sg.IPRanges {
   134  		rule := map[string]interface{}{"cidr": *v.CIDRIP}
   135  		rules.Add(rule)
   136  	}
   137  
   138  	for _, g := range sg.EC2SecurityGroups {
   139  		rule := map[string]interface{}{
   140  			"security_group_name":     *g.EC2SecurityGroupName,
   141  			"security_group_owner_id": *g.EC2SecurityGroupOwnerId,
   142  		}
   143  		rules.Add(rule)
   144  	}
   145  
   146  	d.Set("ingress", rules)
   147  	d.Set("name", *sg.ClusterSecurityGroupName)
   148  	d.Set("description", *sg.Description)
   149  
   150  	return nil
   151  }
   152  
   153  func resourceAwsRedshiftSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
   154  	conn := meta.(*AWSClient).redshiftconn
   155  
   156  	log.Printf("[DEBUG] Redshift Security Group destroy: %v", d.Id())
   157  	opts := redshift.DeleteClusterSecurityGroupInput{
   158  		ClusterSecurityGroupName: aws.String(d.Id()),
   159  	}
   160  
   161  	log.Printf("[DEBUG] Redshift Security Group destroy configuration: %v", opts)
   162  	_, err := conn.DeleteClusterSecurityGroup(&opts)
   163  
   164  	if err != nil {
   165  		newerr, ok := err.(awserr.Error)
   166  		if ok && newerr.Code() == "InvalidRedshiftSecurityGroup.NotFound" {
   167  			return nil
   168  		}
   169  		return err
   170  	}
   171  
   172  	return nil
   173  }
   174  
   175  func resourceAwsRedshiftSecurityGroupRetrieve(d *schema.ResourceData, meta interface{}) (*redshift.ClusterSecurityGroup, error) {
   176  	conn := meta.(*AWSClient).redshiftconn
   177  
   178  	opts := redshift.DescribeClusterSecurityGroupsInput{
   179  		ClusterSecurityGroupName: aws.String(d.Id()),
   180  	}
   181  
   182  	log.Printf("[DEBUG] Redshift Security Group describe configuration: %#v", opts)
   183  
   184  	resp, err := conn.DescribeClusterSecurityGroups(&opts)
   185  
   186  	if err != nil {
   187  		return nil, fmt.Errorf("Error retrieving Redshift Security Groups: %s", err)
   188  	}
   189  
   190  	if len(resp.ClusterSecurityGroups) != 1 ||
   191  		*resp.ClusterSecurityGroups[0].ClusterSecurityGroupName != d.Id() {
   192  		return nil, fmt.Errorf("Unable to find Redshift Security Group: %#v", resp.ClusterSecurityGroups)
   193  	}
   194  
   195  	return resp.ClusterSecurityGroups[0], nil
   196  }
   197  
   198  func validateRedshiftSecurityGroupName(v interface{}, k string) (ws []string, errors []error) {
   199  	value := v.(string)
   200  	if value == "default" {
   201  		errors = append(errors, fmt.Errorf("the Redshift Security Group name cannot be %q", value))
   202  	}
   203  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
   204  		errors = append(errors, fmt.Errorf(
   205  			"only lowercase alphanumeric characters and hyphens allowed in %q: %q",
   206  			k, value))
   207  	}
   208  	if len(value) > 255 {
   209  		errors = append(errors, fmt.Errorf(
   210  			"%q cannot be longer than 32 characters: %q", k, value))
   211  	}
   212  	return
   213  
   214  }
   215  
   216  func resourceAwsRedshiftSecurityGroupIngressHash(v interface{}) int {
   217  	var buf bytes.Buffer
   218  	m := v.(map[string]interface{})
   219  
   220  	if v, ok := m["cidr"]; ok {
   221  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   222  	}
   223  
   224  	if v, ok := m["security_group_name"]; ok {
   225  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   226  	}
   227  
   228  	if v, ok := m["security_group_owner_id"]; ok {
   229  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   230  	}
   231  
   232  	return hashcode.String(buf.String())
   233  }
   234  
   235  func resourceAwsRedshiftSecurityGroupAuthorizeRule(ingress interface{}, redshiftSecurityGroupName string, conn *redshift.Redshift) error {
   236  	ing := ingress.(map[string]interface{})
   237  
   238  	opts := redshift.AuthorizeClusterSecurityGroupIngressInput{
   239  		ClusterSecurityGroupName: aws.String(redshiftSecurityGroupName),
   240  	}
   241  
   242  	if attr, ok := ing["cidr"]; ok && attr != "" {
   243  		opts.CIDRIP = aws.String(attr.(string))
   244  	}
   245  
   246  	if attr, ok := ing["security_group_name"]; ok && attr != "" {
   247  		opts.EC2SecurityGroupName = aws.String(attr.(string))
   248  	}
   249  
   250  	if attr, ok := ing["security_group_owner_id"]; ok && attr != "" {
   251  		opts.EC2SecurityGroupOwnerId = aws.String(attr.(string))
   252  	}
   253  
   254  	log.Printf("[DEBUG] Authorize ingress rule configuration: %#v", opts)
   255  	_, err := conn.AuthorizeClusterSecurityGroupIngress(&opts)
   256  
   257  	if err != nil {
   258  		return fmt.Errorf("Error authorizing security group ingress: %s", err)
   259  	}
   260  
   261  	return nil
   262  }
   263  
   264  func resourceAwsRedshiftSecurityGroupStateRefreshFunc(
   265  	d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   266  	return func() (interface{}, string, error) {
   267  		v, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta)
   268  
   269  		if err != nil {
   270  			log.Printf("Error on retrieving Redshift Security Group when waiting: %s", err)
   271  			return nil, "", err
   272  		}
   273  
   274  		statuses := make([]string, 0, len(v.EC2SecurityGroups)+len(v.IPRanges))
   275  		for _, ec2g := range v.EC2SecurityGroups {
   276  			statuses = append(statuses, *ec2g.Status)
   277  		}
   278  		for _, ips := range v.IPRanges {
   279  			statuses = append(statuses, *ips.Status)
   280  		}
   281  
   282  		for _, stat := range statuses {
   283  			// Not done
   284  			if stat != "authorized" {
   285  				return nil, "authorizing", nil
   286  			}
   287  		}
   288  
   289  		return v, "authorized", nil
   290  	}
   291  }