mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-20 11:48:24 -06:00
Instance block devices are now managed by three distinct sub-resources: * `root_block_device` - introduced previously * `ebs_block_device` - all additional ebs-backed volumes * `ephemeral_block_device` - instance store / ephemeral devices The AWS API support around BlockDeviceMapping is pretty confusing. It's a single collection type that supports these three members each of which has different fields and different behavior. My biggest hiccup came from the fact that Instance Store volumes do not show up in any response BlockDeviceMapping for any EC2 `Describe*` API calls. They're only available from the instance meta-data service as queried from inside the node. This removes `block_device` altogether for a clean break from old configs. New configs will need to sort their `block_device` declarations into the three new types. The field has been marked `Removed` to indicate this to users. With the new block device format being introduced, we need to ensure Terraform is able to properly read statefiles written in the old format. So we use the new `helper/schema` facility of "state migrations" to transform statefiles in the old format to something that the current version of the schema can use. Fixes #858
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
package aws
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/terraform/helper/hashcode"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
)
|
|
|
|
func resourceAwsInstanceMigrateState(
|
|
v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
|
|
switch v {
|
|
case 0:
|
|
log.Println("[INFO] Found AWS Instance State v0; migrating to v1")
|
|
return migrateStateV0toV1(is)
|
|
default:
|
|
return is, fmt.Errorf("Unexpected schema version: %d", v)
|
|
}
|
|
|
|
return is, nil
|
|
}
|
|
|
|
func migrateStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) {
|
|
log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes)
|
|
// Delete old count
|
|
delete(is.Attributes, "block_device.#")
|
|
|
|
oldBds, err := readV0BlockDevices(is)
|
|
if err != nil {
|
|
return is, err
|
|
}
|
|
// seed count fields for new types
|
|
is.Attributes["ebs_block_device.#"] = "0"
|
|
is.Attributes["ephemeral_block_device.#"] = "0"
|
|
// depending on if state was v0.3.7 or an earlier version, it might have
|
|
// root_block_device defined already
|
|
if _, ok := is.Attributes["root_block_device.#"]; !ok {
|
|
is.Attributes["root_block_device.#"] = "0"
|
|
}
|
|
for _, oldBd := range oldBds {
|
|
if err := writeV1BlockDevice(is, oldBd); err != nil {
|
|
return is, err
|
|
}
|
|
}
|
|
log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes)
|
|
return is, nil
|
|
}
|
|
|
|
func readV0BlockDevices(is *terraform.InstanceState) (map[string]map[string]string, error) {
|
|
oldBds := make(map[string]map[string]string)
|
|
for k, v := range is.Attributes {
|
|
if !strings.HasPrefix(k, "block_device.") {
|
|
continue
|
|
}
|
|
path := strings.Split(k, ".")
|
|
if len(path) != 3 {
|
|
return oldBds, fmt.Errorf("Found unexpected block_device field: %#v", k)
|
|
}
|
|
hashcode, attribute := path[1], path[2]
|
|
oldBd, ok := oldBds[hashcode]
|
|
if !ok {
|
|
oldBd = make(map[string]string)
|
|
oldBds[hashcode] = oldBd
|
|
}
|
|
oldBd[attribute] = v
|
|
delete(is.Attributes, k)
|
|
}
|
|
return oldBds, nil
|
|
}
|
|
|
|
func writeV1BlockDevice(
|
|
is *terraform.InstanceState, oldBd map[string]string) error {
|
|
code := hashcode.String(oldBd["device_name"])
|
|
bdType := "ebs_block_device"
|
|
if vn, ok := oldBd["virtual_name"]; ok && strings.HasPrefix(vn, "ephemeral") {
|
|
bdType = "ephemeral_block_device"
|
|
} else if dn, ok := oldBd["device_name"]; ok && dn == "/dev/sda1" {
|
|
bdType = "root_block_device"
|
|
}
|
|
|
|
switch bdType {
|
|
case "ebs_block_device":
|
|
delete(oldBd, "virtual_name")
|
|
case "root_block_device":
|
|
delete(oldBd, "virtual_name")
|
|
delete(oldBd, "encrypted")
|
|
delete(oldBd, "snapshot_id")
|
|
case "ephemeral_block_device":
|
|
delete(oldBd, "delete_on_termination")
|
|
delete(oldBd, "encrypted")
|
|
delete(oldBd, "iops")
|
|
delete(oldBd, "volume_size")
|
|
delete(oldBd, "volume_type")
|
|
}
|
|
for attr, val := range oldBd {
|
|
attrKey := fmt.Sprintf("%s.%d.%s", bdType, code, attr)
|
|
is.Attributes[attrKey] = val
|
|
}
|
|
|
|
countAttr := fmt.Sprintf("%s.#", bdType)
|
|
count, _ := strconv.Atoi(is.Attributes[countAttr])
|
|
is.Attributes[countAttr] = strconv.Itoa(count + 1)
|
|
return nil
|
|
}
|