github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 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 Optional: true, 45 ForceNew: true, 46 Default: "Managed by Terraform", 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 resourceAwsRDSClusterParameterGroupCreate(d *schema.ResourceData, meta interface{}) error { 86 rdsconn := meta.(*AWSClient).rdsconn 87 tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) 88 89 createOpts := rds.CreateDBClusterParameterGroupInput{ 90 DBClusterParameterGroupName: 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 Cluster Parameter Group: %#v", createOpts) 97 _, err := rdsconn.CreateDBClusterParameterGroup(&createOpts) 98 if err != nil { 99 return fmt.Errorf("Error creating DB Cluster Parameter Group: %s", err) 100 } 101 102 d.SetId(*createOpts.DBClusterParameterGroupName) 103 log.Printf("[INFO] DB Cluster Parameter Group ID: %s", d.Id()) 104 105 return resourceAwsRDSClusterParameterGroupUpdate(d, meta) 106 } 107 108 func resourceAwsRDSClusterParameterGroupRead(d *schema.ResourceData, meta interface{}) error { 109 rdsconn := meta.(*AWSClient).rdsconn 110 111 describeOpts := rds.DescribeDBClusterParameterGroupsInput{ 112 DBClusterParameterGroupName: aws.String(d.Id()), 113 } 114 115 describeResp, err := rdsconn.DescribeDBClusterParameterGroups(&describeOpts) 116 if err != nil { 117 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DBParameterGroupNotFound" { 118 log.Printf("[WARN] DB Cluster Parameter Group (%s) not found, error code (404)", d.Id()) 119 d.SetId("") 120 return nil 121 } 122 123 return err 124 } 125 126 if len(describeResp.DBClusterParameterGroups) != 1 || 127 *describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName != d.Id() { 128 return fmt.Errorf("Unable to find Cluster Parameter Group: %#v", describeResp.DBClusterParameterGroups) 129 } 130 131 d.Set("name", describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName) 132 d.Set("family", describeResp.DBClusterParameterGroups[0].DBParameterGroupFamily) 133 d.Set("description", describeResp.DBClusterParameterGroups[0].Description) 134 135 // Only include user customized parameters as there's hundreds of system/default ones 136 describeParametersOpts := rds.DescribeDBClusterParametersInput{ 137 DBClusterParameterGroupName: aws.String(d.Id()), 138 Source: aws.String("user"), 139 } 140 141 describeParametersResp, err := rdsconn.DescribeDBClusterParameters(&describeParametersOpts) 142 if err != nil { 143 return err 144 } 145 146 d.Set("parameter", flattenParameters(describeParametersResp.Parameters)) 147 148 paramGroup := describeResp.DBClusterParameterGroups[0] 149 arn, err := buildRDSCPGARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region) 150 if err != nil { 151 name := "<empty>" 152 if paramGroup.DBClusterParameterGroupName != nil && *paramGroup.DBClusterParameterGroupName != "" { 153 name = *paramGroup.DBClusterParameterGroupName 154 } 155 log.Printf("[DEBUG] Error building ARN for DB Cluster Parameter Group, not setting Tags for Cluster 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 resourceAwsRDSClusterParameterGroupUpdate(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.ModifyDBClusterParameterGroupInput{ 201 DBClusterParameterGroupName: aws.String(d.Get("name").(string)), 202 Parameters: parameters, 203 } 204 205 log.Printf("[DEBUG] Modify DB Cluster Parameter Group: %s", modifyOpts) 206 _, err = rdsconn.ModifyDBClusterParameterGroup(&modifyOpts) 207 if err != nil { 208 return fmt.Errorf("Error modifying DB Cluster Parameter Group: %s", err) 209 } 210 } 211 d.SetPartial("parameter") 212 } 213 214 if arn, err := buildRDSCPGARN(d.Id(), meta.(*AWSClient).partition, meta.(*AWSClient).accountid, meta.(*AWSClient).region); 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 resourceAwsRDSClusterParameterGroupRead(d, meta) 225 } 226 227 func resourceAwsRDSClusterParameterGroupDelete(d *schema.ResourceData, meta interface{}) error { 228 stateConf := &resource.StateChangeConf{ 229 Pending: []string{"pending"}, 230 Target: []string{"destroyed"}, 231 Refresh: resourceAwsRDSClusterParameterGroupDeleteRefreshFunc(d, meta), 232 Timeout: 3 * time.Minute, 233 MinTimeout: 1 * time.Second, 234 } 235 _, err := stateConf.WaitForState() 236 return err 237 } 238 239 func resourceAwsRDSClusterParameterGroupDeleteRefreshFunc( 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.DeleteDBClusterParameterGroupInput{ 247 DBClusterParameterGroupName: aws.String(d.Id()), 248 } 249 250 if _, err := rdsconn.DeleteDBClusterParameterGroup(&deleteOpts); err != nil { 251 rdserr, ok := err.(awserr.Error) 252 if !ok { 253 return d, "error", err 254 } 255 256 if rdserr.Code() != "DBParameterGroupNotFound" { 257 return d, "error", err 258 } 259 } 260 261 return d, "destroyed", nil 262 } 263 } 264 265 func buildRDSCPGARN(identifier, partition, accountid, region string) (string, error) { 266 if partition == "" { 267 return "", fmt.Errorf("Unable to construct RDS Cluster ARN because of missing AWS partition") 268 } 269 if accountid == "" { 270 return "", fmt.Errorf("Unable to construct RDS Cluster ARN because of missing AWS Account ID") 271 } 272 arn := fmt.Sprintf("arn:%s:rds:%s:%s:cluster-pg:%s", partition, region, accountid, identifier) 273 return arn, nil 274 275 }