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