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