From 79fa978896fb4ebd6562c75b33d44382c867eb6b Mon Sep 17 00:00:00 2001 From: Aaron Welch Date: Thu, 11 Aug 2016 09:40:23 -0700 Subject: [PATCH] working volume resource and test, website docs updated --- .../packet/resource_packet_volume.go | 97 ++++++++++++++----- .../packet/resource_packet_volume_test.go | 1 + .../docs/providers/packet/r/volume.html | 60 ++++++++++++ website/source/layouts/packet.erb | 3 + 4 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 website/source/docs/providers/packet/r/volume.html diff --git a/builtin/providers/packet/resource_packet_volume.go b/builtin/providers/packet/resource_packet_volume.go index d212a1561..c5dc0a887 100644 --- a/builtin/providers/packet/resource_packet_volume.go +++ b/builtin/providers/packet/resource_packet_volume.go @@ -1,8 +1,11 @@ package packet import ( + "errors" "fmt" + "time" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/packethost/packngo" ) @@ -39,9 +42,7 @@ func resourcePacketVolume() *schema.Resource { "size": &schema.Schema{ Type: schema.TypeInt, - Required: false, - Optional: true, - Computed: true, + Required: true, }, "facility": &schema.Schema{ @@ -53,13 +54,12 @@ func resourcePacketVolume() *schema.Resource { "plan": &schema.Schema{ Type: schema.TypeString, Required: true, - ForceNew: true, }, "billing_cycle": &schema.Schema{ Type: schema.TypeString, - Required: true, - ForceNew: true, + Computed: true, + Optional: true, }, "state": &schema.Schema{ @@ -69,7 +69,7 @@ func resourcePacketVolume() *schema.Resource { "locked": &schema.Schema{ Type: schema.TypeBool, - Computed: true, + Optional: true, }, "snapshot_policies": &schema.Schema{ @@ -121,26 +121,30 @@ 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), - BillingCycle: d.Get("billing_cycle").(string), - ProjectID: d.Get("project_id").(string), + 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) } - if attr, ok := d.GetOk("size"); ok { - createRequest.Size = attr.(int) - } - - snapshot_policies := d.Get("snapshot_policies.#").(int) - if snapshot_policies > 0 { - createRequest.SnapshotPolicies = make([]*packngo.SnapshotPolicy, 0, snapshot_policies) - for i := 0; i < snapshot_policies; i++ { - key := fmt.Sprintf("snapshot_policies.%d", i) - createRequest.SnapshotPolicies = append(createRequest.SnapshotPolicies, d.Get(key).(*packngo.SnapshotPolicy)) + 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) } } @@ -151,9 +155,52 @@ func resourcePacketVolumeCreate(d *schema.ResourceData, meta interface{}) error 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) @@ -181,9 +228,13 @@ func resourcePacketVolumeRead(d *schema.ResourceData, meta interface{}) error { d.Set("created", volume.Created) d.Set("updated", volume.Updated) - snapshot_policies := make([]*packngo.SnapshotPolicy, 0, len(volume.SnapshotPolicies)) + snapshot_policies := make([]map[string]interface{}, 0, len(volume.SnapshotPolicies)) for _, snapshot_policy := range volume.SnapshotPolicies { - snapshot_policies = append(snapshot_policies, snapshot_policy) + 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) diff --git a/builtin/providers/packet/resource_packet_volume_test.go b/builtin/providers/packet/resource_packet_volume_test.go index 1cf316a72..cffd55f13 100644 --- a/builtin/providers/packet/resource_packet_volume_test.go +++ b/builtin/providers/packet/resource_packet_volume_test.go @@ -99,4 +99,5 @@ resource "packet_volume" "foobar" { size = 100 project_id = "%s" facility = "%s" + snapshot_policies = { snapshot_frequency = "1day", snapshot_count = 7 } }` diff --git a/website/source/docs/providers/packet/r/volume.html b/website/source/docs/providers/packet/r/volume.html new file mode 100644 index 000000000..3179518ea --- /dev/null +++ b/website/source/docs/providers/packet/r/volume.html @@ -0,0 +1,60 @@ +--- +layout: "packet" +page_title: "Packet: packet_volume" +sidebar_current: "docs-packet-resource-volume" +description: |- + Provides a Packet Block Storage Volume Resource. +--- + +# packet\_volume + +Provides a Packet Block Storage Volume resource to allow you to +manage block volumes on your account. +Once created by Terraform, they must then be attached and mounted +using the api and `packet_block_attach` and `packet_block_detach` +scripts. + +## Example Usage + +``` +# Create a new block volume +resource "packet_volume" "volume1" { + description = "terraform-volume-1" + facility = "ewr1" + project_id = "${packet_project.cool_project.id}" + plan = 'storage_1' + size = 100 + billing_cycle = "hourly" + snapshot_policies = { snapshot_frequency = "1day", snapshot_count = 7 } + snapshot_policies = { snapshot_frequency = "1month", snapshot_count = 6 } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `plan` - (Required) The service plan slug of the volume +* `facility` - (Required) The facility to create the volume in +* `project_id` - (Required) The packet project ID to deploy the volume in +* `size` - (Required) The size in GB to make the volume +* `billing_cycle` - The billing cycle, defaults to "hourly" +* `description` - Optional description for the volume +* `snapshot_policies` - Optional list of snapshot policies + +## Attributes Reference + +The following attributes are exported: + +* `id` - The unique ID of the volume +* `name` - The name of the volume +* `description` - The description of the volume +* `size` - The size in GB of the volume +* `plan` - Performance plan the volume is on +* `billing_cycle` - The billing cycle, defaults to hourly +* `facility` - The facility slug the volume resides in +* `state` - The state of the volume +* `locked` - Whether the volume is locked or not +* `project_id ` - The project id the volume is in +* `created` - The timestamp for when the volume was created +* `updated` - The timestamp for the last time the volume was updated diff --git a/website/source/layouts/packet.erb b/website/source/layouts/packet.erb index f7464f198..8591e23e3 100644 --- a/website/source/layouts/packet.erb +++ b/website/source/layouts/packet.erb @@ -22,6 +22,9 @@ > packet_ssh_key + > + packet_volume +