github.com/IBM-Cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/builtin/providers/aws/resource_aws_db_option_group.go (about)

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