github.com/shvar/terraform@v0.6.9-0.20151215234924-3365cd2231df/builtin/providers/aws/resource_aws_db_parameter_group.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "regexp" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/terraform/helper/hashcode" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/aws/awserr" 17 "github.com/aws/aws-sdk-go/service/iam" 18 "github.com/aws/aws-sdk-go/service/rds" 19 ) 20 21 func resourceAwsDbParameterGroup() *schema.Resource { 22 return &schema.Resource{ 23 Create: resourceAwsDbParameterGroupCreate, 24 Read: resourceAwsDbParameterGroupRead, 25 Update: resourceAwsDbParameterGroupUpdate, 26 Delete: resourceAwsDbParameterGroupDelete, 27 Schema: map[string]*schema.Schema{ 28 "arn": &schema.Schema{ 29 Type: schema.TypeString, 30 Computed: true, 31 }, 32 "name": &schema.Schema{ 33 Type: schema.TypeString, 34 ForceNew: true, 35 Required: true, 36 ValidateFunc: validateDbParamGroupName, 37 }, 38 "family": &schema.Schema{ 39 Type: schema.TypeString, 40 Required: true, 41 ForceNew: true, 42 }, 43 "description": &schema.Schema{ 44 Type: schema.TypeString, 45 Required: true, 46 ForceNew: true, 47 }, 48 "parameter": &schema.Schema{ 49 Type: schema.TypeSet, 50 Optional: true, 51 ForceNew: false, 52 Elem: &schema.Resource{ 53 Schema: map[string]*schema.Schema{ 54 "name": &schema.Schema{ 55 Type: schema.TypeString, 56 Required: true, 57 }, 58 "value": &schema.Schema{ 59 Type: schema.TypeString, 60 Required: true, 61 }, 62 "apply_method": &schema.Schema{ 63 Type: schema.TypeString, 64 Optional: true, 65 Default: "immediate", 66 // this parameter is not actually state, but a 67 // meta-parameter describing how the RDS API call 68 // to modify the parameter group should be made. 69 // Future reads of the resource from AWS don't tell 70 // us what we used for apply_method previously, so 71 // by squashing state to an empty string we avoid 72 // needing to do an update for every future run. 73 StateFunc: func(interface{}) string { return "" }, 74 }, 75 }, 76 }, 77 Set: resourceAwsDbParameterHash, 78 }, 79 80 "tags": tagsSchema(), 81 }, 82 } 83 } 84 85 func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) error { 86 rdsconn := meta.(*AWSClient).rdsconn 87 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 88 89 createOpts := rds.CreateDBParameterGroupInput{ 90 DBParameterGroupName: aws.String(d.Get("name").(string)), 91 DBParameterGroupFamily: aws.String(d.Get("family").(string)), 92 Description: aws.String(d.Get("description").(string)), 93 Tags: tags, 94 } 95 96 log.Printf("[DEBUG] Create DB Parameter Group: %#v", createOpts) 97 _, err := rdsconn.CreateDBParameterGroup(&createOpts) 98 if err != nil { 99 return fmt.Errorf("Error creating DB Parameter Group: %s", err) 100 } 101 102 d.Partial(true) 103 d.SetPartial("name") 104 d.SetPartial("family") 105 d.SetPartial("description") 106 d.Partial(false) 107 108 d.SetId(*createOpts.DBParameterGroupName) 109 log.Printf("[INFO] DB Parameter Group ID: %s", d.Id()) 110 111 return resourceAwsDbParameterGroupUpdate(d, meta) 112 } 113 114 func resourceAwsDbParameterGroupRead(d *schema.ResourceData, meta interface{}) error { 115 rdsconn := meta.(*AWSClient).rdsconn 116 117 describeOpts := rds.DescribeDBParameterGroupsInput{ 118 DBParameterGroupName: aws.String(d.Id()), 119 } 120 121 describeResp, err := rdsconn.DescribeDBParameterGroups(&describeOpts) 122 if err != nil { 123 return err 124 } 125 126 if len(describeResp.DBParameterGroups) != 1 || 127 *describeResp.DBParameterGroups[0].DBParameterGroupName != d.Id() { 128 return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.DBParameterGroups) 129 } 130 131 d.Set("name", describeResp.DBParameterGroups[0].DBParameterGroupName) 132 d.Set("family", describeResp.DBParameterGroups[0].DBParameterGroupFamily) 133 d.Set("description", describeResp.DBParameterGroups[0].Description) 134 135 // Only include user customized parameters as there's hundreds of system/default ones 136 describeParametersOpts := rds.DescribeDBParametersInput{ 137 DBParameterGroupName: aws.String(d.Id()), 138 Source: aws.String("user"), 139 } 140 141 describeParametersResp, err := rdsconn.DescribeDBParameters(&describeParametersOpts) 142 if err != nil { 143 return err 144 } 145 146 d.Set("parameter", flattenParameters(describeParametersResp.Parameters)) 147 148 paramGroup := describeResp.DBParameterGroups[0] 149 arn, err := buildRDSPGARN(d, meta) 150 if err != nil { 151 name := "<empty>" 152 if paramGroup.DBParameterGroupName != nil && *paramGroup.DBParameterGroupName != "" { 153 name = *paramGroup.DBParameterGroupName 154 } 155 log.Printf("[DEBUG] Error building ARN for DB Parameter Group, not setting Tags for Param Group %s", name) 156 } else { 157 d.Set("arn", arn) 158 resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{ 159 ResourceName: aws.String(arn), 160 }) 161 162 if err != nil { 163 log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn) 164 } 165 166 var dt []*rds.Tag 167 if len(resp.TagList) > 0 { 168 dt = resp.TagList 169 } 170 d.Set("tags", tagsToMapRDS(dt)) 171 } 172 173 return nil 174 } 175 176 func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error { 177 rdsconn := meta.(*AWSClient).rdsconn 178 179 d.Partial(true) 180 181 if d.HasChange("parameter") { 182 o, n := d.GetChange("parameter") 183 if o == nil { 184 o = new(schema.Set) 185 } 186 if n == nil { 187 n = new(schema.Set) 188 } 189 190 os := o.(*schema.Set) 191 ns := n.(*schema.Set) 192 193 // Expand the "parameter" set to aws-sdk-go compat []rds.Parameter 194 parameters, err := expandParameters(ns.Difference(os).List()) 195 if err != nil { 196 return err 197 } 198 199 if len(parameters) > 0 { 200 modifyOpts := rds.ModifyDBParameterGroupInput{ 201 DBParameterGroupName: aws.String(d.Get("name").(string)), 202 Parameters: parameters, 203 } 204 205 log.Printf("[DEBUG] Modify DB Parameter Group: %s", modifyOpts) 206 _, err = rdsconn.ModifyDBParameterGroup(&modifyOpts) 207 if err != nil { 208 return fmt.Errorf("Error modifying DB Parameter Group: %s", err) 209 } 210 } 211 d.SetPartial("parameter") 212 } 213 214 if arn, err := buildRDSPGARN(d, meta); err == nil { 215 if err := setTagsRDS(rdsconn, d, arn); err != nil { 216 return err 217 } else { 218 d.SetPartial("tags") 219 } 220 } 221 222 d.Partial(false) 223 224 return resourceAwsDbParameterGroupRead(d, meta) 225 } 226 227 func resourceAwsDbParameterGroupDelete(d *schema.ResourceData, meta interface{}) error { 228 stateConf := &resource.StateChangeConf{ 229 Pending: []string{"pending"}, 230 Target: "destroyed", 231 Refresh: resourceAwsDbParameterGroupDeleteRefreshFunc(d, meta), 232 Timeout: 3 * time.Minute, 233 MinTimeout: 1 * time.Second, 234 } 235 _, err := stateConf.WaitForState() 236 return err 237 } 238 239 func resourceAwsDbParameterGroupDeleteRefreshFunc( 240 d *schema.ResourceData, 241 meta interface{}) resource.StateRefreshFunc { 242 rdsconn := meta.(*AWSClient).rdsconn 243 244 return func() (interface{}, string, error) { 245 246 deleteOpts := rds.DeleteDBParameterGroupInput{ 247 DBParameterGroupName: aws.String(d.Id()), 248 } 249 250 if _, err := rdsconn.DeleteDBParameterGroup(&deleteOpts); err != nil { 251 rdserr, ok := err.(awserr.Error) 252 if !ok { 253 return d, "error", err 254 } 255 256 if rdserr.Code() != "DBParameterGroupNotFoundFault" { 257 return d, "error", err 258 } 259 } 260 261 return d, "destroyed", nil 262 } 263 } 264 265 func resourceAwsDbParameterHash(v interface{}) int { 266 var buf bytes.Buffer 267 m := v.(map[string]interface{}) 268 buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) 269 // Store the value as a lower case string, to match how we store them in flattenParameters 270 buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["value"].(string)))) 271 272 return hashcode.String(buf.String()) 273 } 274 275 func buildRDSPGARN(d *schema.ResourceData, meta interface{}) (string, error) { 276 iamconn := meta.(*AWSClient).iamconn 277 region := meta.(*AWSClient).region 278 // An zero value GetUserInput{} defers to the currently logged in user 279 resp, err := iamconn.GetUser(&iam.GetUserInput{}) 280 if err != nil { 281 return "", err 282 } 283 userARN := *resp.User.Arn 284 accountID := strings.Split(userARN, ":")[4] 285 arn := fmt.Sprintf("arn:aws:rds:%s:%s:pg:%s", region, accountID, d.Id()) 286 return arn, nil 287 } 288 289 func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []error) { 290 value := v.(string) 291 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 292 errors = append(errors, fmt.Errorf( 293 "only lowercase alphanumeric characters and hyphens allowed in %q", k)) 294 } 295 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 296 errors = append(errors, fmt.Errorf( 297 "first character of %q must be a letter", k)) 298 } 299 if regexp.MustCompile(`--`).MatchString(value) { 300 errors = append(errors, fmt.Errorf( 301 "%q cannot contain two consecutive hyphens", k)) 302 } 303 if regexp.MustCompile(`-$`).MatchString(value) { 304 errors = append(errors, fmt.Errorf( 305 "%q cannot end with a hyphen", k)) 306 } 307 if len(value) > 255 { 308 errors = append(errors, fmt.Errorf( 309 "%q cannot be greater than 255 characters", k)) 310 } 311 return 312 313 }