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 }