diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go index 9cd6c525b..028dbd24a 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go @@ -111,6 +111,9 @@ func resourceVSphereVirtualMachine() *schema.Resource { Update: resourceVSphereVirtualMachineUpdate, Delete: resourceVSphereVirtualMachineDelete, + SchemaVersion: 1, + MigrateState: resourceVSphereVirtualMachineMigrateState, + Schema: map[string]*schema.Schema{ "name": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine_migrate.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine_migrate.go new file mode 100644 index 000000000..e062e61b1 --- /dev/null +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine_migrate.go @@ -0,0 +1,58 @@ +package vsphere + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/terraform" +) + +func resourceVSphereVirtualMachineMigrateState( + v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { + if is.Empty() { + log.Println("[DEBUG] Empty InstanceState; nothing to migrate.") + return is, nil + } + + switch v { + case 0: + log.Println("[INFO] Found Compute Instance State v0; migrating to v1") + is, err := migrateVSphereVirtualMachineStateV0toV1(is) + if err != nil { + return is, err + } + return is, nil + default: + return is, fmt.Errorf("Unexpected schema version: %d", v) + } +} + +func migrateVSphereVirtualMachineStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { + if is.Empty() || is.Attributes == nil { + log.Println("[DEBUG] Empty VSphere Virtual Machine State; nothing to migrate.") + return is, nil + } + + log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes) + + if is.Attributes["skip_customization"] == "" { + is.Attributes["skip_customization"] = "false" + } + + for k, _ := range is.Attributes { + if strings.HasPrefix(k, "disk.") && strings.HasSuffix(k, ".size") { + diskParts := strings.Split(k, ".") + if len(diskParts) != 3 { + continue + } + s := strings.Join([]string{diskParts[0], diskParts[1], "controller_type"}, ".") + if _, ok := is.Attributes[s]; !ok { + is.Attributes[s] = "scsi" + } + } + } + + log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes) + return is, nil +} diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine_migrate_test.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine_migrate_test.go new file mode 100644 index 000000000..9f2cbe332 --- /dev/null +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine_migrate_test.go @@ -0,0 +1,85 @@ +package vsphere + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestVSphereVirtualMachineMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + Attributes map[string]string + Expected map[string]string + Meta interface{} + }{ + "skip_customization before 0.6.16": { + StateVersion: 0, + Attributes: map[string]string{}, + Expected: map[string]string{ + "skip_customization": "false", + }, + }, + "disk controller_type": { + StateVersion: 0, + Attributes: map[string]string{ + "disk.1234.size": "0", + "disk.5678.size": "0", + "disk.9999.size": "0", + "disk.9999.controller_type": "ide", + }, + Expected: map[string]string{ + "disk.1234.size": "0", + "disk.1234.controller_type": "scsi", + "disk.5678.size": "0", + "disk.5678.controller_type": "scsi", + "disk.9999.size": "0", + "disk.9999.controller_type": "ide", + }, + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: "i-abc123", + Attributes: tc.Attributes, + } + is, err := resourceVSphereVirtualMachineMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + for k, v := range tc.Expected { + if is.Attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, v, k, is.Attributes[k], is.Attributes) + } + } + } +} + +func TestComputeInstanceMigrateState_empty(t *testing.T) { + var is *terraform.InstanceState + var meta interface{} + + // should handle nil + is, err := resourceVSphereVirtualMachineMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } + if is != nil { + t.Fatalf("expected nil instancestate, got: %#v", is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceVSphereVirtualMachineMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } +}