terraform/builtin/providers/packet/resource_packet_volume.go

282 lines
6.8 KiB
Go

package packet
import (
"errors"
"fmt"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/packethost/packngo"
)
func resourcePacketVolume() *schema.Resource {
return &schema.Resource{
Create: resourcePacketVolumeCreate,
Read: resourcePacketVolumeRead,
Update: resourcePacketVolumeUpdate,
Delete: resourcePacketVolumeDelete,
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"project_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Required: false,
Optional: true,
},
"size": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"facility": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"plan": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"billing_cycle": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Optional: true,
},
"state": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"locked": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
"snapshot_policies": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"snapshot_frequency": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"snapshot_count": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
},
},
},
"attachments": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"href": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
},
},
"created": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"updated": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourcePacketVolumeCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*packngo.Client)
createRequest := &packngo.VolumeCreateRequest{
PlanID: d.Get("plan").(string),
FacilityID: d.Get("facility").(string),
ProjectID: d.Get("project_id").(string),
Size: d.Get("size").(int),
}
if attr, ok := d.GetOk("billing_cycle"); ok {
createRequest.BillingCycle = attr.(string)
} else {
createRequest.BillingCycle = "hourly"
}
if attr, ok := d.GetOk("description"); ok {
createRequest.Description = attr.(string)
}
snapshot_count := d.Get("snapshot_policies.#").(int)
if snapshot_count > 0 {
createRequest.SnapshotPolicies = make([]*packngo.SnapshotPolicy, 0, snapshot_count)
for i := 0; i < snapshot_count; i++ {
policy := new(packngo.SnapshotPolicy)
policy.SnapshotFrequency = d.Get(fmt.Sprintf("snapshot_policies.%d.snapshot_frequency", i)).(string)
policy.SnapshotCount = d.Get(fmt.Sprintf("snapshot_policies.%d.snapshot_count", i)).(int)
createRequest.SnapshotPolicies = append(createRequest.SnapshotPolicies, policy)
}
}
newVolume, _, err := client.Volumes.Create(createRequest)
if err != nil {
return friendlyError(err)
}
d.SetId(newVolume.ID)
_, err = waitForVolumeAttribute(d, "active", []string{"queued", "provisioning"}, "state", meta)
if err != nil {
if isForbidden(err) {
// If the volume doesn't get to the active state, we can't recover it from here.
d.SetId("")
return errors.New("provisioning time limit exceeded; the Packet team will investigate")
}
return err
}
return resourcePacketVolumeRead(d, meta)
}
func waitForVolumeAttribute(d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) {
stateConf := &resource.StateChangeConf{
Pending: pending,
Target: []string{target},
Refresh: newVolumeStateRefreshFunc(d, attribute, meta),
Timeout: 60 * time.Minute,
Delay: 10 * time.Second,
MinTimeout: 3 * time.Second,
}
return stateConf.WaitForState()
}
func newVolumeStateRefreshFunc(d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc {
client := meta.(*packngo.Client)
return func() (interface{}, string, error) {
if err := resourcePacketVolumeRead(d, meta); err != nil {
return nil, "", err
}
if attr, ok := d.GetOk(attribute); ok {
volume, _, err := client.Volumes.Get(d.Id())
if err != nil {
return nil, "", friendlyError(err)
}
return &volume, attr.(string), nil
}
return nil, "", nil
}
}
func resourcePacketVolumeRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*packngo.Client)
volume, _, err := client.Volumes.Get(d.Id())
if err != nil {
err = friendlyError(err)
// If the volume somehow already destroyed, mark as succesfully gone.
if isNotFound(err) {
d.SetId("")
return nil
}
return err
}
d.Set("name", volume.Name)
d.Set("description", volume.Description)
d.Set("size", volume.Size)
d.Set("plan", volume.Plan.Slug)
d.Set("facility", volume.Facility.Code)
d.Set("state", volume.State)
d.Set("billing_cycle", volume.BillingCycle)
d.Set("locked", volume.Locked)
d.Set("created", volume.Created)
d.Set("updated", volume.Updated)
snapshot_policies := make([]map[string]interface{}, 0, len(volume.SnapshotPolicies))
for _, snapshot_policy := range volume.SnapshotPolicies {
policy := map[string]interface{}{
"snapshot_frequency": snapshot_policy.SnapshotFrequency,
"snapshot_count": snapshot_policy.SnapshotCount,
}
snapshot_policies = append(snapshot_policies, policy)
}
d.Set("snapshot_policies", snapshot_policies)
attachments := make([]*packngo.Attachment, 0, len(volume.Attachments))
for _, attachment := range volume.Attachments {
attachments = append(attachments, attachment)
}
d.Set("attachments", attachments)
return nil
}
func resourcePacketVolumeUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*packngo.Client)
updateRequest := &packngo.VolumeUpdateRequest{
ID: d.Get("id").(string),
}
if attr, ok := d.GetOk("description"); ok {
updateRequest.Description = attr.(string)
}
if attr, ok := d.GetOk("plan"); ok {
updateRequest.Plan = attr.(string)
}
_, _, err := client.Volumes.Update(updateRequest)
if err != nil {
return friendlyError(err)
}
return resourcePacketVolumeRead(d, meta)
}
func resourcePacketVolumeDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*packngo.Client)
if _, err := client.Volumes.Delete(d.Id()); err != nil {
return friendlyError(err)
}
return nil
}