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