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