Skip to content

Commit

Permalink
feat(resource/virtual_machine): add support for setting number and si…
Browse files Browse the repository at this point in the history
…ze of disks during creation

Adds a repeatable "disk" block with "name" and "size" fields which are
used to define what disks should be created and attached to the VM
during creation.

Due to this not being a full disk resource implementation, but a
creation-only property of virtual machines, any changes to disk blocks
after VM creation will make Terraform want to replace the whole VM.

Fully managing disks as separate resources is planned, but out of scope
of this change.
  • Loading branch information
jimeh committed Jan 26, 2023
1 parent fe3ad79 commit 94f42ca
Show file tree
Hide file tree
Showing 7 changed files with 2,234 additions and 1 deletion.
24 changes: 24 additions & 0 deletions docs/resources/virtual_machine.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ resource "katapult_virtual_machine" "base" {
install_agent = true
}
# First defined disk becomes the boot disk that the OS is installed on.
disk {
name = "System Disk" # Optional
size = 20 # GB
}
disk {
name = "Data" # Optional
size = 100 # GB
}
ip_address_ids = [
katapult_ip.web-2.id,
katapult_ip.web-2-internal.id
Expand All @@ -70,6 +81,7 @@ resource "katapult_virtual_machine" "base" {
### Optional

- `description` (String)
- `disk` (Block List) Specify one or more disks with custom sizes to create and attach to the Virtual Machine during creation. First defined disk will be used as the boot disk. If no disks are defined, a single disk will be created based on the chosen package. (see [below for nested schema](#nestedblock--disk))
- `disk_template_options` (Map of String)
- `group_id` (String)
- `hostname` (String)
Expand All @@ -85,6 +97,18 @@ resource "katapult_virtual_machine" "base" {
- `ip_addresses` (Set of String)
- `state` (String)

<a id="nestedblock--disk"></a>
### Nested Schema for `disk`

Required:

- `size` (Number) Size of the disk in GB.

Optional:

- `name` (String) Name of the disk.


<a id="nestedblock--timeouts"></a>
### Nested Schema for `timeouts`

Expand Down
11 changes: 11 additions & 0 deletions examples/resources/katapult_virtual_machine/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ resource "katapult_virtual_machine" "base" {
install_agent = true
}

# First defined disk becomes the boot disk that the OS is installed on.
disk {
name = "System Disk" # Optional
size = 20 # GB
}

disk {
name = "Data" # Optional
size = 100 # GB
}

ip_address_ids = [
katapult_ip.web-2.id,
katapult_ip.web-2-internal.id
Expand Down
3 changes: 3 additions & 0 deletions internal/provider/data_source_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ func dataSourceVirtualMachine() *schema.Resource {

ds["fqdn"].Optional = true

// Remove creation-only fields which cannot be read back from the API.
delete(ds, "disk")

return &schema.Resource{
ReadContext: dataSourceVirtualMachineRead,
Schema: ds,
Expand Down
51 changes: 50 additions & 1 deletion internal/provider/resource_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"github.com/krystal/go-katapult/core"
)

func resourceVirtualMachine() *schema.Resource {
func resourceVirtualMachine() *schema.Resource { //nolint:funlen
return &schema.Resource{
CreateContext: resourceVirtualMachineCreate,
ReadContext: resourceVirtualMachineRead,
Expand Down Expand Up @@ -77,6 +77,32 @@ func resourceVirtualMachine() *schema.Resource {
Type: schema.TypeString,
},
},
"disk": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Description: "Specify one or more disks with custom sizes to " +
"create and attach to the Virtual Machine during " +
"creation. First defined disk will be used as the boot " +
"disk. If no disks are defined, a single disk will be " +
"created based on the chosen package.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "Name of the disk.",
},
"size": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "Size of the disk in GB.",
},
},
},
},
"ip_address_ids": {
Type: schema.TypeSet,
Description: "One or more IP IDs.",
Expand Down Expand Up @@ -207,6 +233,29 @@ func resourceVirtualMachineCreate(
}
}

if rawDisks, ok := d.GetOk("disk"); ok {
for i, rawDisk := range rawDisks.([]interface{}) {
disk := rawDisk.(map[string]interface{})
var name string
if diskName, ok := disk["name"]; ok {
name = diskName.(string)
}

if name == "" {
if i == 0 {
name = "System Disk"
} else {
name = fmt.Sprintf("Disk #%d", i+1)
}
}

spec.SystemDisks = append(spec.SystemDisks, &buildspec.SystemDisk{
Name: name,
Size: disk["size"].(int),
})
}
}

ipGroups := map[string][]*core.IPAddress{}
for _, rawIP := range d.Get("ip_address_ids").(*schema.Set).List() {
ip, _, err := m.Core.IPAddresses.GetByID(ctx, rawIP.(string))
Expand Down
140 changes: 140 additions & 0 deletions internal/provider/resource_virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,146 @@ func TestAccKatapultVirtualMachine_basic(t *testing.T) {
})
}

// TODO: Update this test to verify that the VM was created with the correct set
// of disks when go-katapult API client gains support for fetching VM disk
// details.
func TestAccKatapultVirtualMachine_custom_disks(t *testing.T) {
tt := newTestTools(t)

name := tt.ResourceName("custom-disks")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeAggregateTestCheckFunc(
testAccCheckKatapultVirtualMachineDestroy(tt),
testAccCheckKatapultIPDestroy(tt),
),
Steps: []resource.TestStep{
{
Config: undent.Stringf(`
resource "katapult_ip" "web" {}
resource "katapult_ip" "internal" {}
resource "katapult_virtual_machine_group" "web" {
name = "%s"
}
resource "katapult_virtual_machine" "base" {
name = "%s"
hostname = "%s"
description = "A web server."
package = "rock-3"
disk_template = "ubuntu-18-04"
disk_template_options = {
install_agent = true
}
group_id = katapult_virtual_machine_group.web.id
ip_address_ids = [
katapult_ip.web.id,
katapult_ip.internal.id
]
tags = ["web", "public"]
network_speed_profile = "1gbps"
disk {
name = "System"
size = 20
}
disk {
name = "Data"
size = 10
}
disk { # Automatically named "Disk #3"
size = 10
}
}`,
name+"-group", name, name+"-host",
),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckKatapultVirtualMachineExists(
tt, "katapult_virtual_machine.base",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"name", name,
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"hostname", name+"-host",
),
resource.TestMatchResourceAttr(
"katapult_virtual_machine.base",
"fqdn", regexp.MustCompile(
fmt.Sprintf(
`^%s\..+$`,
regexp.QuoteMeta(name+"-host"),
),
),
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"description", "A web server.",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"package", "rock-3",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"disk_template", "ubuntu-18-04",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"disk_template_options.install_agent", "true",
),
resource.TestCheckResourceAttrPair(
"katapult_virtual_machine.base", "group_id",
"katapult_virtual_machine_group.web", "id",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"ip_address_ids.#", "2",
),
resource.TestCheckTypeSetElemAttrPair(
"katapult_virtual_machine.base", "ip_address_ids.*",
"katapult_ip.web", "id",
),
resource.TestCheckTypeSetElemAttrPair(
"katapult_virtual_machine.base", "ip_address_ids.*",
"katapult_ip.internal", "id",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"ip_addresses.#", "2",
),
resource.TestCheckTypeSetElemAttrPair(
"katapult_virtual_machine.base", "ip_addresses.*",
"katapult_ip.web", "address",
),
resource.TestCheckTypeSetElemAttrPair(
"katapult_virtual_machine.base", "ip_addresses.*",
"katapult_ip.internal", "address",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"tags.#", "2",
),
resource.TestCheckTypeSetElemAttr(
"katapult_virtual_machine.base", "tags.*", "web",
),
resource.TestCheckTypeSetElemAttr(
"katapult_virtual_machine.base", "tags.*", "public",
),
resource.TestCheckResourceAttr(
"katapult_virtual_machine.base",
"network_speed_profile", "1gbps",
),
),
},
},
})
}

func TestAccKatapultVirtualMachine_update(t *testing.T) {
tt := newTestTools(t)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mj1luon2gbp9
Loading

0 comments on commit 94f42ca

Please sign in to comment.