Skip to content

Commit

Permalink
Volumes management (#69)
Browse files Browse the repository at this point in the history
* Remove 'bootable' field (it was OpenStack field)

* Various fixes for volume resource

* Add custom volume import function
* Add 'network_id' when creating volume
* Disable volume resize until it's ready on Civo side
* Disable volume 'name' and 'network_id' change
* Docs update

* Set 'network_id' when reading volume

* Add 'region' field to volume attachment
  • Loading branch information
zulh-civo committed Aug 24, 2021
1 parent 97dacba commit 0dcfc1c
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 60 deletions.
163 changes: 109 additions & 54 deletions civo/resource_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package civo
import (
"fmt"
"log"
"time"

"github.com/civo/civogo"
"github.com/civo/terraform-provider-civo/internal/utils"
Expand All @@ -26,33 +25,29 @@ func resourceVolume() *schema.Resource {
Required: true,
Description: "A minimum of 1 and a maximum of your available disk space from your quota specifies the size of the volume in gigabytes ",
},
"bootable": {
Type: schema.TypeBool,
Required: true,
Description: "Mark the volume as bootable",
},
"region": {
Type: schema.TypeString,
Optional: true,
Description: "The region for the volume",
},
"network_id": {
Type: schema.TypeString,
Required: true,
Description: "The network that the volume belongs to",
},
// Computed resource
"mount_point": {
Type: schema.TypeString,
Computed: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
},
Create: resourceVolumeCreate,
Read: resourceVolumeRead,
Update: resourceVolumeUpdate,
Delete: resourceVolumeDelete,
//Exists: resourceExistsItem,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
State: resourceVolumeImport,
},
}
}
Expand All @@ -61,17 +56,23 @@ func resourceVolume() *schema.Resource {
func resourceVolumeCreate(d *schema.ResourceData, m interface{}) error {
apiClient := m.(*civogo.Client)

log.Printf("[INFO] configuring the volume %s", d.Get("name").(string))
config := &civogo.VolumeConfig{
Name: d.Get("name").(string),
SizeGigabytes: d.Get("size_gb").(int),
NetworkID: d.Get("network_id").(string),
}

// overwrite the region if is define in the datasource
if region, ok := d.GetOk("region"); ok {
apiClient.Region = region.(string)
currentRegion := region.(string)
apiClient.Region = currentRegion
config.Region = currentRegion
}

log.Printf("[INFO] configuring the volume %s", d.Get("name").(string))
config := &civogo.VolumeConfig{Name: d.Get("name").(string), SizeGigabytes: d.Get("size_gb").(int), Bootable: d.Get("bootable").(bool)}

volume, err := apiClient.NewVolume(config)
if err != nil {
return fmt.Errorf("[ERR] failed to create a new config: %s", err)
return fmt.Errorf("[ERR] failed to create a new volume: %s", err)
}

d.SetId(volume.ID)
Expand Down Expand Up @@ -99,59 +100,71 @@ func resourceVolumeRead(d *schema.ResourceData, m interface{}) error {
}

d.Set("name", resp.Name)
d.Set("network_id", resp.NetworkID)
d.Set("size_gb", resp.SizeGigabytes)
d.Set("bootable", resp.Bootable)
d.Set("mount_point", resp.MountPoint)
d.Set("created_at", resp.CreatedAt.UTC().String())

return nil
}

// function to update the volume
func resourceVolumeUpdate(d *schema.ResourceData, m interface{}) error {
apiClient := m.(*civogo.Client)
/*
apiClient := m.(*civogo.Client)
// overwrite the region if is define in the datasource
if region, ok := d.GetOk("region"); ok {
apiClient.Region = region.(string)
}
// overwrite the region if is define in the datasource
if region, ok := d.GetOk("region"); ok {
apiClient.Region = region.(string)
}
log.Printf("[INFO] retrieving the volume %s", d.Id())
resp, err := apiClient.FindVolume(d.Id())
if err != nil {
return fmt.Errorf("[ERR] failed retrieving the volume: %s", err)
}
log.Printf("[INFO] retrieving the volume %s", d.Id())
resp, err := apiClient.FindVolume(d.Id())
if err != nil {
return fmt.Errorf("[ERR] failed retrieving the volume: %s", err)
}
*/

if d.HasChange("size_gb") {
if resp.InstanceID != "" {
_, err := apiClient.DetachVolume(d.Id())
if err != nil {
return fmt.Errorf("[WARN] an error occurred while tring to detach volume %s, %s", d.Id(), err)
}

time.Sleep(10 * time.Second)

newSize := d.Get("size_gb").(int)
_, err = apiClient.ResizeVolume(d.Id(), newSize)
if err != nil {
return fmt.Errorf("[ERR] the volume (%s) size not change %s", d.Id(), err)
}

time.Sleep(2 * time.Second)

_, err = apiClient.AttachVolume(d.Id(), resp.InstanceID)
if err != nil {
return fmt.Errorf("[ERR] an error occurred while tring to attach the volume %s", d.Id())
return fmt.Errorf("[ERR] Resize operation is not available at this moment - we are working to re-enable it soon")

/*
if resp.InstanceID != "" {
_, err := apiClient.DetachVolume(d.Id())
if err != nil {
return fmt.Errorf("[WARN] an error occurred while trying to detach volume %s, %s", d.Id(), err)
}
time.Sleep(10 * time.Second)
newSize := d.Get("size_gb").(int)
_, err = apiClient.ResizeVolume(d.Id(), newSize)
if err != nil {
return fmt.Errorf("[ERR] the volume (%s) size not change %s", d.Id(), err)
}
time.Sleep(2 * time.Second)
_, err = apiClient.AttachVolume(d.Id(), resp.InstanceID)
if err != nil {
return fmt.Errorf("[ERR] an error occurred while tring to attach the volume %s", d.Id())
}
} else {
newSize := d.Get("size_gb").(int)
_, err = apiClient.ResizeVolume(d.Id(), newSize)
if err != nil {
return fmt.Errorf("[ERR] the volume (%s) size not change %s", d.Id(), err)
}
}
*/
}

} else {
newSize := d.Get("size_gb").(int)
_, err = apiClient.ResizeVolume(d.Id(), newSize)
if err != nil {
return fmt.Errorf("[ERR] the volume (%s) size not change %s", d.Id(), err)
}
}
if d.HasChange("network_id") {
return fmt.Errorf("[ERR] Network change for volume is not supported at this moment")
}

if d.HasChange("name") {
return fmt.Errorf("[ERR] Name change for volume is not supported at this moment")
}

return resourceVolumeRead(d, m)
Expand All @@ -173,3 +186,45 @@ func resourceVolumeDelete(d *schema.ResourceData, m interface{}) error {
}
return nil
}

// custom import to able to import a volume
func resourceVolumeImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
apiClient := m.(*civogo.Client)
regions, err := apiClient.ListRegions()
if err != nil {
return nil, err
}

volumeFound := false
for _, region := range regions {
if volumeFound {
break
}

currentRegion := region.Code
apiClient.Region = currentRegion

volumes, err := apiClient.ListVolumes()
if err != nil {
return nil, err
}

for _, volume := range volumes {
if volume.ID == d.Id() {
volumeFound = true
d.SetId(volume.ID)
d.Set("name", volume.Name)
d.Set("network_id", volume.NetworkID)
d.Set("region", currentRegion)
d.Set("size_gb", volume.SizeGigabytes)
d.Set("mount_point", volume.MountPoint)
}
}
}

if !volumeFound {
return nil, fmt.Errorf("[ERR] Volume %s not found", d.Id())
}

return []*schema.ResourceData{d}, nil
}
21 changes: 21 additions & 0 deletions civo/resource_volume_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ func resourceVolumeAttachment() *schema.Resource {
ForceNew: true,
ValidateFunc: validation.NoZeroValues,
},
"region": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: "The region for the volume attachment",
},
},
Create: resourceVolumeAttachmentCreate,
Read: resourceVolumeAttachmentRead,
Expand All @@ -39,6 +45,11 @@ func resourceVolumeAttachment() *schema.Resource {
func resourceVolumeAttachmentCreate(d *schema.ResourceData, m interface{}) error {
apiClient := m.(*civogo.Client)

// 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)

Expand Down Expand Up @@ -66,6 +77,11 @@ func resourceVolumeAttachmentCreate(d *schema.ResourceData, m interface{}) error
func resourceVolumeAttachmentRead(d *schema.ResourceData, m interface{}) error {
apiClient := m.(*civogo.Client)

// 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)

Expand All @@ -92,6 +108,11 @@ func resourceVolumeAttachmentRead(d *schema.ResourceData, m interface{}) error {
func resourceVolumeAttachmentDelete(d *schema.ResourceData, m interface{}) error {
apiClient := m.(*civogo.Client)

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

volumeID := d.Get("volume_id").(string)

log.Printf("[INFO] Detaching the volume %s", d.Id())
Expand Down
11 changes: 5 additions & 6 deletions docs/resources/volume.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ resource "civo_volume" "db" {

The following arguments are supported:

* `name` - (Required) A name that you wish to use to refer to this volume .
* `size_gb` - (Required) A minimum of 1 and a maximum of your available disk space from your quota specifies the size of the volume in gigabytes .
* `bootable` - (Required) Mark the volume as bootable.
* `name` - (Required) A name that you wish to use to refer to this volume.
* `size_gb` - (Required) A minimum of 1 and a maximum of your available disk space from your quota specifies the size of the volume in gigabytes.
* `network_id` - (Required) The network that the volume belongs to.

## Attributes Reference

Expand All @@ -35,9 +35,8 @@ The following attributes are exported:
* `id` - The unique identifier for the volume.
* `name` - Name of the volume.
* `size_gb` - The size of the volume.
* `bootable` - if is bootable or not.
* `mount_point` - The mount point of the volume.
* `created_at` - The date of the creation of the volume.
* `mount_point` - The mount point of the volume.
* `network_id` - The network that the volume belongs to.

## Import

Expand Down

0 comments on commit 0dcfc1c

Please sign in to comment.