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