github.com/lamielle/terraform@v0.3.2-0.20141121070651-81f008ba53d5/builtin/providers/aws/resource_aws_db_instance.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/hashicorp/terraform/flatmap" 11 "github.com/hashicorp/terraform/helper/config" 12 "github.com/hashicorp/terraform/helper/diff" 13 "github.com/hashicorp/terraform/helper/resource" 14 "github.com/hashicorp/terraform/terraform" 15 "github.com/mitchellh/goamz/rds" 16 ) 17 18 func resource_aws_db_instance_create( 19 s *terraform.InstanceState, 20 d *terraform.InstanceDiff, 21 meta interface{}) (*terraform.InstanceState, error) { 22 p := meta.(*ResourceProvider) 23 conn := p.rdsconn 24 25 // Merge the diff into the state so that we have all the attributes 26 // properly. 27 rs := s.MergeDiff(d) 28 29 var err error 30 var attr string 31 32 opts := rds.CreateDBInstance{} 33 34 if attr = rs.Attributes["allocated_storage"]; attr != "" { 35 opts.AllocatedStorage, err = strconv.Atoi(attr) 36 opts.SetAllocatedStorage = true 37 } 38 39 if attr = rs.Attributes["backup_retention_period"]; attr != "" { 40 opts.BackupRetentionPeriod, err = strconv.Atoi(attr) 41 opts.SetBackupRetentionPeriod = true 42 } 43 44 if attr = rs.Attributes["iops"]; attr != "" { 45 opts.Iops, err = strconv.Atoi(attr) 46 opts.SetIops = true 47 } 48 49 if attr = rs.Attributes["port"]; attr != "" { 50 opts.Port, err = strconv.Atoi(attr) 51 opts.SetPort = true 52 } 53 54 if attr = rs.Attributes["availability_zone"]; attr != "" { 55 opts.AvailabilityZone = attr 56 } 57 58 if attr = rs.Attributes["instance_class"]; attr != "" { 59 opts.DBInstanceClass = attr 60 } 61 62 if attr = rs.Attributes["maintenance_window"]; attr != "" { 63 opts.PreferredMaintenanceWindow = attr 64 } 65 66 if attr = rs.Attributes["backup_window"]; attr != "" { 67 opts.PreferredBackupWindow = attr 68 } 69 70 if attr = rs.Attributes["multi_az"]; attr == "true" { 71 opts.MultiAZ = true 72 } 73 74 if attr = rs.Attributes["publicly_accessible"]; attr == "true" { 75 opts.PubliclyAccessible = true 76 } 77 78 if attr = rs.Attributes["db_subnet_group_name"]; attr != "" { 79 opts.DBSubnetGroupName = attr 80 } 81 82 if attr = rs.Attributes["parameter_group_name"]; attr != "" { 83 opts.DBParameterGroupName = attr 84 } 85 86 if err != nil { 87 return nil, fmt.Errorf("Error parsing configuration: %s", err) 88 } 89 90 if _, ok := rs.Attributes["vpc_security_group_ids.#"]; ok { 91 opts.VpcSecurityGroupIds = expandStringList(flatmap.Expand( 92 rs.Attributes, "vpc_security_group_ids").([]interface{})) 93 } 94 95 if _, ok := rs.Attributes["security_group_names.#"]; ok { 96 opts.DBSecurityGroupNames = expandStringList(flatmap.Expand( 97 rs.Attributes, "security_group_names").([]interface{})) 98 } 99 100 opts.DBInstanceIdentifier = rs.Attributes["identifier"] 101 opts.DBName = rs.Attributes["name"] 102 opts.MasterUsername = rs.Attributes["username"] 103 opts.MasterUserPassword = rs.Attributes["password"] 104 opts.EngineVersion = rs.Attributes["engine_version"] 105 opts.Engine = rs.Attributes["engine"] 106 107 log.Printf("[DEBUG] DB Instance create configuration: %#v", opts) 108 _, err = conn.CreateDBInstance(&opts) 109 if err != nil { 110 return nil, fmt.Errorf("Error creating DB Instance: %s", err) 111 } 112 113 rs.ID = rs.Attributes["identifier"] 114 115 log.Printf("[INFO] DB Instance ID: %s", rs.ID) 116 117 log.Println( 118 "[INFO] Waiting for DB Instance to be available") 119 120 stateConf := &resource.StateChangeConf{ 121 Pending: []string{"creating", "backing-up", "modifying"}, 122 Target: "available", 123 Refresh: DBInstanceStateRefreshFunc(rs.ID, conn), 124 Timeout: 20 * time.Minute, 125 MinTimeout: 10 * time.Second, 126 Delay: 30 * time.Second, // Wait 30 secs before starting 127 } 128 129 // Wait, catching any errors 130 _, err = stateConf.WaitForState() 131 if err != nil { 132 return rs, err 133 } 134 135 v, err := resource_aws_db_instance_retrieve(rs.ID, conn) 136 if err != nil { 137 return rs, err 138 } 139 140 return resource_aws_db_instance_update_state(rs, v) 141 } 142 143 func resource_aws_db_instance_update( 144 s *terraform.InstanceState, 145 d *terraform.InstanceDiff, 146 meta interface{}) (*terraform.InstanceState, error) { 147 panic("Cannot update DB") 148 } 149 150 func resource_aws_db_instance_destroy( 151 s *terraform.InstanceState, 152 meta interface{}) error { 153 p := meta.(*ResourceProvider) 154 conn := p.rdsconn 155 156 log.Printf("[DEBUG] DB Instance destroy: %v", s.ID) 157 158 opts := rds.DeleteDBInstance{DBInstanceIdentifier: s.ID} 159 160 if s.Attributes["skip_final_snapshot"] == "true" { 161 opts.SkipFinalSnapshot = true 162 } else { 163 opts.FinalDBSnapshotIdentifier = s.Attributes["final_snapshot_identifier"] 164 } 165 166 log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts) 167 if _, err := conn.DeleteDBInstance(&opts); err != nil { 168 return err 169 } 170 171 log.Println( 172 "[INFO] Waiting for DB Instance to be destroyed") 173 stateConf := &resource.StateChangeConf{ 174 Pending: []string{"creating", "backing-up", 175 "modifying", "deleting", "available"}, 176 Target: "", 177 Refresh: DBInstanceStateRefreshFunc(s.ID, conn), 178 Timeout: 20 * time.Minute, 179 MinTimeout: 10 * time.Second, 180 Delay: 30 * time.Second, // Wait 30 secs before starting 181 } 182 if _, err := stateConf.WaitForState(); err != nil { 183 return err 184 } 185 186 return nil 187 } 188 189 func resource_aws_db_instance_refresh( 190 s *terraform.InstanceState, 191 meta interface{}) (*terraform.InstanceState, error) { 192 p := meta.(*ResourceProvider) 193 conn := p.rdsconn 194 195 v, err := resource_aws_db_instance_retrieve(s.ID, conn) 196 197 if err != nil { 198 return s, err 199 } 200 if v == nil { 201 s.ID = "" 202 return s, nil 203 } 204 205 return resource_aws_db_instance_update_state(s, v) 206 } 207 208 func resource_aws_db_instance_diff( 209 s *terraform.InstanceState, 210 c *terraform.ResourceConfig, 211 meta interface{}) (*terraform.InstanceDiff, error) { 212 213 b := &diff.ResourceBuilder{ 214 Attrs: map[string]diff.AttrType{ 215 "allocated_storage": diff.AttrTypeCreate, 216 "availability_zone": diff.AttrTypeCreate, 217 "backup_retention_period": diff.AttrTypeCreate, 218 "backup_window": diff.AttrTypeCreate, 219 "engine": diff.AttrTypeCreate, 220 "engine_version": diff.AttrTypeCreate, 221 "identifier": diff.AttrTypeCreate, 222 "instance_class": diff.AttrTypeCreate, 223 "iops": diff.AttrTypeCreate, 224 "maintenance_window": diff.AttrTypeCreate, 225 "multi_az": diff.AttrTypeCreate, 226 "name": diff.AttrTypeCreate, 227 "password": diff.AttrTypeCreate, 228 "port": diff.AttrTypeCreate, 229 "publicly_accessible": diff.AttrTypeCreate, 230 "username": diff.AttrTypeCreate, 231 "vpc_security_group_ids": diff.AttrTypeCreate, 232 "security_group_names": diff.AttrTypeCreate, 233 "db_subnet_group_name": diff.AttrTypeCreate, 234 "parameter_group_name": diff.AttrTypeCreate, 235 "skip_final_snapshot": diff.AttrTypeUpdate, 236 "final_snapshot_identifier": diff.AttrTypeUpdate, 237 }, 238 239 ComputedAttrs: []string{ 240 "address", 241 "availability_zone", 242 "backup_retention_period", 243 "backup_window", 244 "engine_version", 245 "maintenance_window", 246 "endpoint", 247 "status", 248 "multi_az", 249 "port", 250 "address", 251 "password", 252 }, 253 } 254 255 return b.Diff(s, c) 256 } 257 258 func resource_aws_db_instance_update_state( 259 s *terraform.InstanceState, 260 v *rds.DBInstance) (*terraform.InstanceState, error) { 261 262 s.Attributes["address"] = v.Address 263 s.Attributes["allocated_storage"] = strconv.Itoa(v.AllocatedStorage) 264 s.Attributes["availability_zone"] = v.AvailabilityZone 265 s.Attributes["backup_retention_period"] = strconv.Itoa(v.BackupRetentionPeriod) 266 s.Attributes["backup_window"] = v.PreferredBackupWindow 267 s.Attributes["endpoint"] = fmt.Sprintf("%s:%s", s.Attributes["address"], strconv.Itoa(v.Port)) 268 s.Attributes["engine"] = v.Engine 269 s.Attributes["engine_version"] = v.EngineVersion 270 s.Attributes["instance_class"] = v.DBInstanceClass 271 s.Attributes["maintenance_window"] = v.PreferredMaintenanceWindow 272 s.Attributes["multi_az"] = strconv.FormatBool(v.MultiAZ) 273 s.Attributes["name"] = v.DBName 274 s.Attributes["port"] = strconv.Itoa(v.Port) 275 s.Attributes["status"] = v.DBInstanceStatus 276 s.Attributes["username"] = v.MasterUsername 277 s.Attributes["db_subnet_group_name"] = v.DBSubnetGroup.Name 278 s.Attributes["parameter_group_name"] = v.DBParameterGroupName 279 280 // Flatten our group values 281 toFlatten := make(map[string]interface{}) 282 283 if len(v.DBSecurityGroupNames) > 0 && v.DBSecurityGroupNames[0] != "" { 284 toFlatten["security_group_names"] = v.DBSecurityGroupNames 285 } 286 if len(v.VpcSecurityGroupIds) > 0 && v.VpcSecurityGroupIds[0] != "" { 287 toFlatten["vpc_security_group_ids"] = v.VpcSecurityGroupIds 288 } 289 for k, v := range flatmap.Flatten(toFlatten) { 290 s.Attributes[k] = v 291 } 292 293 return s, nil 294 } 295 296 func resource_aws_db_instance_retrieve(id string, conn *rds.Rds) (*rds.DBInstance, error) { 297 opts := rds.DescribeDBInstances{ 298 DBInstanceIdentifier: id, 299 } 300 301 log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts) 302 303 resp, err := conn.DescribeDBInstances(&opts) 304 305 if err != nil { 306 if strings.Contains(err.Error(), "DBInstanceNotFound") { 307 return nil, nil 308 } 309 return nil, fmt.Errorf("Error retrieving DB Instances: %s", err) 310 } 311 312 if len(resp.DBInstances) != 1 || 313 resp.DBInstances[0].DBInstanceIdentifier != id { 314 if err != nil { 315 return nil, nil 316 } 317 } 318 319 v := resp.DBInstances[0] 320 321 return &v, nil 322 } 323 324 func resource_aws_db_instance_validation() *config.Validator { 325 return &config.Validator{ 326 Required: []string{ 327 "allocated_storage", 328 "engine", 329 "engine_version", 330 "identifier", 331 "instance_class", 332 "name", 333 "password", 334 "username", 335 }, 336 Optional: []string{ 337 "availability_zone", 338 "backup_retention_period", 339 "backup_window", 340 "iops", 341 "maintenance_window", 342 "multi_az", 343 "port", 344 "publicly_accessible", 345 "vpc_security_group_ids.*", 346 "skip_final_snapshot", 347 "security_group_names.*", 348 "db_subnet_group_name", 349 "parameter_group_name", 350 "skip_final_snapshot", 351 "final_snapshot_identifier", 352 }, 353 } 354 } 355 356 func DBInstanceStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc { 357 return func() (interface{}, string, error) { 358 v, err := resource_aws_db_instance_retrieve(id, conn) 359 360 if err != nil { 361 log.Printf("Error on retrieving DB Instance when waiting: %s", err) 362 return nil, "", err 363 } 364 365 if v == nil { 366 return nil, "", nil 367 } 368 369 return v, v.DBInstanceStatus, nil 370 } 371 }