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