Skip to content

Commit

Permalink
Support attaching/detaching volumes on create/update instance
Browse files Browse the repository at this point in the history
Signed-off-by: Vishal Anarase <[email protected]>
  • Loading branch information
vishalanarase committed Sep 5, 2024
1 parent b834b4d commit de35c8c
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 10 deletions.
26 changes: 26 additions & 0 deletions civo/instances/datasource_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ func DataSourceInstance() *schema.Resource {
Computed: true,
Description: "An optional list of tags",
},
"attached_volume": {
Type: schema.TypeList,
Optional: true,
Description: "A list of volumes to attached at boot to the instance.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
Description: "The ID of the volume to attach.",
},
},
},
},
"script": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -192,5 +206,17 @@ func dataSourceInstanceRead(_ context.Context, d *schema.ResourceData, m interfa
d.Set("created_at", foundImage.CreatedAt.UTC().String())
d.Set("notes", foundImage.Notes)

if len(foundImage.AttachedVolume) > 0 {
volumes := make([]map[string]interface{}, 0, len(foundImage.AttachedVolume))
for _, volume := range foundImage.AttachedVolume {
volumeMap := map[string]interface{}{
"id": volume.ID,
}
volumes = append(volumes, volumeMap)
}

d.Set("attached_volume", volumes)
}

return nil
}
41 changes: 40 additions & 1 deletion civo/instances/resource_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package instances
import (
"context"
"errors"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"log"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"

"github.com/civo/civogo"
"github.com/civo/terraform-provider-civo/internal/utils"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand Down Expand Up @@ -98,6 +99,20 @@ func ResourceInstance() *schema.Resource {
Description: "An optional list of tags, represented as a key, value pair",
Elem: &schema.Schema{Type: schema.TypeString},
},
"attached_volume": {
Type: schema.TypeList,
Optional: true,
Description: "A list of volumes to attached at boot to the instance.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
Description: "The ID of the volume to attach.",
},
},
},
},
"script": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -274,6 +289,18 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter

config.Tags = tags

tfVolumeAttach := d.Get("attached_volume").([]interface{})
volumes := make([]civogo.AttachedVolume, 0, len(tfVolumeAttach))
for _, v := range tfVolumeAttach {
volumeData := v.(map[string]interface{})
volumes = append(volumes, civogo.AttachedVolume{
ID: volumeData["id"].(string),
})
}
if len(volumes) > 0 {
config.AttachedVolume = volumes
}

log.Printf("[INFO] creating the instance %s", d.Get("hostname").(string))

instance, err := apiClient.CreateInstance(config)
Expand Down Expand Up @@ -361,6 +388,18 @@ func resourceInstanceRead(_ context.Context, d *schema.ResourceData, m interface
d.Set("initial_password", "")
}

if len(resp.AttachedVolume) > 0 {
volumes := make([]map[string]interface{}, 0, len(resp.AttachedVolume))
for _, volume := range resp.AttachedVolume {
volumeMap := map[string]interface{}{
"id": volume.ID,
}
volumes = append(volumes, volumeMap)
}

d.Set("attached_volume", volumes)
}

d.Set("hostname", resp.Hostname)
d.Set("reverse_dns", resp.ReverseDNS)
d.Set("size", resp.Size)
Expand Down
55 changes: 49 additions & 6 deletions civo/volume/resource_volume_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func ResourceVolumeAttachment() *schema.Resource {
ValidateFunc: validation.NoZeroValues,
Description: "The ID of target volume for attachment",
},
"attach_at_boot": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Description: "The volume is attach at boot time",
},
"region": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -49,14 +55,15 @@ func ResourceVolumeAttachment() *schema.Resource {
// function to create the new volume
func resourceVolumeAttachmentCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
apiClient := m.(*civogo.Client)

var diags diag.Diagnostics
// overwrite the region if it's defined
if region, ok := d.GetOk("region"); ok {
apiClient.Region = region.(string)
}

instanceID := d.Get("instance_id").(string)
volumeID := d.Get("volume_id").(string)
attachAtBoot := d.Get("attach_at_boot").(bool)

log.Printf("[INFO] retrieving the volume %s", volumeID)
volume, err := apiClient.FindVolume(volumeID)
Expand All @@ -66,7 +73,22 @@ func resourceVolumeAttachmentCreate(ctx context.Context, d *schema.ResourceData,

if volume.InstanceID == "" || volume.InstanceID != instanceID {
log.Printf("[INFO] attaching the volume %s to instance %s", volumeID, instanceID)
_, err := apiClient.AttachVolume(volumeID, instanceID)
vuc := civogo.VolumeAttachConfig{
InstanceID: instanceID,
Region: apiClient.Region,
}
if attachAtBoot {
// Notify the terminal
msg := fmt.Sprintf("To use the volume %s, The instance %s needs to be rebooted", volumeID, instanceID)
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: msg,
})

vuc.AttachAtBoot = true
}

_, err := apiClient.AttachVolume(volumeID, vuc)
if err != nil {
return diag.Errorf("[ERR] error attaching volume to instance %s", err)
}
Expand All @@ -89,12 +111,22 @@ func resourceVolumeAttachmentCreate(ctx context.Context, d *schema.ResourceData,
MinTimeout: 3 * time.Second,
NotFoundChecks: 10,
}
_, err = createStateConf.WaitForStateContext(context.Background())
if attachAtBoot {
createStateConf.Pending = []string{"available", "attaching"}
createStateConf.Target = []string{"attaching"}
}

ctx, cancel := context.WithTimeout(context.Background(), createStateConf.Timeout)
defer cancel()

_, err = createStateConf.WaitForStateContext(ctx)
if err != nil {
return diag.Errorf("error waiting for volume (%s) to be attached: %s", d.Id(), err)
}

return resourceVolumeAttachmentRead(ctx, d, m)
ret := resourceVolumeAttachmentRead(ctx, d, m)
diags = append(diags, ret...)
return diags
}

// function to read the volume
Expand Down Expand Up @@ -131,18 +163,29 @@ func resourceVolumeAttachmentRead(_ context.Context, d *schema.ResourceData, m i
// function to delete the volume
func resourceVolumeAttachmentDelete(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
apiClient := m.(*civogo.Client)

var diags diag.Diagnostics
// overwrite the region if it's defined
if region, ok := d.GetOk("region"); ok {
apiClient.Region = region.(string)
}

volumeID := d.Get("volume_id").(string)
attachAtBoot := d.Get("attach_at_boot").(bool)
instanceID := d.Get("instance_id").(string)

if attachAtBoot {
// Notify the terminal
msg := fmt.Sprintf("To use the volume %s, The instance %s needs to be rebooted", volumeID, instanceID)
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: msg,
})
}

log.Printf("[INFO] Detaching the volume %s", d.Id())
_, err := apiClient.DetachVolume(volumeID)
if err != nil {
return diag.Errorf("[ERR] an error occurred while trying to detach the volume %s", err)
}
return nil
return diags
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module github.com/civo/terraform-provider-civo

require (
github.com/civo/civogo v0.3.73
github.com/civo/civogo v0.3.75
github.com/google/uuid v1.3.1
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmms
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/civo/civogo v0.3.73 h1:thkNnkziU+xh+MEOChIUwRZI1forN20+SSAPe/VFDME=
github.com/civo/civogo v0.3.73/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
github.com/civo/civogo v0.3.75 h1:hrF+ALGDV5Be/jG9NmDo2wLhL4yuD8kIOxUbVRklGNU=
github.com/civo/civogo v0.3.75/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
Expand Down

0 comments on commit de35c8c

Please sign in to comment.