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 }