github.com/ottenhoff/terraform@v0.7.0-rc1.0.20160607213102-ac2d195cc560/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 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/iam" 12 "github.com/aws/aws-sdk-go/service/rds" 13 14 "github.com/hashicorp/terraform/helper/resource" 15 "github.com/hashicorp/terraform/helper/schema" 16 ) 17 18 func resourceAwsRDSClusterParameterGroup() *schema.Resource { 19 return &schema.Resource{ 20 Create: resourceAwsRDSClusterParameterGroupCreate, 21 Read: resourceAwsRDSClusterParameterGroupRead, 22 Update: resourceAwsRDSClusterParameterGroupUpdate, 23 Delete: resourceAwsRDSClusterParameterGroupDelete, 24 Schema: map[string]*schema.Schema{ 25 "arn": &schema.Schema{ 26 Type: schema.TypeString, 27 Computed: true, 28 }, 29 "name": &schema.Schema{ 30 Type: schema.TypeString, 31 ForceNew: true, 32 Required: true, 33 ValidateFunc: validateDbParamGroupName, 34 }, 35 "family": &schema.Schema{ 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 "description": &schema.Schema{ 41 Type: schema.TypeString, 42 Optional: true, 43 ForceNew: true, 44 Default: "Managed by Terraform", 45 }, 46 "parameter": &schema.Schema{ 47 Type: schema.TypeSet, 48 Optional: true, 49 ForceNew: false, 50 Elem: &schema.Resource{ 51 Schema: map[string]*schema.Schema{ 52 "name": &schema.Schema{ 53 Type: schema.TypeString, 54 Required: true, 55 }, 56 "value": &schema.Schema{ 57 Type: schema.TypeString, 58 Required: true, 59 }, 60 "apply_method": &schema.Schema{ 61 Type: schema.TypeString, 62 Optional: true, 63 Default: "immediate", 64 // this parameter is not actually state, but a 65 // meta-parameter describing how the RDS API call 66 // to modify the parameter group should be made. 67 // Future reads of the resource from AWS don't tell 68 // us what we used for apply_method previously, so 69 // by squashing state to an empty string we avoid 70 // needing to do an update for every future run. 71 StateFunc: func(interface{}) string { return "" }, 72 }, 73 }, 74 }, 75 Set: resourceAwsDbParameterHash, 76 }, 77 78 "tags": tagsSchema(), 79 }, 80 } 81 } 82 83 func resourceAwsRDSClusterParameterGroupCreate(d *schema.ResourceData, meta interface{}) error { 84 rdsconn := meta.(*AWSClient).rdsconn 85 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 86 87 createOpts := rds.CreateDBClusterParameterGroupInput{ 88 DBClusterParameterGroupName: aws.String(d.Get("name").(string)), 89 DBParameterGroupFamily: aws.String(d.Get("family").(string)), 90 Description: aws.String(d.Get("description").(string)), 91 Tags: tags, 92 } 93 94 log.Printf("[DEBUG] Create DB Cluster Parameter Group: %#v", createOpts) 95 _, err := rdsconn.CreateDBClusterParameterGroup(&createOpts) 96 if err != nil { 97 return fmt.Errorf("Error creating DB Cluster Parameter Group: %s", err) 98 } 99 100 d.SetId(*createOpts.DBClusterParameterGroupName) 101 log.Printf("[INFO] DB Cluster Parameter Group ID: %s", d.Id()) 102 103 return resourceAwsRDSClusterParameterGroupUpdate(d, meta) 104 } 105 106 func resourceAwsRDSClusterParameterGroupRead(d *schema.ResourceData, meta interface{}) error { 107 rdsconn := meta.(*AWSClient).rdsconn 108 109 describeOpts := rds.DescribeDBClusterParameterGroupsInput{ 110 DBClusterParameterGroupName: aws.String(d.Id()), 111 } 112 113 describeResp, err := rdsconn.DescribeDBClusterParameterGroups(&describeOpts) 114 if err != nil { 115 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DBParameterGroupNotFound" { 116 log.Printf("[WARN] DB Cluster Parameter Group (%s) not found, error code (404)", d.Id()) 117 d.SetId("") 118 return nil 119 } 120 121 return err 122 } 123 124 if len(describeResp.DBClusterParameterGroups) != 1 || 125 *describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName != d.Id() { 126 return fmt.Errorf("Unable to find Cluster Parameter Group: %#v", describeResp.DBClusterParameterGroups) 127 } 128 129 d.Set("name", describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName) 130 d.Set("family", describeResp.DBClusterParameterGroups[0].DBParameterGroupFamily) 131 d.Set("description", describeResp.DBClusterParameterGroups[0].Description) 132 133 // Only include user customized parameters as there's hundreds of system/default ones 134 describeParametersOpts := rds.DescribeDBClusterParametersInput{ 135 DBClusterParameterGroupName: aws.String(d.Id()), 136 Source: aws.String("user"), 137 } 138 139 describeParametersResp, err := rdsconn.DescribeDBClusterParameters(&describeParametersOpts) 140 if err != nil { 141 return err 142 } 143 144 d.Set("parameter", flattenParameters(describeParametersResp.Parameters)) 145 146 paramGroup := describeResp.DBClusterParameterGroups[0] 147 arn, err := buildRDSCPGARN(d, meta) 148 if err != nil { 149 name := "<empty>" 150 if paramGroup.DBClusterParameterGroupName != nil && *paramGroup.DBClusterParameterGroupName != "" { 151 name = *paramGroup.DBClusterParameterGroupName 152 } 153 log.Printf("[DEBUG] Error building ARN for DB Cluster Parameter Group, not setting Tags for Cluster Param Group %s", name) 154 } else { 155 d.Set("arn", arn) 156 resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{ 157 ResourceName: aws.String(arn), 158 }) 159 160 if err != nil { 161 log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn) 162 } 163 164 var dt []*rds.Tag 165 if len(resp.TagList) > 0 { 166 dt = resp.TagList 167 } 168 d.Set("tags", tagsToMapRDS(dt)) 169 } 170 171 return nil 172 } 173 174 func resourceAwsRDSClusterParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error { 175 rdsconn := meta.(*AWSClient).rdsconn 176 177 d.Partial(true) 178 179 if d.HasChange("parameter") { 180 o, n := d.GetChange("parameter") 181 if o == nil { 182 o = new(schema.Set) 183 } 184 if n == nil { 185 n = new(schema.Set) 186 } 187 188 os := o.(*schema.Set) 189 ns := n.(*schema.Set) 190 191 // Expand the "parameter" set to aws-sdk-go compat []rds.Parameter 192 parameters, err := expandParameters(ns.Difference(os).List()) 193 if err != nil { 194 return err 195 } 196 197 if len(parameters) > 0 { 198 modifyOpts := rds.ModifyDBClusterParameterGroupInput{ 199 DBClusterParameterGroupName: aws.String(d.Get("name").(string)), 200 Parameters: parameters, 201 } 202 203 log.Printf("[DEBUG] Modify DB Cluster Parameter Group: %s", modifyOpts) 204 _, err = rdsconn.ModifyDBClusterParameterGroup(&modifyOpts) 205 if err != nil { 206 return fmt.Errorf("Error modifying DB Cluster Parameter Group: %s", err) 207 } 208 } 209 d.SetPartial("parameter") 210 } 211 212 if arn, err := buildRDSCPGARN(d, meta); err == nil { 213 if err := setTagsRDS(rdsconn, d, arn); err != nil { 214 return err 215 } else { 216 d.SetPartial("tags") 217 } 218 } 219 220 d.Partial(false) 221 222 return resourceAwsRDSClusterParameterGroupRead(d, meta) 223 } 224 225 func resourceAwsRDSClusterParameterGroupDelete(d *schema.ResourceData, meta interface{}) error { 226 stateConf := &resource.StateChangeConf{ 227 Pending: []string{"pending"}, 228 Target: []string{"destroyed"}, 229 Refresh: resourceAwsRDSClusterParameterGroupDeleteRefreshFunc(d, meta), 230 Timeout: 3 * time.Minute, 231 MinTimeout: 1 * time.Second, 232 } 233 _, err := stateConf.WaitForState() 234 return err 235 } 236 237 func resourceAwsRDSClusterParameterGroupDeleteRefreshFunc( 238 d *schema.ResourceData, 239 meta interface{}) resource.StateRefreshFunc { 240 rdsconn := meta.(*AWSClient).rdsconn 241 242 return func() (interface{}, string, error) { 243 244 deleteOpts := rds.DeleteDBClusterParameterGroupInput{ 245 DBClusterParameterGroupName: aws.String(d.Id()), 246 } 247 248 if _, err := rdsconn.DeleteDBClusterParameterGroup(&deleteOpts); err != nil { 249 rdserr, ok := err.(awserr.Error) 250 if !ok { 251 return d, "error", err 252 } 253 254 if rdserr.Code() != "DBParameterGroupNotFound" { 255 return d, "error", err 256 } 257 } 258 259 return d, "destroyed", nil 260 } 261 } 262 263 func buildRDSCPGARN(d *schema.ResourceData, meta interface{}) (string, error) { 264 iamconn := meta.(*AWSClient).iamconn 265 region := meta.(*AWSClient).region 266 // An zero value GetUserInput{} defers to the currently logged in user 267 resp, err := iamconn.GetUser(&iam.GetUserInput{}) 268 if err != nil { 269 return "", err 270 } 271 userARN := *resp.User.Arn 272 accountID := strings.Split(userARN, ":")[4] 273 arn := fmt.Sprintf("arn:aws:rds:%s:%s:cluster-pg:%s", region, accountID, d.Id()) 274 return arn, nil 275 }