github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/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 err != nil { 83 return nil, fmt.Errorf("Error parsing configuration: %s", err) 84 } 85 86 if _, ok := rs.Attributes["vpc_security_group_ids.#"]; ok { 87 opts.VpcSecurityGroupIds = expandStringList(flatmap.Expand( 88 rs.Attributes, "vpc_security_group_ids").([]interface{})) 89 } 90 91 if _, ok := rs.Attributes["security_group_names.#"]; ok { 92 opts.DBSecurityGroupNames = expandStringList(flatmap.Expand( 93 rs.Attributes, "security_group_names").([]interface{})) 94 } 95 96 opts.DBInstanceIdentifier = rs.Attributes["identifier"] 97 opts.DBName = rs.Attributes["name"] 98 opts.MasterUsername = rs.Attributes["username"] 99 opts.MasterUserPassword = rs.Attributes["password"] 100 opts.EngineVersion = rs.Attributes["engine_version"] 101 opts.Engine = rs.Attributes["engine"] 102 103 log.Printf("[DEBUG] DB Instance create configuration: %#v", opts) 104 _, err = conn.CreateDBInstance(&opts) 105 if err != nil { 106 return nil, fmt.Errorf("Error creating DB Instance: %s", err) 107 } 108 109 rs.ID = rs.Attributes["identifier"] 110 111 log.Printf("[INFO] DB Instance ID: %s", rs.ID) 112 113 log.Println( 114 "[INFO] Waiting for DB Instance to be available") 115 116 stateConf := &resource.StateChangeConf{ 117 Pending: []string{"creating", "backing-up", "modifying"}, 118 Target: "available", 119 Refresh: DBInstanceStateRefreshFunc(rs.ID, conn), 120 Timeout: 10 * time.Minute, 121 MinTimeout: 10 * time.Second, 122 Delay: 30 * time.Second, // Wait 30 secs before starting 123 } 124 125 // Wait, catching any errors 126 _, err = stateConf.WaitForState() 127 if err != nil { 128 return rs, err 129 } 130 131 v, err := resource_aws_db_instance_retrieve(rs.ID, conn) 132 if err != nil { 133 return rs, err 134 } 135 136 return resource_aws_db_instance_update_state(rs, v) 137 } 138 139 func resource_aws_db_instance_update( 140 s *terraform.InstanceState, 141 d *terraform.InstanceDiff, 142 meta interface{}) (*terraform.InstanceState, error) { 143 panic("Cannot update DB") 144 } 145 146 func resource_aws_db_instance_destroy( 147 s *terraform.InstanceState, 148 meta interface{}) error { 149 p := meta.(*ResourceProvider) 150 conn := p.rdsconn 151 152 log.Printf("[DEBUG] DB Instance destroy: %v", s.ID) 153 154 opts := rds.DeleteDBInstance{DBInstanceIdentifier: s.ID} 155 156 if s.Attributes["skip_final_snapshot"] == "true" { 157 opts.SkipFinalSnapshot = true 158 } else { 159 opts.FinalDBSnapshotIdentifier = s.Attributes["final_snapshot_identifier"] 160 } 161 162 log.Printf("[DEBUG] DB Instance destroy configuration: %v", opts) 163 if _, err := conn.DeleteDBInstance(&opts); err != nil { 164 return err 165 } 166 167 log.Println( 168 "[INFO] Waiting for DB Instance to be destroyed") 169 stateConf := &resource.StateChangeConf{ 170 Pending: []string{"creating", "backing-up", 171 "modifying", "deleting", "available"}, 172 Target: "", 173 Refresh: DBInstanceStateRefreshFunc(s.ID, conn), 174 Timeout: 10 * time.Minute, 175 MinTimeout: 10 * time.Second, 176 Delay: 30 * time.Second, // Wait 30 secs before starting 177 } 178 if _, err := stateConf.WaitForState(); err != nil { 179 return err 180 } 181 182 return nil 183 } 184 185 func resource_aws_db_instance_refresh( 186 s *terraform.InstanceState, 187 meta interface{}) (*terraform.InstanceState, error) { 188 p := meta.(*ResourceProvider) 189 conn := p.rdsconn 190 191 v, err := resource_aws_db_instance_retrieve(s.ID, conn) 192 193 if err != nil { 194 return s, err 195 } 196 if v == nil { 197 s.ID = "" 198 return s, nil 199 } 200 201 return resource_aws_db_instance_update_state(s, v) 202 } 203 204 func resource_aws_db_instance_diff( 205 s *terraform.InstanceState, 206 c *terraform.ResourceConfig, 207 meta interface{}) (*terraform.InstanceDiff, error) { 208 209 b := &diff.ResourceBuilder{ 210 Attrs: map[string]diff.AttrType{ 211 "allocated_storage": diff.AttrTypeCreate, 212 "availability_zone": diff.AttrTypeCreate, 213 "backup_retention_period": diff.AttrTypeCreate, 214 "backup_window": diff.AttrTypeCreate, 215 "engine": diff.AttrTypeCreate, 216 "engine_version": diff.AttrTypeCreate, 217 "identifier": diff.AttrTypeCreate, 218 "instance_class": diff.AttrTypeCreate, 219 "iops": diff.AttrTypeCreate, 220 "maintenance_window": diff.AttrTypeCreate, 221 "multi_az": diff.AttrTypeCreate, 222 "name": diff.AttrTypeCreate, 223 "password": diff.AttrTypeCreate, 224 "port": diff.AttrTypeCreate, 225 "publicly_accessible": diff.AttrTypeCreate, 226 "username": diff.AttrTypeCreate, 227 "vpc_security_group_ids": diff.AttrTypeCreate, 228 "security_group_names": diff.AttrTypeCreate, 229 "db_subnet_group_name": diff.AttrTypeCreate, 230 "skip_final_snapshot": diff.AttrTypeUpdate, 231 "final_snapshot_identifier": diff.AttrTypeUpdate, 232 }, 233 234 ComputedAttrs: []string{ 235 "address", 236 "availability_zone", 237 "backup_retention_period", 238 "backup_window", 239 "engine_version", 240 "maintenance_window", 241 "endpoint", 242 "status", 243 "multi_az", 244 "port", 245 "address", 246 "password", 247 }, 248 } 249 250 return b.Diff(s, c) 251 } 252 253 func resource_aws_db_instance_update_state( 254 s *terraform.InstanceState, 255 v *rds.DBInstance) (*terraform.InstanceState, error) { 256 257 s.Attributes["address"] = v.Address 258 s.Attributes["allocated_storage"] = strconv.Itoa(v.AllocatedStorage) 259 s.Attributes["availability_zone"] = v.AvailabilityZone 260 s.Attributes["backup_retention_period"] = strconv.Itoa(v.BackupRetentionPeriod) 261 s.Attributes["backup_window"] = v.PreferredBackupWindow 262 s.Attributes["endpoint"] = fmt.Sprintf("%s:%s", s.Attributes["address"], strconv.Itoa(v.Port)) 263 s.Attributes["engine"] = v.Engine 264 s.Attributes["engine_version"] = v.EngineVersion 265 s.Attributes["instance_class"] = v.DBInstanceClass 266 s.Attributes["maintenance_window"] = v.PreferredMaintenanceWindow 267 s.Attributes["multi_az"] = strconv.FormatBool(v.MultiAZ) 268 s.Attributes["name"] = v.DBName 269 s.Attributes["port"] = strconv.Itoa(v.Port) 270 s.Attributes["status"] = v.DBInstanceStatus 271 s.Attributes["username"] = v.MasterUsername 272 s.Attributes["db_subnet_group_name"] = v.DBSubnetGroup.Name 273 274 // Flatten our group values 275 toFlatten := make(map[string]interface{}) 276 277 if len(v.DBSecurityGroupNames) > 0 && v.DBSecurityGroupNames[0] != "" { 278 toFlatten["security_group_names"] = v.DBSecurityGroupNames 279 } 280 if len(v.VpcSecurityGroupIds) > 0 && v.VpcSecurityGroupIds[0] != "" { 281 toFlatten["vpc_security_group_ids"] = v.VpcSecurityGroupIds 282 } 283 for k, v := range flatmap.Flatten(toFlatten) { 284 s.Attributes[k] = v 285 } 286 287 return s, nil 288 } 289 290 func resource_aws_db_instance_retrieve(id string, conn *rds.Rds) (*rds.DBInstance, error) { 291 opts := rds.DescribeDBInstances{ 292 DBInstanceIdentifier: id, 293 } 294 295 log.Printf("[DEBUG] DB Instance describe configuration: %#v", opts) 296 297 resp, err := conn.DescribeDBInstances(&opts) 298 299 if err != nil { 300 if strings.Contains(err.Error(), "DBInstanceNotFound") { 301 return nil, nil 302 } 303 return nil, fmt.Errorf("Error retrieving DB Instances: %s", err) 304 } 305 306 if len(resp.DBInstances) != 1 || 307 resp.DBInstances[0].DBInstanceIdentifier != id { 308 if err != nil { 309 return nil, nil 310 } 311 } 312 313 v := resp.DBInstances[0] 314 315 return &v, nil 316 } 317 318 func resource_aws_db_instance_validation() *config.Validator { 319 return &config.Validator{ 320 Required: []string{ 321 "allocated_storage", 322 "engine", 323 "engine_version", 324 "identifier", 325 "instance_class", 326 "name", 327 "password", 328 "username", 329 }, 330 Optional: []string{ 331 "availability_zone", 332 "backup_retention_period", 333 "backup_window", 334 "iops", 335 "maintenance_window", 336 "multi_az", 337 "port", 338 "publicly_accessible", 339 "vpc_security_group_ids.*", 340 "skip_final_snapshot", 341 "security_group_names.*", 342 "db_subnet_group_name", 343 }, 344 } 345 } 346 347 func DBInstanceStateRefreshFunc(id string, conn *rds.Rds) resource.StateRefreshFunc { 348 return func() (interface{}, string, error) { 349 v, err := resource_aws_db_instance_retrieve(id, conn) 350 351 if err != nil { 352 log.Printf("Error on retrieving DB Instance when waiting: %s", err) 353 return nil, "", err 354 } 355 356 if v == nil { 357 return nil, "", nil 358 } 359 360 return v, v.DBInstanceStatus, nil 361 } 362 }