github.com/jdextraze/terraform@v0.6.17-0.20160511153921-e33847c8a8af/builtin/providers/aws/resource_aws_db_option_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"regexp"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/rds"
    12  	"github.com/hashicorp/terraform/helper/hashcode"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceAwsDbOptionGroup() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceAwsDbOptionGroupCreate,
    19  		Read:   resourceAwsDbOptionGroupRead,
    20  		Update: resourceAwsDbOptionGroupUpdate,
    21  		Delete: resourceAwsDbOptionGroupDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"arn": &schema.Schema{
    25  				Type:     schema.TypeString,
    26  				Computed: true,
    27  			},
    28  			"name": &schema.Schema{
    29  				Type:         schema.TypeString,
    30  				ForceNew:     true,
    31  				Required:     true,
    32  				ValidateFunc: validateDbOptionGroupName,
    33  			},
    34  			"engine_name": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  				ForceNew: true,
    38  			},
    39  			"major_engine_version": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  				ForceNew: true,
    43  			},
    44  			"option_group_description": &schema.Schema{
    45  				Type:     schema.TypeString,
    46  				Required: true,
    47  				ForceNew: true,
    48  			},
    49  
    50  			"option": &schema.Schema{
    51  				Type:     schema.TypeSet,
    52  				Optional: true,
    53  				Elem: &schema.Resource{
    54  					Schema: map[string]*schema.Schema{
    55  						"option_name": &schema.Schema{
    56  							Type:     schema.TypeString,
    57  							Required: true,
    58  						},
    59  						"port": &schema.Schema{
    60  							Type:     schema.TypeInt,
    61  							Optional: true,
    62  						},
    63  						"db_security_group_memberships": &schema.Schema{
    64  							Type:     schema.TypeSet,
    65  							Optional: true,
    66  							Elem:     &schema.Schema{Type: schema.TypeString},
    67  							Set:      schema.HashString,
    68  						},
    69  						"vpc_security_group_memberships": &schema.Schema{
    70  							Type:     schema.TypeSet,
    71  							Optional: true,
    72  							Elem:     &schema.Schema{Type: schema.TypeString},
    73  							Set:      schema.HashString,
    74  						},
    75  					},
    76  				},
    77  				Set: resourceAwsDbOptionHash,
    78  			},
    79  
    80  			"tags": tagsSchema(),
    81  		},
    82  	}
    83  }
    84  
    85  func resourceAwsDbOptionGroupCreate(d *schema.ResourceData, meta interface{}) error {
    86  	rdsconn := meta.(*AWSClient).rdsconn
    87  	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
    88  
    89  	createOpts := &rds.CreateOptionGroupInput{
    90  		EngineName:             aws.String(d.Get("engine_name").(string)),
    91  		MajorEngineVersion:     aws.String(d.Get("major_engine_version").(string)),
    92  		OptionGroupDescription: aws.String(d.Get("option_group_description").(string)),
    93  		OptionGroupName:        aws.String(d.Get("name").(string)),
    94  		Tags:                   tags,
    95  	}
    96  
    97  	log.Printf("[DEBUG] Create DB Option Group: %#v", createOpts)
    98  	_, err := rdsconn.CreateOptionGroup(createOpts)
    99  	if err != nil {
   100  		return fmt.Errorf("Error creating DB Option Group: %s", err)
   101  	}
   102  
   103  	d.SetId(d.Get("name").(string))
   104  	log.Printf("[INFO] DB Option Group ID: %s", d.Id())
   105  
   106  	return resourceAwsDbOptionGroupUpdate(d, meta)
   107  }
   108  
   109  func resourceAwsDbOptionGroupRead(d *schema.ResourceData, meta interface{}) error {
   110  	rdsconn := meta.(*AWSClient).rdsconn
   111  	params := &rds.DescribeOptionGroupsInput{
   112  		OptionGroupName: aws.String(d.Get("name").(string)),
   113  	}
   114  
   115  	log.Printf("[DEBUG] Describe DB Option Group: %#v", params)
   116  	options, err := rdsconn.DescribeOptionGroups(params)
   117  	if err != nil {
   118  		if awsErr, ok := err.(awserr.Error); ok {
   119  			if "OptionGroupNotFoundFault" == awsErr.Code() {
   120  				d.SetId("")
   121  				log.Printf("[DEBUG] DB Option Group (%s) not found", d.Get("name").(string))
   122  				return nil
   123  			}
   124  		}
   125  		return fmt.Errorf("Error Describing DB Option Group: %s", err)
   126  	}
   127  
   128  	var option *rds.OptionGroup
   129  	for _, ogl := range options.OptionGroupsList {
   130  		if *ogl.OptionGroupName == d.Get("name").(string) {
   131  			option = ogl
   132  			break
   133  		}
   134  	}
   135  
   136  	if option == nil {
   137  		return fmt.Errorf("Unable to find Option Group: %#v", options.OptionGroupsList)
   138  	}
   139  
   140  	d.Set("major_engine_version", option.MajorEngineVersion)
   141  	d.Set("engine_name", option.EngineName)
   142  	d.Set("option_group_description", option.OptionGroupDescription)
   143  	if len(option.Options) != 0 {
   144  		d.Set("option", flattenOptions(option.Options))
   145  	}
   146  
   147  	optionGroup := options.OptionGroupsList[0]
   148  	arn, err := buildRDSOptionGroupARN(d.Id(), meta.(*AWSClient).accountid, meta.(*AWSClient).region)
   149  	if err != nil {
   150  		name := "<empty>"
   151  		if optionGroup.OptionGroupName != nil && *optionGroup.OptionGroupName != "" {
   152  			name = *optionGroup.OptionGroupName
   153  		}
   154  		log.Printf("[DEBUG] Error building ARN for DB Option Group, not setting Tags for Option Group %s", name)
   155  	} else {
   156  		d.Set("arn", arn)
   157  		resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{
   158  			ResourceName: aws.String(arn),
   159  		})
   160  
   161  		if err != nil {
   162  			log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn)
   163  		}
   164  
   165  		var dt []*rds.Tag
   166  		if len(resp.TagList) > 0 {
   167  			dt = resp.TagList
   168  		}
   169  		d.Set("tags", tagsToMapRDS(dt))
   170  	}
   171  
   172  	return nil
   173  }
   174  
   175  func resourceAwsDbOptionGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   176  	rdsconn := meta.(*AWSClient).rdsconn
   177  	if d.HasChange("option") {
   178  		o, n := d.GetChange("option")
   179  		if o == nil {
   180  			o = new(schema.Set)
   181  		}
   182  		if n == nil {
   183  			n = new(schema.Set)
   184  		}
   185  
   186  		os := o.(*schema.Set)
   187  		ns := n.(*schema.Set)
   188  		addOptions, addErr := expandOptionConfiguration(ns.Difference(os).List())
   189  		if addErr != nil {
   190  			return addErr
   191  		}
   192  
   193  		removeOptions, removeErr := flattenOptionConfigurationNames(os.Difference(ns).List())
   194  		if removeErr != nil {
   195  			return removeErr
   196  		}
   197  
   198  		modifyOpts := &rds.ModifyOptionGroupInput{
   199  			OptionGroupName:  aws.String(d.Id()),
   200  			ApplyImmediately: aws.Bool(true),
   201  		}
   202  
   203  		if len(addOptions) > 0 {
   204  			modifyOpts.OptionsToInclude = addOptions
   205  		}
   206  
   207  		if len(removeOptions) > 0 {
   208  			modifyOpts.OptionsToRemove = removeOptions
   209  		}
   210  
   211  		log.Printf("[DEBUG] Modify DB Option Group: %s", modifyOpts)
   212  		_, err := rdsconn.ModifyOptionGroup(modifyOpts)
   213  		if err != nil {
   214  			return fmt.Errorf("Error modifying DB Option Group: %s", err)
   215  		}
   216  		d.SetPartial("option")
   217  
   218  	}
   219  
   220  	if arn, err := buildRDSOptionGroupARN(d.Id(), meta.(*AWSClient).accountid, meta.(*AWSClient).region); err == nil {
   221  		if err := setTagsRDS(rdsconn, d, arn); err != nil {
   222  			return err
   223  		} else {
   224  			d.SetPartial("tags")
   225  		}
   226  	}
   227  
   228  	return resourceAwsDbOptionGroupRead(d, meta)
   229  }
   230  
   231  func resourceAwsDbOptionGroupDelete(d *schema.ResourceData, meta interface{}) error {
   232  	rdsconn := meta.(*AWSClient).rdsconn
   233  
   234  	deleteOpts := &rds.DeleteOptionGroupInput{
   235  		OptionGroupName: aws.String(d.Id()),
   236  	}
   237  
   238  	log.Printf("[DEBUG] Delete DB Option Group: %#v", deleteOpts)
   239  	_, err := rdsconn.DeleteOptionGroup(deleteOpts)
   240  	if err != nil {
   241  		return fmt.Errorf("Error Deleting DB Option Group: %s", err)
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  func flattenOptionConfigurationNames(configured []interface{}) ([]*string, error) {
   248  	var optionNames []*string
   249  	for _, pRaw := range configured {
   250  		data := pRaw.(map[string]interface{})
   251  		optionNames = append(optionNames, aws.String(data["option_name"].(string)))
   252  	}
   253  
   254  	return optionNames, nil
   255  }
   256  
   257  func resourceAwsDbOptionHash(v interface{}) int {
   258  	var buf bytes.Buffer
   259  	m := v.(map[string]interface{})
   260  	buf.WriteString(fmt.Sprintf("%s-", m["option_name"].(string)))
   261  	if _, ok := m["port"]; ok {
   262  		buf.WriteString(fmt.Sprintf("%d-", m["port"].(int)))
   263  	}
   264  
   265  	return hashcode.String(buf.String())
   266  }
   267  
   268  func buildRDSOptionGroupARN(identifier, accountid, region string) (string, error) {
   269  	if accountid == "" {
   270  		return "", fmt.Errorf("Unable to construct RDS Option Group ARN because of missing AWS Account ID")
   271  	}
   272  	arn := fmt.Sprintf("arn:aws:rds:%s:%s:og:%s", region, accountid, identifier)
   273  	return arn, nil
   274  }
   275  
   276  func validateDbOptionGroupName(v interface{}, k string) (ws []string, errors []error) {
   277  	value := v.(string)
   278  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
   279  		errors = append(errors, fmt.Errorf(
   280  			"first character of %q must be a letter", k))
   281  	}
   282  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   283  		errors = append(errors, fmt.Errorf(
   284  			"only alphanumeric characters and hyphens allowed in %q", k))
   285  	}
   286  	if regexp.MustCompile(`--`).MatchString(value) {
   287  		errors = append(errors, fmt.Errorf(
   288  			"%q cannot contain two consecutive hyphens", k))
   289  	}
   290  	if regexp.MustCompile(`-$`).MatchString(value) {
   291  		errors = append(errors, fmt.Errorf(
   292  			"%q cannot end with a hyphen", k))
   293  	}
   294  	if len(value) > 255 {
   295  		errors = append(errors, fmt.Errorf(
   296  			"%q cannot be greater than 255 characters", k))
   297  	}
   298  	return
   299  }