From 3099fc7b1c7bbdab9605787ca1e318888cc67fa0 Mon Sep 17 00:00:00 2001 From: Ayu Demura Date: Thu, 21 Jul 2016 15:57:49 -0400 Subject: [PATCH 01/13] add volume --- builtin/providers/packet/provider.go | 1 + .../packet/resource_packet_device.go | 4 +- .../packet/resource_packet_volume.go | 225 ++++++++++++++++++ .../packet/resource_packet_volume_test.go | 86 +++++++ .../github.com/packethost/packngo/README.md | 9 + .../github.com/packethost/packngo/devices.go | 13 - vendor/github.com/packethost/packngo/email.go | 11 +- .../packethost/packngo/facilities.go | 17 +- vendor/github.com/packethost/packngo/ip.go | 198 +++++++++++++++ .../packethost/packngo/operatingsystems.go | 5 +- .../github.com/packethost/packngo/packngo.go | 19 +- vendor/github.com/packethost/packngo/plans.go | 20 +- .../github.com/packethost/packngo/projects.go | 47 ++++ vendor/github.com/packethost/packngo/rate.go | 7 +- .../github.com/packethost/packngo/sshkeys.go | 29 ++- vendor/github.com/packethost/packngo/user.go | 33 +-- .../github.com/packethost/packngo/volumes.go | 146 ++++++++++++ vendor/vendor.json | 4 +- 18 files changed, 802 insertions(+), 72 deletions(-) create mode 100644 builtin/providers/packet/resource_packet_volume.go create mode 100644 builtin/providers/packet/resource_packet_volume_test.go create mode 100644 vendor/github.com/packethost/packngo/README.md create mode 100644 vendor/github.com/packethost/packngo/ip.go create mode 100644 vendor/github.com/packethost/packngo/volumes.go diff --git a/builtin/providers/packet/provider.go b/builtin/providers/packet/provider.go index 82f7dbf77d..f3a8483370 100644 --- a/builtin/providers/packet/provider.go +++ b/builtin/providers/packet/provider.go @@ -21,6 +21,7 @@ func Provider() terraform.ResourceProvider { "packet_device": resourcePacketDevice(), "packet_ssh_key": resourcePacketSSHKey(), "packet_project": resourcePacketProject(), + "packet_volume": resourcePacketVolume(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/packet/resource_packet_device.go b/builtin/providers/packet/resource_packet_device.go index 2c6e3de548..adae42575a 100644 --- a/builtin/providers/packet/resource_packet_device.go +++ b/builtin/providers/packet/resource_packet_device.go @@ -208,13 +208,13 @@ func resourcePacketDeviceRead(d *schema.ResourceData, meta interface{}) error { network := map[string]interface{}{ "address": ip.Address, "gateway": ip.Gateway, - "family": ip.Family, + "family": ip.AddressFamily, "cidr": ip.Cidr, "public": ip.Public, } networks = append(networks, network) - if ip.Family == 4 && ip.Public == true { + if ip.AddressFamily == 4 && ip.Public == true { host = ip.Address } } diff --git a/builtin/providers/packet/resource_packet_volume.go b/builtin/providers/packet/resource_packet_volume.go new file mode 100644 index 0000000000..f17f03c4f9 --- /dev/null +++ b/builtin/providers/packet/resource_packet_volume.go @@ -0,0 +1,225 @@ +package packet + +import ( + "fmt" + + "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{ + "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: false, + Optional: true, + Computed: true, + }, + + "facility": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "plan": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "billing_cycle": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "state": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "locked": &schema.Schema{ + Type: schema.TypeBool, + Computed: 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), + BillingCycle: d.Get("billing_cycle").(string), + ProjectID: d.Get("project_id").(string), + } + + 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)) + } + } + + newVolume, _, err := client.Volumes.Create(createRequest) + if err != nil { + return friendlyError(err) + } + + d.SetId(newVolume.ID) + + return resourcePacketVolumeRead(d, meta) +} + +func resourcePacketVolumeRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*packngo.Client) + + device, _, 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", device.Name) + d.Set("description", device.Description) + d.Set("size", device.Size) + d.Set("plan", device.Plan.Slug) + d.Set("facility", device.Facility.Code) + d.Set("state", device.State) + d.Set("billing_cycle", device.BillingCycle) + d.Set("locked", device.Locked) + d.Set("created", device.Created) + d.Set("updated", device.Updated) + + snapshot_policies := make([]*packngo.SnapshotPolicy, 0, len(device.SnapshotPolicies)) + for _, snapshot_policy := range device.SnapshotPolicies { + snapshot_policies = append(snapshot_policies, snapshot_policy) + } + d.Set("snapshot_policies", snapshot_policies) + + attachments := make([]*packngo.Attachment, 0, len(device.Attachments)) + for _, attachment := range device.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 +} diff --git a/builtin/providers/packet/resource_packet_volume_test.go b/builtin/providers/packet/resource_packet_volume_test.go new file mode 100644 index 0000000000..9d866e43b7 --- /dev/null +++ b/builtin/providers/packet/resource_packet_volume_test.go @@ -0,0 +1,86 @@ +package packet + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/packethost/packngo" +) + +func TestAccPacketVolume_Basic(t *testing.T) { + var volume packngo.Volume + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPacketVolumeDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckPacketVolumeConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckPacketVolumeExists("packet_volume.foobar", &volume), + testAccCheckPacketVolumeAttributes(&volume), + resource.TestCheckResourceAttr( + "packet_volume.foobar", "name", "foobar"), + ), + }, + }, + }) +} + +func testAccCheckPacketVolumeDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*packngo.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "packet_volume" { + continue + } + if _, _, err := client.Volumes.Get(rs.Primary.ID); err == nil { + return fmt.Errorf("Volume cstill exists") + } + } + + return nil +} + +func testAccCheckPacketVolumeAttributes(volume *packngo.Volume) resource.TestCheckFunc { + return func(s *terraform.State) error { + if volume.Name != "foobar" { + return fmt.Errorf("Bad name: %s", volume.Name) + } + return nil + } +} + +func testAccCheckPacketVolumeExists(n string, volume *packngo.Volume) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + client := testAccProvider.Meta().(*packngo.Client) + + foundVolume, _, err := client.Volumes.Get(rs.Primary.ID) + if err != nil { + return err + } + if foundVolume.ID != rs.Primary.ID { + return fmt.Errorf("Record not found: %v - %v", rs.Primary.ID, foundVolume) + } + + *volume = *foundVolume + + return nil + } +} + +var testAccCheckPacketVolumeConfig_basic = fmt.Sprintf(` +resource "packet_volume" "foobar" { + name = "foobar" +}`) diff --git a/vendor/github.com/packethost/packngo/README.md b/vendor/github.com/packethost/packngo/README.md new file mode 100644 index 0000000000..d359a10d65 --- /dev/null +++ b/vendor/github.com/packethost/packngo/README.md @@ -0,0 +1,9 @@ +# packngo +Packet Go Api Client + +![](https://www.packet.net/media/labs/images/1679091c5a880faf6fb5e6087eb1b2dc/ULY7-hero.png) + +Committing +---------- + +Before committing, it's a good idea to run `gofmt -w *.go`. ([gofmt](https://golang.org/cmd/gofmt/)) diff --git a/vendor/github.com/packethost/packngo/devices.go b/vendor/github.com/packethost/packngo/devices.go index 7607c1250e..f411bf65cb 100644 --- a/vendor/github.com/packethost/packngo/devices.go +++ b/vendor/github.com/packethost/packngo/devices.go @@ -69,19 +69,6 @@ func (d DeviceActionRequest) String() string { return Stringify(d) } -// IPAddress used to execute actions on devices -type IPAddress struct { - Family int `json:"address_family"` - Cidr int `json:"cidr"` - Address string `json:"address"` - Gateway string `json:"gateway"` - Public bool `json:"public"` -} - -func (n IPAddress) String() string { - return Stringify(n) -} - // DeviceServiceOp implements DeviceService type DeviceServiceOp struct { client *Client diff --git a/vendor/github.com/packethost/packngo/email.go b/vendor/github.com/packethost/packngo/email.go index 98ed87147c..ef53ee4bb0 100644 --- a/vendor/github.com/packethost/packngo/email.go +++ b/vendor/github.com/packethost/packngo/email.go @@ -9,11 +9,12 @@ type EmailService interface { // Email represents a user's email address type Email struct { - ID string `json:"id"` - Address string `json:"address"` - Default bool `json:"default,omitempty"` - URL string `json:"href,omitempty"` + ID string `json:"id"` + Address string `json:"address"` + Default bool `json:"default,omitempty"` + URL string `json:"href,omitempty"` } + func (e Email) String() string { return Stringify(e) } @@ -23,7 +24,7 @@ type EmailServiceOp struct { client *Client } -// Get retrieves an email by id +// Get retrieves an email by id func (s *EmailServiceOp) Get(emailID string) (*Email, *Response, error) { req, err := s.client.NewRequest("GET", emailBasePath, nil) if err != nil { diff --git a/vendor/github.com/packethost/packngo/facilities.go b/vendor/github.com/packethost/packngo/facilities.go index a6deab895b..42a3c7cb11 100644 --- a/vendor/github.com/packethost/packngo/facilities.go +++ b/vendor/github.com/packethost/packngo/facilities.go @@ -1,4 +1,4 @@ -package packngo +package packngo const facilityBasePath = "/facilities" @@ -13,13 +13,14 @@ type facilityRoot struct { // Facility represents a Packet facility type Facility struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` - Code string `json:"code,omitempty"` - Features []string `json:"features,omitempty"` - Address *Address `json:"address,omitempty"` - URL string `json:"href,omitempty"` + ID string `json:"id"` + Name string `json:"name,omitempty"` + Code string `json:"code,omitempty"` + Features []string `json:"features,omitempty"` + Address *Address `json:"address,omitempty"` + URL string `json:"href,omitempty"` } + func (f Facility) String() string { return Stringify(f) } @@ -28,11 +29,11 @@ func (f Facility) String() string { type Address struct { ID string `json:"id,omitempty"` } + func (a Address) String() string { return Stringify(a) } - // FacilityServiceOp implements FacilityService type FacilityServiceOp struct { client *Client diff --git a/vendor/github.com/packethost/packngo/ip.go b/vendor/github.com/packethost/packngo/ip.go new file mode 100644 index 0000000000..2a71b921ca --- /dev/null +++ b/vendor/github.com/packethost/packngo/ip.go @@ -0,0 +1,198 @@ +package packngo + +import "fmt" + +const ipBasePath = "/ips" + +// IPService interface defines available IP methods +type IPService interface { + Assign(deviceID string, assignRequest *IPAddressAssignRequest) (*IPAddress, *Response, error) + Unassign(ipAddressID string) (*Response, error) + Get(ipAddressID string) (*IPAddress, *Response, error) +} + +// IPAddress represents a ip address +type IPAddress struct { + ID string `json:"id"` + Address string `json:"address"` + Gateway string `json:"gateway"` + Network string `json:"network"` + AddressFamily int `json:"address_family"` + Netmask string `json:"netmask"` + Public bool `json:"public"` + Cidr int `json:"cidr"` + AssignedTo map[string]string `json:"assigned_to"` + Href string `json:"href"` +} + +// IPAddressAssignRequest represents the body if a ip assign request +type IPAddressAssignRequest struct { + Address string `json:"address"` +} + +func (i IPAddress) String() string { + return Stringify(i) +} + +// IPServiceOp implements IPService +type IPServiceOp struct { + client *Client +} + +// Get returns IpAddress by ID +func (i *IPServiceOp) Get(ipAddressID string) (*IPAddress, *Response, error) { + path := fmt.Sprintf("%s/%s", ipBasePath, ipAddressID) + + req, err := i.client.NewRequest("GET", path, nil) + if err != nil { + return nil, nil, err + } + + ip := new(IPAddress) + resp, err := i.client.Do(req, ip) + if err != nil { + return nil, resp, err + } + + return ip, resp, err +} + +// Unassign unassigns an IP address record. This will remove the relationship between an IP +// and the device and will make the IP address available to be assigned to another device. +func (i *IPServiceOp) Unassign(ipAddressID string) (*Response, error) { + path := fmt.Sprintf("%s/%s", ipBasePath, ipAddressID) + + req, err := i.client.NewRequest("DELETE", path, nil) + if err != nil { + return nil, err + } + + resp, err := i.client.Do(req, nil) + return resp, err +} + +// Assign assigns an IP address to a device. The IP address must be in one of the IP ranges assigned to the device’s project. +func (i *IPServiceOp) Assign(deviceID string, assignRequest *IPAddressAssignRequest) (*IPAddress, *Response, error) { + path := fmt.Sprintf("%s/%s%s", deviceBasePath, deviceID, ipBasePath) + + req, err := i.client.NewRequest("POST", path, assignRequest) + + ip := new(IPAddress) + resp, err := i.client.Do(req, ip) + if err != nil { + return nil, resp, err + } + + return ip, resp, err +} + +// IP RESERVATIONS API + +// IPReservationService interface defines available IPReservation methods +type IPReservationService interface { + List(projectID string) ([]IPReservation, *Response, error) + RequestMore(projectID string, ipReservationReq *IPReservationRequest) (*Response, error) + Get(ipReservationID string) (*IPReservation, *Response, error) + Remove(ipReservationID string) (*Response, error) +} + +// IPReservationServiceOp implements the IPReservationService interface +type IPReservationServiceOp struct { + client *Client +} + +// IPReservationRequest represents the body of a reservation request +type IPReservationRequest struct { + Type string `json:"type"` + Quantity int `json:"quantity"` + Comments string `json:"comments"` +} + +// IPReservation represent an IP reservation for a single project +type IPReservation struct { + ID string `json:"id"` + Network string `json:"network"` + Address string `json:"address"` + AddressFamily int `json:"address_family"` + Netmask string `json:"netmask"` + Public bool `json:"public"` + Cidr int `json:"cidr"` + Management bool `json:"management"` + Manageable bool `json:"manageable"` + Addon bool `json:"addon"` + Bill bool `json:"bill"` + Assignments []map[string]string `json:"assignments"` + Href string `json:"href"` +} + +type ipReservationRoot struct { + IPReservations []IPReservation `json:"ip_addresses"` +} + +// List provides a list of IP resevations for a single project. +func (i *IPReservationServiceOp) List(projectID string) ([]IPReservation, *Response, error) { + path := fmt.Sprintf("%s/%s%s", projectBasePath, projectID, ipBasePath) + + req, err := i.client.NewRequest("GET", path, nil) + if err != nil { + return nil, nil, err + } + + reservations := new(ipReservationRoot) + resp, err := i.client.Do(req, reservations) + if err != nil { + return nil, resp, err + } + return reservations.IPReservations, resp, err +} + +// RequestMore requests more IP space for a project in order to have additional IP addresses to assign to devices +func (i *IPReservationServiceOp) RequestMore(projectID string, ipReservationReq *IPReservationRequest) (*Response, error) { + path := fmt.Sprintf("%s/%s%s", projectBasePath, projectID, ipBasePath) + + req, err := i.client.NewRequest("POST", path, &ipReservationReq) + if err != nil { + return nil, err + } + + resp, err := i.client.Do(req, nil) + if err != nil { + return nil, err + } + return resp, err +} + +// Get returns a single IP reservation object +func (i *IPReservationServiceOp) Get(ipReservationID string) (*IPReservation, *Response, error) { + path := fmt.Sprintf("%s/%s", ipBasePath, ipReservationID) + + req, err := i.client.NewRequest("GET", path, nil) + if err != nil { + return nil, nil, err + } + + reservation := new(IPReservation) + resp, err := i.client.Do(req, reservation) + if err != nil { + return nil, nil, err + } + + return reservation, resp, err +} + +// Remove removes an IP reservation from the project. +func (i *IPReservationServiceOp) Remove(ipReservationID string) (*Response, error) { + path := fmt.Sprintf("%s/%s", ipBasePath, ipReservationID) + + req, err := i.client.NewRequest("DELETE", path, nil) + if err != nil { + return nil, err + } + + resp, err := i.client.Do(req, nil) + if err != nil { + return nil, err + } + + return resp, err +} diff --git a/vendor/github.com/packethost/packngo/operatingsystems.go b/vendor/github.com/packethost/packngo/operatingsystems.go index bad59e86c4..4200a40cf8 100644 --- a/vendor/github.com/packethost/packngo/operatingsystems.go +++ b/vendor/github.com/packethost/packngo/operatingsystems.go @@ -18,16 +18,17 @@ type OS struct { Distro string `json:"distro"` Version string `json:"version"` } + func (o OS) String() string { return Stringify(o) } -// OSServiceOp implements OSService +// OSServiceOp implements OSService type OSServiceOp struct { client *Client } -// List returns all available operating systems +// List returns all available operating systems func (s *OSServiceOp) List() ([]OS, *Response, error) { req, err := s.client.NewRequest("GET", osBasePath, nil) if err != nil { diff --git a/vendor/github.com/packethost/packngo/packngo.go b/vendor/github.com/packethost/packngo/packngo.go index c3d70a5824..21a0279414 100644 --- a/vendor/github.com/packethost/packngo/packngo.go +++ b/vendor/github.com/packethost/packngo/packngo.go @@ -89,6 +89,9 @@ type Client struct { Projects ProjectService Facilities FacilityService OperatingSystems OSService + Ips IPService + IpReservations IPReservationService + Volumes VolumeService } // NewRequest inits a new http request with the proper headers @@ -165,6 +168,10 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { // an older version of Go, pass in a custom http.Client with a custom TLS configuration // that sets "InsecureSkipVerify" to "true" func NewClient(consumerToken string, apiKey string, httpClient *http.Client) *Client { + client, _ := NewClientWithBaseURL(consumerToken, apiKey, httpClient, baseURL) + return client +} +func NewClientWithBaseURL(consumerToken string, apiKey string, httpClient *http.Client, apiBaseURL string) (*Client, error) { if httpClient == nil { // Don't fall back on http.DefaultClient as it's not nice to adjust state // implicitly. If the client wants to use http.DefaultClient, they can @@ -172,9 +179,12 @@ func NewClient(consumerToken string, apiKey string, httpClient *http.Client) *Cl httpClient = &http.Client{} } - BaseURL, _ := url.Parse(baseURL) + u, err := url.Parse(apiBaseURL) + if err != nil { + return nil, err + } - c := &Client{client: httpClient, BaseURL: BaseURL, UserAgent: userAgent, ConsumerToken: consumerToken, APIKey: apiKey} + c := &Client{client: httpClient, BaseURL: u, UserAgent: userAgent, ConsumerToken: consumerToken, APIKey: apiKey} c.Plans = &PlanServiceOp{client: c} c.Users = &UserServiceOp{client: c} c.Emails = &EmailServiceOp{client: c} @@ -183,8 +193,11 @@ func NewClient(consumerToken string, apiKey string, httpClient *http.Client) *Cl c.Projects = &ProjectServiceOp{client: c} c.Facilities = &FacilityServiceOp{client: c} c.OperatingSystems = &OSServiceOp{client: c} + c.Ips = &IPServiceOp{client: c} + c.IpReservations = &IPReservationServiceOp{client: c} + c.Volumes = &VolumeServiceOp{client: c} - return c + return c, nil } func checkResponse(r *http.Response) error { diff --git a/vendor/github.com/packethost/packngo/plans.go b/vendor/github.com/packethost/packngo/plans.go index 6350443e75..5b3fa86fdf 100644 --- a/vendor/github.com/packethost/packngo/plans.go +++ b/vendor/github.com/packethost/packngo/plans.go @@ -1,4 +1,4 @@ -package packngo +package packngo const planBasePath = "/plans" @@ -21,18 +21,20 @@ type Plan struct { Specs *Specs `json:"specs,omitempty"` Pricing *Pricing `json:"pricing,omitempty"` } + func (p Plan) String() string { return Stringify(p) } // Specs - the server specs for a plan type Specs struct { - Cpus []*Cpus `json:"cpus,omitempty"` - Memory *Memory `json:"memory,omitempty"` - Drives []*Drives `json:"drives,omitempty"` - Nics []*Nics `json:"nics,omitempty"` - Features *Features `json:"features,omitempty"` + Cpus []*Cpus `json:"cpus,omitempty"` + Memory *Memory `json:"memory,omitempty"` + Drives []*Drives `json:"drives,omitempty"` + Nics []*Nics `json:"nics,omitempty"` + Features *Features `json:"features,omitempty"` } + func (s Specs) String() string { return Stringify(s) } @@ -42,6 +44,7 @@ type Cpus struct { Count int `json:"count,omitempty"` Type string `json:"type,omitempty"` } + func (c Cpus) String() string { return Stringify(c) } @@ -50,6 +53,7 @@ func (c Cpus) String() string { type Memory struct { Total string `json:"total,omitempty"` } + func (m Memory) String() string { return Stringify(m) } @@ -60,6 +64,7 @@ type Drives struct { Size string `json:"size,omitempty"` Type string `json:"type,omitempty"` } + func (d Drives) String() string { return Stringify(d) } @@ -69,6 +74,7 @@ type Nics struct { Count int `json:"count,omitempty"` Type string `json:"type,omitempty"` } + func (n Nics) String() string { return Stringify(n) } @@ -78,6 +84,7 @@ type Features struct { Raid bool `json:"raid,omitempty"` Txt bool `json:"txt,omitempty"` } + func (f Features) String() string { return Stringify(f) } @@ -87,6 +94,7 @@ type Pricing struct { Hourly float32 `json:"hourly,omitempty"` Monthly float32 `json:"monthly,omitempty"` } + func (p Pricing) String() string { return Stringify(p) } diff --git a/vendor/github.com/packethost/packngo/projects.go b/vendor/github.com/packethost/packngo/projects.go index 391a4b3ce8..9c539a8ce0 100644 --- a/vendor/github.com/packethost/packngo/projects.go +++ b/vendor/github.com/packethost/packngo/projects.go @@ -11,11 +11,22 @@ type ProjectService interface { Create(*ProjectCreateRequest) (*Project, *Response, error) Update(*ProjectUpdateRequest) (*Project, *Response, error) Delete(string) (*Response, error) + ListIPAddresses(string) ([]IPAddress, *Response, error) + ListVolumes(string) ([]Volume, *Response, error) +} + +type ipsRoot struct { + IPAddresses []IPAddress `json:"ip_addresses"` +} + +type volumesRoot struct { + Volumes []Volume `json:"volumes"` } type projectsRoot struct { Projects []Project `json:"projects"` } + // Project represents a Packet project type Project struct { ID string `json:"id"` @@ -27,6 +38,7 @@ type Project struct { SSHKeys []SSHKey `json:"ssh_keys,omitempty"` URL string `json:"href,omitempty"` } + func (p Project) String() string { return Stringify(p) } @@ -36,6 +48,7 @@ type ProjectCreateRequest struct { Name string `json:"name"` PaymentMethod string `json:"payment_method,omitempty"` } + func (p ProjectCreateRequest) String() string { return Stringify(p) } @@ -46,6 +59,7 @@ type ProjectUpdateRequest struct { Name string `json:"name,omitempty"` PaymentMethod string `json:"payment_method,omitempty"` } + func (p ProjectUpdateRequest) String() string { return Stringify(p) } @@ -55,6 +69,22 @@ type ProjectServiceOp struct { client *Client } +func (s *ProjectServiceOp) ListIPAddresses(projectID string) ([]IPAddress, *Response, error) { + url := fmt.Sprintf("%s/%s/ips", projectBasePath, projectID) + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + root := new(ipsRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.IPAddresses, resp, err +} + // List returns the user's projects func (s *ProjectServiceOp) List() ([]Project, *Response, error) { req, err := s.client.NewRequest("GET", projectBasePath, nil) @@ -134,3 +164,20 @@ func (s *ProjectServiceOp) Delete(projectID string) (*Response, error) { return resp, err } + +// List returns Volumes for a project +func (s *ProjectServiceOp) ListVolumes(projectID string) ([]Volume, *Response, error) { + url := fmt.Sprintf("%s/%s%s", projectBasePath, projectID, volumeBasePath) + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + root := new(volumesRoot) + resp, err := s.client.Do(req, root) + if err != nil { + return nil, resp, err + } + + return root.Volumes, resp, err +} diff --git a/vendor/github.com/packethost/packngo/rate.go b/vendor/github.com/packethost/packngo/rate.go index 4dc55acaa7..965967d455 100644 --- a/vendor/github.com/packethost/packngo/rate.go +++ b/vendor/github.com/packethost/packngo/rate.go @@ -2,10 +2,11 @@ package packngo // Rate provides the API request rate limit details type Rate struct { - RequestLimit int `json:"request_limit"` - RequestsRemaining int `json:"requests_remaining"` - Reset Timestamp `json:"rate_reset"` + RequestLimit int `json:"request_limit"` + RequestsRemaining int `json:"requests_remaining"` + Reset Timestamp `json:"rate_reset"` } + func (r Rate) String() string { return Stringify(r) } diff --git a/vendor/github.com/packethost/packngo/sshkeys.go b/vendor/github.com/packethost/packngo/sshkeys.go index 33c45e1690..06d9962be8 100644 --- a/vendor/github.com/packethost/packngo/sshkeys.go +++ b/vendor/github.com/packethost/packngo/sshkeys.go @@ -19,34 +19,37 @@ type sshKeyRoot struct { // SSHKey represents a user's ssh key type SSHKey struct { - ID string `json:"id"` - Label string `json:"label"` - Key string `json:"key"` - FingerPrint string `json:"fingerprint"` - Created string `json:"created_at"` - Updated string `json:"updated_at"` - User User `json:"user,omitempty"` - URL string `json:"href,omitempty"` + ID string `json:"id"` + Label string `json:"label"` + Key string `json:"key"` + FingerPrint string `json:"fingerprint"` + Created string `json:"created_at"` + Updated string `json:"updated_at"` + User User `json:"user,omitempty"` + URL string `json:"href,omitempty"` } + func (s SSHKey) String() string { return Stringify(s) } // SSHKeyCreateRequest type used to create an ssh key type SSHKeyCreateRequest struct { - Label string `json:"label"` - Key string `json:"key"` + Label string `json:"label"` + Key string `json:"key"` } + func (s SSHKeyCreateRequest) String() string { return Stringify(s) } // SSHKeyUpdateRequest type used to update an ssh key type SSHKeyUpdateRequest struct { - ID string `json:"id"` - Label string `json:"label"` - Key string `json:"key"` + ID string `json:"id"` + Label string `json:"label"` + Key string `json:"key"` } + func (s SSHKeyUpdateRequest) String() string { return Stringify(s) } diff --git a/vendor/github.com/packethost/packngo/user.go b/vendor/github.com/packethost/packngo/user.go index 5bd2d11fa1..36e03d9090 100644 --- a/vendor/github.com/packethost/packngo/user.go +++ b/vendor/github.com/packethost/packngo/user.go @@ -9,23 +9,24 @@ type UserService interface { // User represents a Packet user type User struct { - ID string `json:"id"` - FirstName string `json:"first_name,omitempty"` - LastName string `json:"last_name,omitempty"` - FullName string `json:"full_name,omitempty"` - Email string `json:"email,omitempty"` - TwoFactor string `json:"two_factor_auth,omitempty"` - AvatarURL string `json:"avatar_url,omitempty"` - Facebook string `json:"twitter,omitempty"` - Twitter string `json:"facebook,omitempty"` - LinkedIn string `json:"linkedin,omitempty"` - Created string `json:"created_at,omitempty"` - Updated string `json:"updated_at,omitempty"` - TimeZone string `json:"timezone,omitempty"` - Emails []Email `json:"email,omitempty"` - PhoneNumber string `json:"phone_number,omitempty"` - URL string `json:"href,omitempty"` + ID string `json:"id"` + FirstName string `json:"first_name,omitempty"` + LastName string `json:"last_name,omitempty"` + FullName string `json:"full_name,omitempty"` + Email string `json:"email,omitempty"` + TwoFactor string `json:"two_factor_auth,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` + Facebook string `json:"twitter,omitempty"` + Twitter string `json:"facebook,omitempty"` + LinkedIn string `json:"linkedin,omitempty"` + Created string `json:"created_at,omitempty"` + Updated string `json:"updated_at,omitempty"` + TimeZone string `json:"timezone,omitempty"` + Emails []Email `json:"email,omitempty"` + PhoneNumber string `json:"phone_number,omitempty"` + URL string `json:"href,omitempty"` } + func (u User) String() string { return Stringify(u) } diff --git a/vendor/github.com/packethost/packngo/volumes.go b/vendor/github.com/packethost/packngo/volumes.go new file mode 100644 index 0000000000..e0b7eeacc9 --- /dev/null +++ b/vendor/github.com/packethost/packngo/volumes.go @@ -0,0 +1,146 @@ +package packngo + +import "fmt" + +const volumeBasePath = "/storage" + +// VolumeService interface defines available Volume methods +type VolumeService interface { + Get(string) (*Volume, *Response, error) + Update(*VolumeUpdateRequest) (*Volume, *Response, error) + Delete(string) (*Response, error) + Create(*VolumeCreateRequest) (*Volume, *Response, error) +} + +// Volume represents a volume +type Volume struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Size int `json:"size,omitempty"` + State string `json:"state,omitempty"` + Locked bool `json:"locked,omitempty"` + BillingCycle string `json:"billing_cycle,omitempty"` + Created string `json:"created_at,omitempty"` + Updated string `json:"updated_at,omitempty"` + Href string `json:"href,omitempty"` + SnapshotPolicies []*SnapshotPolicy `json:"snapshot_policies,omitempty"` + Attachments []*Attachment `json:"attachments,omitempty"` + Plan *Plan `json:"plan,omitempty"` + Facility *Facility `json:"facility,omitempty"` + Project *Project `json:"project,omitempty"` +} + +// SnapshotPolicy used to execute actions on volume +type SnapshotPolicy struct { + ID string `json:"id"` + Href string `json:"href"` + SnapshotFrequency string `json:"snapshot_frequency,omitempty"` + snapshot_count int `json:"snapshot_count,omitempty"` +} + +// Attachment used to execute actions on volume +type Attachment struct { + ID string `json:"id"` + Href string `json:"href"` +} + +func (v Volume) String() string { + return Stringify(v) +} + +// VolumeCreateRequest type used to create a Packet volume +type VolumeCreateRequest struct { + Size int `json:"size"` + BillingCycle string `json:"billing_cycle"` + ProjectID string `json:"project_id"` + PlanID string `json:"plan_id"` + FacilityID string `json:"facility_id"` + Description string `json:"Description,omitempty"` + SnapshotPolicies []*SnapshotPolicy `json:"snapshot_policies,omitempty"` +} + +func (v VolumeCreateRequest) String() string { + return Stringify(v) +} + +// VolumeUpdateRequest type used to update a Packet volume +type VolumeUpdateRequest struct { + ID string `json:"id"` + Description string `json:"description,omitempty"` + Plan string `json:"plan,omitempty"` +} + +func (v VolumeUpdateRequest) String() string { + return Stringify(v) +} + +// VolumeServiceOp implements VolumeService +type VolumeServiceOp struct { + client *Client +} + +// Get returns a volume by id +func (v *VolumeServiceOp) Get(volumeID string) (*Volume, *Response, error) { + path := fmt.Sprintf("%s/%s", volumeBasePath, volumeID) + req, err := v.client.NewRequest("GET", path, nil) + if err != nil { + return nil, nil, err + } + + volume := new(Volume) + resp, err := v.client.Do(req, volume) + if err != nil { + return nil, resp, err + } + + return volume, resp, err +} + +// Update updates a volume +func (v *VolumeServiceOp) Update(updateRequest *VolumeUpdateRequest) (*Volume, *Response, error) { + path := fmt.Sprintf("%s/%s", volumeBasePath, updateRequest.ID) + req, err := v.client.NewRequest("PATCH", path, updateRequest) + if err != nil { + return nil, nil, err + } + + volume := new(Volume) + resp, err := v.client.Do(req, volume) + if err != nil { + return nil, resp, err + } + + return volume, resp, err +} + +// Delete deletes a volume +func (v *VolumeServiceOp) Delete(volumeID string) (*Response, error) { + path := fmt.Sprintf("%s/%s", volumeBasePath, volumeID) + + req, err := v.client.NewRequest("DELETE", path, nil) + if err != nil { + return nil, err + } + + resp, err := v.client.Do(req, nil) + + return resp, err +} + +// Create creates a new volume for a project +func (v *VolumeServiceOp) Create(createRequest *VolumeCreateRequest) (*Volume, *Response, error) { + url := fmt.Sprintf("%s/%s%s", projectBasePath, createRequest.ProjectID, volumeBasePath) + req, err := v.client.NewRequest("POST", url, createRequest) + if err != nil { + return nil, nil, err + } + + volume := new(Volume) + resp, err := v.client.Do(req, volume) + if err != nil { + return nil, resp, err + } + + return volume, resp, err +} diff --git a/vendor/vendor.json b/vendor/vendor.json index ee31a419fd..07197c307b 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1464,8 +1464,10 @@ "revision": "3d184cea22ee1c41ec1697e0d830ff0c78f7ea97" }, { + "checksumSHA1": "yHIFvxXMD3q3ZvQZFLw+EJBhTq8=", "path": "github.com/packethost/packngo", - "revision": "f03d7dc788a8b57b62d301ccb98c950c325756f8" + "revision": "a596b01fbf1afdf90188ec02aa61fe7cc9703945", + "revisionTime": "2016-07-21T21:43:59Z" }, { "path": "github.com/pborman/uuid", From 806939ccdb4a00728e6cd34fb2ec2bfea5c64322 Mon Sep 17 00:00:00 2001 From: Ayu Demura Date: Fri, 22 Jul 2016 10:57:54 -0400 Subject: [PATCH 02/13] add ip_address --- .../packet/resource_packet_ip_address.go | 130 ++++++++++++++++++ .../packet/resource_packet_ip_address_test.go | 86 ++++++++++++ .../packet/resource_packet_volume.go | 30 ++-- vendor/github.com/packethost/packngo/ip.go | 2 + 4 files changed, 233 insertions(+), 15 deletions(-) create mode 100644 builtin/providers/packet/resource_packet_ip_address.go create mode 100644 builtin/providers/packet/resource_packet_ip_address_test.go diff --git a/builtin/providers/packet/resource_packet_ip_address.go b/builtin/providers/packet/resource_packet_ip_address.go new file mode 100644 index 0000000000..c3d2c9815f --- /dev/null +++ b/builtin/providers/packet/resource_packet_ip_address.go @@ -0,0 +1,130 @@ +package packet + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/packethost/packngo" +) + +func resourcePacketIPAddress() *schema.Resource { + return &schema.Resource{ + Create: resourcePacketIPAddressCreate, + Read: resourcePacketIPAddressRead, + Delete: resourcePacketIPAddressDelete, + + Schema: map[string]*schema.Schema{ + "address": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Optional: true, + Computed: true, + }, + + "gateway": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "network": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "family": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + + "netmask": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + + "cidr": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + + "public": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + + "assigned_to": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + + "created": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "updated": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourcePacketIPAddressCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*packngo.Client) + + createRequest := &packngo.IPAddressAssignRequest{ + Address: d.Get("address").(string), + } + + device_id := "" + if attr, ok := d.GetOk("instance_id"); ok { + device_id = attr.(string) + } + + newIPAddress, _, err := client.Ips.Assign(device_id, createRequest) + if err != nil { + return friendlyError(err) + } + + d.SetId(newIPAddress.ID) + + return resourcePacketIPAddressRead(d, meta) +} + +func resourcePacketIPAddressRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*packngo.Client) + + ip_address, _, err := client.Ips.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("address", ip_address.Address) + d.Set("gateway", ip_address.Gateway) + d.Set("network", ip_address.Network) + d.Set("family", ip_address.AddressFamily) + d.Set("netmask", ip_address.Netmask) + d.Set("public", ip_address.Public) + d.Set("cidr", ip_address.Cidr) + d.Set("assigned_to", ip_address.AssignedTo) + d.Set("created", ip_address.Created) + d.Set("updated", ip_address.Updated) + + return nil +} + +func resourcePacketIPAddressDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*packngo.Client) + + if _, err := client.Ips.Unassign(d.Id()); err != nil { + return friendlyError(err) + } + + return nil +} diff --git a/builtin/providers/packet/resource_packet_ip_address_test.go b/builtin/providers/packet/resource_packet_ip_address_test.go new file mode 100644 index 0000000000..9944eb107c --- /dev/null +++ b/builtin/providers/packet/resource_packet_ip_address_test.go @@ -0,0 +1,86 @@ +package packet + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/packethost/packngo" +) + +func TestAccPacketIPAddress_Basic(t *testing.T) { + var ip_address packngo.IPAddress + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPacketIPAddressDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckPacketIPAddressConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckPacketIPAddressExists("packet_ip_address.foobar", &ip_address), + testAccCheckPacketIPAddressAttributes(&ip_address), + resource.TestCheckResourceAttr( + "packet_ip_address.foobar", "address", "foobar"), + ), + }, + }, + }) +} + +func testAccCheckPacketIPAddressDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*packngo.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "packet_ip_address" { + continue + } + if _, _, err := client.Ips.Get(rs.Primary.ID); err == nil { + return fmt.Errorf("IPAddress cstill exists") + } + } + + return nil +} + +func testAccCheckPacketIPAddressAttributes(ip_address *packngo.IPAddress) resource.TestCheckFunc { + return func(s *terraform.State) error { + if ip_address.Address != "foobar" { + return fmt.Errorf("Bad address: %s", ip_address.Address) + } + return nil + } +} + +func testAccCheckPacketIPAddressExists(n string, ip_address *packngo.IPAddress) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + client := testAccProvider.Meta().(*packngo.Client) + + foundIPAddress, _, err := client.Ips.Get(rs.Primary.ID) + if err != nil { + return err + } + if foundIPAddress.ID != rs.Primary.ID { + return fmt.Errorf("Record not found: %v - %v", rs.Primary.ID, foundIPAddress) + } + + *ip_address = *foundIPAddress + + return nil + } +} + +var testAccCheckPacketIPAddressConfig_basic = fmt.Sprintf(` +resource "packet_ip_address" "foobar" { + address = "foobar" +}`) diff --git a/builtin/providers/packet/resource_packet_volume.go b/builtin/providers/packet/resource_packet_volume.go index f17f03c4f9..0b121557c3 100644 --- a/builtin/providers/packet/resource_packet_volume.go +++ b/builtin/providers/packet/resource_packet_volume.go @@ -152,7 +152,7 @@ func resourcePacketVolumeCreate(d *schema.ResourceData, meta interface{}) error func resourcePacketVolumeRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*packngo.Client) - device, _, err := client.Volumes.Get(d.Id()) + volume, _, err := client.Volumes.Get(d.Id()) if err != nil { err = friendlyError(err) @@ -165,25 +165,25 @@ func resourcePacketVolumeRead(d *schema.ResourceData, meta interface{}) error { return err } - d.Set("name", device.Name) - d.Set("description", device.Description) - d.Set("size", device.Size) - d.Set("plan", device.Plan.Slug) - d.Set("facility", device.Facility.Code) - d.Set("state", device.State) - d.Set("billing_cycle", device.BillingCycle) - d.Set("locked", device.Locked) - d.Set("created", device.Created) - d.Set("updated", device.Updated) + 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([]*packngo.SnapshotPolicy, 0, len(device.SnapshotPolicies)) - for _, snapshot_policy := range device.SnapshotPolicies { + snapshot_policies := make([]*packngo.SnapshotPolicy, 0, len(volume.SnapshotPolicies)) + for _, snapshot_policy := range volume.SnapshotPolicies { snapshot_policies = append(snapshot_policies, snapshot_policy) } d.Set("snapshot_policies", snapshot_policies) - attachments := make([]*packngo.Attachment, 0, len(device.Attachments)) - for _, attachment := range device.Attachments { + attachments := make([]*packngo.Attachment, 0, len(volume.Attachments)) + for _, attachment := range volume.Attachments { attachments = append(attachments, attachment) } d.Set("attachments", attachments) diff --git a/vendor/github.com/packethost/packngo/ip.go b/vendor/github.com/packethost/packngo/ip.go index 2a71b921ca..c95147a9a8 100644 --- a/vendor/github.com/packethost/packngo/ip.go +++ b/vendor/github.com/packethost/packngo/ip.go @@ -22,6 +22,8 @@ type IPAddress struct { Public bool `json:"public"` Cidr int `json:"cidr"` AssignedTo map[string]string `json:"assigned_to"` + Created string `json:"created_at,omitempty"` + Updated string `json:"updated_at,omitempty"` Href string `json:"href"` } From be3df2e54794331e906667c06445438a93f49ef1 Mon Sep 17 00:00:00 2001 From: Ayu Demura Date: Fri, 22 Jul 2016 11:55:49 -0400 Subject: [PATCH 03/13] Add ip reservation --- .../packet/resource_packet_ip_address.go | 2 +- .../packet/resource_packet_ip_reservation.go | 135 ++++++++++++++++++ .../resource_packet_ip_reservation_test.go | 86 +++++++++++ vendor/github.com/packethost/packngo/ip.go | 15 +- .../github.com/packethost/packngo/volumes.go | 38 ++--- vendor/vendor.json | 6 +- 6 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 builtin/providers/packet/resource_packet_ip_reservation.go create mode 100644 builtin/providers/packet/resource_packet_ip_reservation_test.go diff --git a/builtin/providers/packet/resource_packet_ip_address.go b/builtin/providers/packet/resource_packet_ip_address.go index c3d2c9815f..502e378b31 100644 --- a/builtin/providers/packet/resource_packet_ip_address.go +++ b/builtin/providers/packet/resource_packet_ip_address.go @@ -96,7 +96,7 @@ func resourcePacketIPAddressRead(d *schema.ResourceData, meta interface{}) error if err != nil { err = friendlyError(err) - // If the volume somehow already destroyed, mark as succesfully gone. + // If the ip_address somehow already destroyed, mark as succesfully gone. if isNotFound(err) { d.SetId("") return nil diff --git a/builtin/providers/packet/resource_packet_ip_reservation.go b/builtin/providers/packet/resource_packet_ip_reservation.go new file mode 100644 index 0000000000..80547f3c01 --- /dev/null +++ b/builtin/providers/packet/resource_packet_ip_reservation.go @@ -0,0 +1,135 @@ +package packet + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/packethost/packngo" +) + +func resourcePacketIPReservation() *schema.Resource { + return &schema.Resource{ + Create: resourcePacketIPReservationCreate, + Read: resourcePacketIPReservationRead, + Delete: resourcePacketIPReservationDelete, + + Schema: map[string]*schema.Schema{ + "address": &schema.Schema{ + Type: schema.TypeString, + Required: true, + Optional: true, + Computed: true, + }, + + "gateway": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "network": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "family": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + + "netmask": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + + "cidr": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + + "public": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + + "assigned_to": &schema.Schema{ + Type: schema.TypeBool, + Computed: true, + }, + + "created": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "updated": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourcePacketIPReservationCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*packngo.Client) + + createRequest := &packngo.IPReservationRequest{ + Type: d.Get("type").(string), + Quantity: d.Get("quantity").(int), + Comments: d.Get("comments").(string), + } + + project_id := "" + if attr, ok := d.GetOk("project_id"); ok { + project_id = attr.(string) + } + + newIPReservation, _, err := client.IpReservations.RequestMore(project_id, createRequest) + if err != nil { + return friendlyError(err) + } + + d.SetId(newIPReservation.ID) + + return resourcePacketIPReservationRead(d, meta) +} + +func resourcePacketIPReservationRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*packngo.Client) + + ip_reservation, _, err := client.IpReservations.Get(d.Id()) + if err != nil { + err = friendlyError(err) + + // If the ip_reservation somehow already destroyed, mark as succesfully gone. + if isNotFound(err) { + d.SetId("") + return nil + } + + return err + } + + d.Set("address", ip_reservation.Address) + d.Set("network", ip_reservation.Network) + d.Set("family", ip_reservation.AddressFamily) + d.Set("netmask", ip_reservation.Netmask) + d.Set("public", ip_reservation.Public) + d.Set("cidr", ip_reservation.Cidr) + d.Set("management", ip_reservation.Management) + d.Set("manageable", ip_reservation.Manageable) + d.Set("addon", ip_reservation.Addon) + d.Set("bill", ip_reservation.Bill) + d.Set("assignments", ip_reservation.Assignments) + d.Set("created", ip_reservation.Created) + d.Set("updated", ip_reservation.Updated) + + return nil +} + +func resourcePacketIPReservationDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*packngo.Client) + + if _, err := client.IpReservations.Remove(d.Id()); err != nil { + return friendlyError(err) + } + + return nil +} diff --git a/builtin/providers/packet/resource_packet_ip_reservation_test.go b/builtin/providers/packet/resource_packet_ip_reservation_test.go new file mode 100644 index 0000000000..954e7c3c5c --- /dev/null +++ b/builtin/providers/packet/resource_packet_ip_reservation_test.go @@ -0,0 +1,86 @@ +package packet + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/packethost/packngo" +) + +func TestAccPacketIPReservation_Basic(t *testing.T) { + var ip_reservation packngo.IPReservation + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPacketIPReservationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckPacketIPReservationConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckPacketIPReservationExists("packet_ip_reservation.foobar", &ip_reservation), + testAccCheckPacketIPReservationAttributes(&ip_reservation), + resource.TestCheckResourceAttr( + "packet_ip_reservation.foobar", "address", "foobar"), + ), + }, + }, + }) +} + +func testAccCheckPacketIPReservationDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*packngo.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "packet_ip_reservation" { + continue + } + if _, _, err := client.IpReservations.Get(rs.Primary.ID); err == nil { + return fmt.Errorf("IPReservation cstill exists") + } + } + + return nil +} + +func testAccCheckPacketIPReservationAttributes(ip_reservation *packngo.IPReservation) resource.TestCheckFunc { + return func(s *terraform.State) error { + if ip_reservation.Address != "foobar" { + return fmt.Errorf("Bad address: %s", ip_reservation.Address) + } + return nil + } +} + +func testAccCheckPacketIPReservationExists(n string, ip_reservation *packngo.IPReservation) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + client := testAccProvider.Meta().(*packngo.Client) + + foundIPReservation, _, err := client.IpReservations.Get(rs.Primary.ID) + if err != nil { + return err + } + if foundIPReservation.ID != rs.Primary.ID { + return fmt.Errorf("Record not found: %v - %v", rs.Primary.ID, foundIPReservation) + } + + *ip_reservation = *foundIPReservation + + return nil + } +} + +var testAccCheckPacketIPReservationConfig_basic = fmt.Sprintf(` +resource "packet_ip_reservation" "foobar" { + address = "foobar" +}`) diff --git a/vendor/github.com/packethost/packngo/ip.go b/vendor/github.com/packethost/packngo/ip.go index c95147a9a8..e65be77450 100644 --- a/vendor/github.com/packethost/packngo/ip.go +++ b/vendor/github.com/packethost/packngo/ip.go @@ -93,7 +93,7 @@ func (i *IPServiceOp) Assign(deviceID string, assignRequest *IPAddressAssignRequ // IPReservationService interface defines available IPReservation methods type IPReservationService interface { List(projectID string) ([]IPReservation, *Response, error) - RequestMore(projectID string, ipReservationReq *IPReservationRequest) (*Response, error) + RequestMore(projectID string, ipReservationReq *IPReservationRequest) (*IPReservation, *Response, error) Get(ipReservationID string) (*IPReservation, *Response, error) Remove(ipReservationID string) (*Response, error) } @@ -124,6 +124,8 @@ type IPReservation struct { Addon bool `json:"addon"` Bill bool `json:"bill"` Assignments []map[string]string `json:"assignments"` + Created string `json:"created_at,omitempty"` + Updated string `json:"updated_at,omitempty"` Href string `json:"href"` } @@ -149,19 +151,20 @@ func (i *IPReservationServiceOp) List(projectID string) ([]IPReservation, *Respo } // RequestMore requests more IP space for a project in order to have additional IP addresses to assign to devices -func (i *IPReservationServiceOp) RequestMore(projectID string, ipReservationReq *IPReservationRequest) (*Response, error) { +func (i *IPReservationServiceOp) RequestMore(projectID string, ipReservationReq *IPReservationRequest) (*IPReservation, *Response, error) { path := fmt.Sprintf("%s/%s%s", projectBasePath, projectID, ipBasePath) req, err := i.client.NewRequest("POST", path, &ipReservationReq) if err != nil { - return nil, err + return nil, nil, err } - resp, err := i.client.Do(req, nil) + ip := new(IPReservation) + resp, err := i.client.Do(req, ip) if err != nil { - return nil, err + return nil, resp, err } - return resp, err + return ip, resp, err } // Get returns a single IP reservation object diff --git a/vendor/github.com/packethost/packngo/volumes.go b/vendor/github.com/packethost/packngo/volumes.go index e0b7eeacc9..6b6171f953 100644 --- a/vendor/github.com/packethost/packngo/volumes.go +++ b/vendor/github.com/packethost/packngo/volumes.go @@ -14,21 +14,21 @@ type VolumeService interface { // Volume represents a volume type Volume struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Size int `json:"size,omitempty"` - State string `json:"state,omitempty"` - Locked bool `json:"locked,omitempty"` - BillingCycle string `json:"billing_cycle,omitempty"` - Created string `json:"created_at,omitempty"` - Updated string `json:"updated_at,omitempty"` - Href string `json:"href,omitempty"` + ID string `json:"id"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Size int `json:"size,omitempty"` + State string `json:"state,omitempty"` + Locked bool `json:"locked,omitempty"` + BillingCycle string `json:"billing_cycle,omitempty"` + Created string `json:"created_at,omitempty"` + Updated string `json:"updated_at,omitempty"` + Href string `json:"href,omitempty"` SnapshotPolicies []*SnapshotPolicy `json:"snapshot_policies,omitempty"` Attachments []*Attachment `json:"attachments,omitempty"` - Plan *Plan `json:"plan,omitempty"` - Facility *Facility `json:"facility,omitempty"` - Project *Project `json:"project,omitempty"` + Plan *Plan `json:"plan,omitempty"` + Facility *Facility `json:"facility,omitempty"` + Project *Project `json:"project,omitempty"` } // SnapshotPolicy used to execute actions on volume @@ -51,12 +51,12 @@ func (v Volume) String() string { // VolumeCreateRequest type used to create a Packet volume type VolumeCreateRequest struct { - Size int `json:"size"` - BillingCycle string `json:"billing_cycle"` - ProjectID string `json:"project_id"` - PlanID string `json:"plan_id"` - FacilityID string `json:"facility_id"` - Description string `json:"Description,omitempty"` + Size int `json:"size"` + BillingCycle string `json:"billing_cycle"` + ProjectID string `json:"project_id"` + PlanID string `json:"plan_id"` + FacilityID string `json:"facility_id"` + Description string `json:"Description,omitempty"` SnapshotPolicies []*SnapshotPolicy `json:"snapshot_policies,omitempty"` } diff --git a/vendor/vendor.json b/vendor/vendor.json index 07197c307b..c55457f687 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1464,10 +1464,10 @@ "revision": "3d184cea22ee1c41ec1697e0d830ff0c78f7ea97" }, { - "checksumSHA1": "yHIFvxXMD3q3ZvQZFLw+EJBhTq8=", + "checksumSHA1": "/r0HjrIkynh4Gj0EIAKirEKP/Rk=", "path": "github.com/packethost/packngo", - "revision": "a596b01fbf1afdf90188ec02aa61fe7cc9703945", - "revisionTime": "2016-07-21T21:43:59Z" + "revision": "1bf5adfc4f590757da03407f576c46569a9c04b0", + "revisionTime": "2016-07-22T15:57:43Z" }, { "path": "github.com/pborman/uuid", From 27dc29ce285f6be44c9a04629193e0cf96625ce6 Mon Sep 17 00:00:00 2001 From: Ayu Demura Date: Fri, 22 Jul 2016 12:26:41 -0400 Subject: [PATCH 04/13] missing resources --- builtin/providers/packet/provider.go | 10 ++++++---- builtin/providers/packet/resource_packet_ip_address.go | 2 -- .../providers/packet/resource_packet_ip_reservation.go | 2 -- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/builtin/providers/packet/provider.go b/builtin/providers/packet/provider.go index f3a8483370..853e1c2473 100644 --- a/builtin/providers/packet/provider.go +++ b/builtin/providers/packet/provider.go @@ -18,10 +18,12 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "packet_device": resourcePacketDevice(), - "packet_ssh_key": resourcePacketSSHKey(), - "packet_project": resourcePacketProject(), - "packet_volume": resourcePacketVolume(), + "packet_device": resourcePacketDevice(), + "packet_ssh_key": resourcePacketSSHKey(), + "packet_project": resourcePacketProject(), + "packet_volume": resourcePacketVolume(), + "packet_ip_address": resourcePacketIPAddress(), + "packet_ip_reservation": resourcePacketIPReservation(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/packet/resource_packet_ip_address.go b/builtin/providers/packet/resource_packet_ip_address.go index 502e378b31..fd8b61fddf 100644 --- a/builtin/providers/packet/resource_packet_ip_address.go +++ b/builtin/providers/packet/resource_packet_ip_address.go @@ -14,8 +14,6 @@ func resourcePacketIPAddress() *schema.Resource { Schema: map[string]*schema.Schema{ "address": &schema.Schema{ Type: schema.TypeString, - Required: true, - Optional: true, Computed: true, }, diff --git a/builtin/providers/packet/resource_packet_ip_reservation.go b/builtin/providers/packet/resource_packet_ip_reservation.go index 80547f3c01..51331e7eb9 100644 --- a/builtin/providers/packet/resource_packet_ip_reservation.go +++ b/builtin/providers/packet/resource_packet_ip_reservation.go @@ -14,8 +14,6 @@ func resourcePacketIPReservation() *schema.Resource { Schema: map[string]*schema.Schema{ "address": &schema.Schema{ Type: schema.TypeString, - Required: true, - Optional: true, Computed: true, }, From f50c6b91282129f890e5f3477fb7e7816da0d9a0 Mon Sep 17 00:00:00 2001 From: Ayu Demura Date: Mon, 25 Jul 2016 09:41:50 -0400 Subject: [PATCH 05/13] Failed test cases --- .../providers/packet/resource_packet_ip_address.go | 9 +++++---- .../packet/resource_packet_ip_reservation.go | 2 +- .../packet/resource_packet_ip_reservation_test.go | 6 ++---- .../providers/packet/resource_packet_volume_test.go | 13 +++++++++++-- vendor/github.com/packethost/packngo/volumes.go | 2 +- vendor/vendor.json | 6 +++--- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/builtin/providers/packet/resource_packet_ip_address.go b/builtin/providers/packet/resource_packet_ip_address.go index fd8b61fddf..0b76fcd166 100644 --- a/builtin/providers/packet/resource_packet_ip_address.go +++ b/builtin/providers/packet/resource_packet_ip_address.go @@ -14,7 +14,8 @@ func resourcePacketIPAddress() *schema.Resource { Schema: map[string]*schema.Schema{ "address": &schema.Schema{ Type: schema.TypeString, - Computed: true, + Required: true, + ForceNew: true, }, "gateway": &schema.Schema{ @@ -28,12 +29,12 @@ func resourcePacketIPAddress() *schema.Resource { }, "family": &schema.Schema{ - Type: schema.TypeInt, + Type: schema.TypeString, Computed: true, }, "netmask": &schema.Schema{ - Type: schema.TypeInt, + Type: schema.TypeString, Computed: true, }, @@ -48,7 +49,7 @@ func resourcePacketIPAddress() *schema.Resource { }, "assigned_to": &schema.Schema{ - Type: schema.TypeBool, + Type: schema.TypeString, Computed: true, }, diff --git a/builtin/providers/packet/resource_packet_ip_reservation.go b/builtin/providers/packet/resource_packet_ip_reservation.go index 51331e7eb9..14d327b807 100644 --- a/builtin/providers/packet/resource_packet_ip_reservation.go +++ b/builtin/providers/packet/resource_packet_ip_reservation.go @@ -28,7 +28,7 @@ func resourcePacketIPReservation() *schema.Resource { }, "family": &schema.Schema{ - Type: schema.TypeInt, + Type: schema.TypeString, Computed: true, }, diff --git a/builtin/providers/packet/resource_packet_ip_reservation_test.go b/builtin/providers/packet/resource_packet_ip_reservation_test.go index 954e7c3c5c..b47963977f 100644 --- a/builtin/providers/packet/resource_packet_ip_reservation_test.go +++ b/builtin/providers/packet/resource_packet_ip_reservation_test.go @@ -23,7 +23,7 @@ func TestAccPacketIPReservation_Basic(t *testing.T) { testAccCheckPacketIPReservationExists("packet_ip_reservation.foobar", &ip_reservation), testAccCheckPacketIPReservationAttributes(&ip_reservation), resource.TestCheckResourceAttr( - "packet_ip_reservation.foobar", "address", "foobar"), + "packet_ip_reservation", "foobar", "{}"), ), }, }, @@ -81,6 +81,4 @@ func testAccCheckPacketIPReservationExists(n string, ip_reservation *packngo.IPR } var testAccCheckPacketIPReservationConfig_basic = fmt.Sprintf(` -resource "packet_ip_reservation" "foobar" { - address = "foobar" -}`) +resource "packet_ip_reservation" "foobar" {}`) diff --git a/builtin/providers/packet/resource_packet_volume_test.go b/builtin/providers/packet/resource_packet_volume_test.go index 9d866e43b7..b2db4e3f0d 100644 --- a/builtin/providers/packet/resource_packet_volume_test.go +++ b/builtin/providers/packet/resource_packet_volume_test.go @@ -23,7 +23,13 @@ func TestAccPacketVolume_Basic(t *testing.T) { testAccCheckPacketVolumeExists("packet_volume.foobar", &volume), testAccCheckPacketVolumeAttributes(&volume), resource.TestCheckResourceAttr( - "packet_volume.foobar", "name", "foobar"), + "packet_volume.foobar", "project_id", "foobar"), + resource.TestCheckResourceAttr( + "packet_volume.foobar", "plan", "foobar"), + resource.TestCheckResourceAttr( + "packet_volume.foobar", "facility", "foobar"), + resource.TestCheckResourceAttr( + "packet_volume.foobar", "billing_cycle", "hourly"), ), }, }, @@ -82,5 +88,8 @@ func testAccCheckPacketVolumeExists(n string, volume *packngo.Volume) resource.T var testAccCheckPacketVolumeConfig_basic = fmt.Sprintf(` resource "packet_volume" "foobar" { - name = "foobar" + project_id = "foobar" + plan = "foobar" + facility = "foobar" + billing_cycle = "hourly" }`) diff --git a/vendor/github.com/packethost/packngo/volumes.go b/vendor/github.com/packethost/packngo/volumes.go index 6b6171f953..6a70378267 100644 --- a/vendor/github.com/packethost/packngo/volumes.go +++ b/vendor/github.com/packethost/packngo/volumes.go @@ -36,7 +36,7 @@ type SnapshotPolicy struct { ID string `json:"id"` Href string `json:"href"` SnapshotFrequency string `json:"snapshot_frequency,omitempty"` - snapshot_count int `json:"snapshot_count,omitempty"` + SnapshotCount int `json:"snapshot_count,omitempty"` } // Attachment used to execute actions on volume diff --git a/vendor/vendor.json b/vendor/vendor.json index c55457f687..3190596930 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1464,10 +1464,10 @@ "revision": "3d184cea22ee1c41ec1697e0d830ff0c78f7ea97" }, { - "checksumSHA1": "/r0HjrIkynh4Gj0EIAKirEKP/Rk=", + "checksumSHA1": "bpR9eoBfEDcXrm5Q0By4cDDlXo8=", "path": "github.com/packethost/packngo", - "revision": "1bf5adfc4f590757da03407f576c46569a9c04b0", - "revisionTime": "2016-07-22T15:57:43Z" + "revision": "5c74c3276242fb359a06b643437a9d8903eb76e6", + "revisionTime": "2016-07-26T11:06:53Z" }, { "path": "github.com/pborman/uuid", From 18814655f8f3ab28da87c97abcf87da62b683b34 Mon Sep 17 00:00:00 2001 From: Ayu Demura Date: Wed, 27 Jul 2016 12:10:36 -0400 Subject: [PATCH 06/13] wip --- .../packet/resource_packet_ip_address.go | 40 +++---------------- .../packet/resource_packet_ip_reservation.go | 39 +++++------------- .../resource_packet_ip_reservation_test.go | 6 ++- .../packet/resource_packet_volume.go | 5 +++ 4 files changed, 25 insertions(+), 65 deletions(-) diff --git a/builtin/providers/packet/resource_packet_ip_address.go b/builtin/providers/packet/resource_packet_ip_address.go index 0b76fcd166..3bffdf443b 100644 --- a/builtin/providers/packet/resource_packet_ip_address.go +++ b/builtin/providers/packet/resource_packet_ip_address.go @@ -12,47 +12,17 @@ func resourcePacketIPAddress() *schema.Resource { Delete: resourcePacketIPAddressDelete, Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "address": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "gateway": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "network": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "family": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "netmask": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "cidr": &schema.Schema{ - Type: schema.TypeInt, - Computed: true, - }, - - "public": &schema.Schema{ - Type: schema.TypeBool, - Computed: true, - }, - - "assigned_to": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "created": &schema.Schema{ Type: schema.TypeString, Computed: true, diff --git a/builtin/providers/packet/resource_packet_ip_reservation.go b/builtin/providers/packet/resource_packet_ip_reservation.go index 14d327b807..286df050bc 100644 --- a/builtin/providers/packet/resource_packet_ip_reservation.go +++ b/builtin/providers/packet/resource_packet_ip_reservation.go @@ -12,44 +12,27 @@ func resourcePacketIPReservation() *schema.Resource { Delete: resourcePacketIPReservationDelete, Schema: map[string]*schema.Schema{ - "address": &schema.Schema{ + "id": &schema.Schema{ Type: schema.TypeString, Computed: true, }, - "gateway": &schema.Schema{ + "type": &schema.Schema{ Type: schema.TypeString, - Computed: true, + Required: true, + ForceNew: true, }, - "network": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "family": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "netmask": &schema.Schema{ + "quantity": &schema.Schema{ Type: schema.TypeInt, - Computed: true, + Optional: true, + ForceNew: true, }, - "cidr": &schema.Schema{ - Type: schema.TypeInt, - Computed: true, - }, - - "public": &schema.Schema{ - Type: schema.TypeBool, - Computed: true, - }, - - "assigned_to": &schema.Schema{ - Type: schema.TypeBool, - Computed: true, + "comments": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, }, "created": &schema.Schema{ diff --git a/builtin/providers/packet/resource_packet_ip_reservation_test.go b/builtin/providers/packet/resource_packet_ip_reservation_test.go index b47963977f..6c2c962fa2 100644 --- a/builtin/providers/packet/resource_packet_ip_reservation_test.go +++ b/builtin/providers/packet/resource_packet_ip_reservation_test.go @@ -23,7 +23,7 @@ func TestAccPacketIPReservation_Basic(t *testing.T) { testAccCheckPacketIPReservationExists("packet_ip_reservation.foobar", &ip_reservation), testAccCheckPacketIPReservationAttributes(&ip_reservation), resource.TestCheckResourceAttr( - "packet_ip_reservation", "foobar", "{}"), + "packet_ip_reservation.foobar", "type", "foobar"), ), }, }, @@ -81,4 +81,6 @@ func testAccCheckPacketIPReservationExists(n string, ip_reservation *packngo.IPR } var testAccCheckPacketIPReservationConfig_basic = fmt.Sprintf(` -resource "packet_ip_reservation" "foobar" {}`) +resource "packet_ip_reservation" "foobar" { + type = "foobar" +}`) diff --git a/builtin/providers/packet/resource_packet_volume.go b/builtin/providers/packet/resource_packet_volume.go index 0b121557c3..d212a1561d 100644 --- a/builtin/providers/packet/resource_packet_volume.go +++ b/builtin/providers/packet/resource_packet_volume.go @@ -15,6 +15,11 @@ func resourcePacketVolume() *schema.Resource { Delete: resourcePacketVolumeDelete, Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "project_id": &schema.Schema{ Type: schema.TypeString, Required: true, From d60cf4d77792a8cbd66722d70bcdc01cf0f0317b Mon Sep 17 00:00:00 2001 From: Aaron Welch Date: Wed, 10 Aug 2016 11:37:17 -0700 Subject: [PATCH 07/13] update acceptance tests for packet volume, update packngo api client --- .../packet/resource_packet_project_test.go | 2 +- .../packet/resource_packet_volume_test.go | 51 +++++++++++-------- .../github.com/packethost/packngo/volumes.go | 2 +- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/builtin/providers/packet/resource_packet_project_test.go b/builtin/providers/packet/resource_packet_project_test.go index ff1b45f7c6..1ba91b1fab 100644 --- a/builtin/providers/packet/resource_packet_project_test.go +++ b/builtin/providers/packet/resource_packet_project_test.go @@ -38,7 +38,7 @@ func testAccCheckPacketProjectDestroy(s *terraform.State) error { continue } if _, _, err := client.Projects.Get(rs.Primary.ID); err == nil { - return fmt.Errorf("Project cstill exists") + return fmt.Errorf("Project still exists") } } diff --git a/builtin/providers/packet/resource_packet_volume_test.go b/builtin/providers/packet/resource_packet_volume_test.go index b2db4e3f0d..1cf316a727 100644 --- a/builtin/providers/packet/resource_packet_volume_test.go +++ b/builtin/providers/packet/resource_packet_volume_test.go @@ -2,6 +2,7 @@ package packet import ( "fmt" + "os" "testing" "github.com/hashicorp/terraform/helper/resource" @@ -12,24 +13,26 @@ import ( func TestAccPacketVolume_Basic(t *testing.T) { var volume packngo.Volume + project_id := os.Getenv("PACKET_PROJECT_ID") + facility := os.Getenv("PACKET_FACILITY") + resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: testAccPacketVolumePreCheck(t), Providers: testAccProviders, CheckDestroy: testAccCheckPacketVolumeDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccCheckPacketVolumeConfig_basic, + Config: fmt.Sprintf(testAccCheckPacketVolumeConfig_basic, project_id, facility), Check: resource.ComposeTestCheckFunc( testAccCheckPacketVolumeExists("packet_volume.foobar", &volume), - testAccCheckPacketVolumeAttributes(&volume), resource.TestCheckResourceAttr( - "packet_volume.foobar", "project_id", "foobar"), + "packet_volume.foobar", "project_id", project_id), resource.TestCheckResourceAttr( - "packet_volume.foobar", "plan", "foobar"), - resource.TestCheckResourceAttr( - "packet_volume.foobar", "facility", "foobar"), + "packet_volume.foobar", "plan", "storage_1"), resource.TestCheckResourceAttr( "packet_volume.foobar", "billing_cycle", "hourly"), + resource.TestCheckResourceAttr( + "packet_volume.foobar", "size", "100"), ), }, }, @@ -44,22 +47,13 @@ func testAccCheckPacketVolumeDestroy(s *terraform.State) error { continue } if _, _, err := client.Volumes.Get(rs.Primary.ID); err == nil { - return fmt.Errorf("Volume cstill exists") + return fmt.Errorf("Volume still exists") } } return nil } -func testAccCheckPacketVolumeAttributes(volume *packngo.Volume) resource.TestCheckFunc { - return func(s *terraform.State) error { - if volume.Name != "foobar" { - return fmt.Errorf("Bad name: %s", volume.Name) - } - return nil - } -} - func testAccCheckPacketVolumeExists(n string, volume *packngo.Volume) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -86,10 +80,23 @@ func testAccCheckPacketVolumeExists(n string, volume *packngo.Volume) resource.T } } -var testAccCheckPacketVolumeConfig_basic = fmt.Sprintf(` +func testAccPacketVolumePreCheck(t *testing.T) func() { + return func() { + testAccPreCheck(t) + if os.Getenv("PACKET_PROJECT_ID") == "" { + t.Fatal("PACKET_PROJECT_ID must be set") + } + if os.Getenv("PACKET_FACILITY") == "" { + t.Fatal("PACKET_FACILITY must be set") + } + } +} + +const testAccCheckPacketVolumeConfig_basic = ` resource "packet_volume" "foobar" { - project_id = "foobar" - plan = "foobar" - facility = "foobar" + plan = "storage_1" billing_cycle = "hourly" -}`) + size = 100 + project_id = "%s" + facility = "%s" +}` diff --git a/vendor/github.com/packethost/packngo/volumes.go b/vendor/github.com/packethost/packngo/volumes.go index 6a70378267..c3c3e561b2 100644 --- a/vendor/github.com/packethost/packngo/volumes.go +++ b/vendor/github.com/packethost/packngo/volumes.go @@ -82,7 +82,7 @@ type VolumeServiceOp struct { // Get returns a volume by id func (v *VolumeServiceOp) Get(volumeID string) (*Volume, *Response, error) { - path := fmt.Sprintf("%s/%s", volumeBasePath, volumeID) + path := fmt.Sprintf("%s/%s?include=facility", volumeBasePath, volumeID) req, err := v.client.NewRequest("GET", path, nil) if err != nil { return nil, nil, err From 3430afb8a54ea800cc26da3cb7f901329b516450 Mon Sep 17 00:00:00 2001 From: Aaron Welch Date: Wed, 10 Aug 2016 11:51:09 -0700 Subject: [PATCH 08/13] remove IP stuff for now --- .../packet/resource_packet_ip_address.go | 99 --------------- .../packet/resource_packet_ip_address_test.go | 86 ------------- .../packet/resource_packet_ip_reservation.go | 116 ------------------ .../resource_packet_ip_reservation_test.go | 86 ------------- 4 files changed, 387 deletions(-) delete mode 100644 builtin/providers/packet/resource_packet_ip_address.go delete mode 100644 builtin/providers/packet/resource_packet_ip_address_test.go delete mode 100644 builtin/providers/packet/resource_packet_ip_reservation.go delete mode 100644 builtin/providers/packet/resource_packet_ip_reservation_test.go diff --git a/builtin/providers/packet/resource_packet_ip_address.go b/builtin/providers/packet/resource_packet_ip_address.go deleted file mode 100644 index 3bffdf443b..0000000000 --- a/builtin/providers/packet/resource_packet_ip_address.go +++ /dev/null @@ -1,99 +0,0 @@ -package packet - -import ( - "github.com/hashicorp/terraform/helper/schema" - "github.com/packethost/packngo" -) - -func resourcePacketIPAddress() *schema.Resource { - return &schema.Resource{ - Create: resourcePacketIPAddressCreate, - Read: resourcePacketIPAddressRead, - Delete: resourcePacketIPAddressDelete, - - Schema: map[string]*schema.Schema{ - "id": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "address": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "created": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "updated": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func resourcePacketIPAddressCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*packngo.Client) - - createRequest := &packngo.IPAddressAssignRequest{ - Address: d.Get("address").(string), - } - - device_id := "" - if attr, ok := d.GetOk("instance_id"); ok { - device_id = attr.(string) - } - - newIPAddress, _, err := client.Ips.Assign(device_id, createRequest) - if err != nil { - return friendlyError(err) - } - - d.SetId(newIPAddress.ID) - - return resourcePacketIPAddressRead(d, meta) -} - -func resourcePacketIPAddressRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*packngo.Client) - - ip_address, _, err := client.Ips.Get(d.Id()) - if err != nil { - err = friendlyError(err) - - // If the ip_address somehow already destroyed, mark as succesfully gone. - if isNotFound(err) { - d.SetId("") - return nil - } - - return err - } - - d.Set("address", ip_address.Address) - d.Set("gateway", ip_address.Gateway) - d.Set("network", ip_address.Network) - d.Set("family", ip_address.AddressFamily) - d.Set("netmask", ip_address.Netmask) - d.Set("public", ip_address.Public) - d.Set("cidr", ip_address.Cidr) - d.Set("assigned_to", ip_address.AssignedTo) - d.Set("created", ip_address.Created) - d.Set("updated", ip_address.Updated) - - return nil -} - -func resourcePacketIPAddressDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*packngo.Client) - - if _, err := client.Ips.Unassign(d.Id()); err != nil { - return friendlyError(err) - } - - return nil -} diff --git a/builtin/providers/packet/resource_packet_ip_address_test.go b/builtin/providers/packet/resource_packet_ip_address_test.go deleted file mode 100644 index 9944eb107c..0000000000 --- a/builtin/providers/packet/resource_packet_ip_address_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package packet - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "github.com/packethost/packngo" -) - -func TestAccPacketIPAddress_Basic(t *testing.T) { - var ip_address packngo.IPAddress - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPacketIPAddressDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccCheckPacketIPAddressConfig_basic, - Check: resource.ComposeTestCheckFunc( - testAccCheckPacketIPAddressExists("packet_ip_address.foobar", &ip_address), - testAccCheckPacketIPAddressAttributes(&ip_address), - resource.TestCheckResourceAttr( - "packet_ip_address.foobar", "address", "foobar"), - ), - }, - }, - }) -} - -func testAccCheckPacketIPAddressDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*packngo.Client) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "packet_ip_address" { - continue - } - if _, _, err := client.Ips.Get(rs.Primary.ID); err == nil { - return fmt.Errorf("IPAddress cstill exists") - } - } - - return nil -} - -func testAccCheckPacketIPAddressAttributes(ip_address *packngo.IPAddress) resource.TestCheckFunc { - return func(s *terraform.State) error { - if ip_address.Address != "foobar" { - return fmt.Errorf("Bad address: %s", ip_address.Address) - } - return nil - } -} - -func testAccCheckPacketIPAddressExists(n string, ip_address *packngo.IPAddress) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - if rs.Primary.ID == "" { - return fmt.Errorf("No Record ID is set") - } - - client := testAccProvider.Meta().(*packngo.Client) - - foundIPAddress, _, err := client.Ips.Get(rs.Primary.ID) - if err != nil { - return err - } - if foundIPAddress.ID != rs.Primary.ID { - return fmt.Errorf("Record not found: %v - %v", rs.Primary.ID, foundIPAddress) - } - - *ip_address = *foundIPAddress - - return nil - } -} - -var testAccCheckPacketIPAddressConfig_basic = fmt.Sprintf(` -resource "packet_ip_address" "foobar" { - address = "foobar" -}`) diff --git a/builtin/providers/packet/resource_packet_ip_reservation.go b/builtin/providers/packet/resource_packet_ip_reservation.go deleted file mode 100644 index 286df050bc..0000000000 --- a/builtin/providers/packet/resource_packet_ip_reservation.go +++ /dev/null @@ -1,116 +0,0 @@ -package packet - -import ( - "github.com/hashicorp/terraform/helper/schema" - "github.com/packethost/packngo" -) - -func resourcePacketIPReservation() *schema.Resource { - return &schema.Resource{ - Create: resourcePacketIPReservationCreate, - Read: resourcePacketIPReservationRead, - Delete: resourcePacketIPReservationDelete, - - Schema: map[string]*schema.Schema{ - "id": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "type": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "quantity": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - }, - - "comments": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - - "created": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - - "updated": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func resourcePacketIPReservationCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*packngo.Client) - - createRequest := &packngo.IPReservationRequest{ - Type: d.Get("type").(string), - Quantity: d.Get("quantity").(int), - Comments: d.Get("comments").(string), - } - - project_id := "" - if attr, ok := d.GetOk("project_id"); ok { - project_id = attr.(string) - } - - newIPReservation, _, err := client.IpReservations.RequestMore(project_id, createRequest) - if err != nil { - return friendlyError(err) - } - - d.SetId(newIPReservation.ID) - - return resourcePacketIPReservationRead(d, meta) -} - -func resourcePacketIPReservationRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*packngo.Client) - - ip_reservation, _, err := client.IpReservations.Get(d.Id()) - if err != nil { - err = friendlyError(err) - - // If the ip_reservation somehow already destroyed, mark as succesfully gone. - if isNotFound(err) { - d.SetId("") - return nil - } - - return err - } - - d.Set("address", ip_reservation.Address) - d.Set("network", ip_reservation.Network) - d.Set("family", ip_reservation.AddressFamily) - d.Set("netmask", ip_reservation.Netmask) - d.Set("public", ip_reservation.Public) - d.Set("cidr", ip_reservation.Cidr) - d.Set("management", ip_reservation.Management) - d.Set("manageable", ip_reservation.Manageable) - d.Set("addon", ip_reservation.Addon) - d.Set("bill", ip_reservation.Bill) - d.Set("assignments", ip_reservation.Assignments) - d.Set("created", ip_reservation.Created) - d.Set("updated", ip_reservation.Updated) - - return nil -} - -func resourcePacketIPReservationDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*packngo.Client) - - if _, err := client.IpReservations.Remove(d.Id()); err != nil { - return friendlyError(err) - } - - return nil -} diff --git a/builtin/providers/packet/resource_packet_ip_reservation_test.go b/builtin/providers/packet/resource_packet_ip_reservation_test.go deleted file mode 100644 index 6c2c962fa2..0000000000 --- a/builtin/providers/packet/resource_packet_ip_reservation_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package packet - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "github.com/packethost/packngo" -) - -func TestAccPacketIPReservation_Basic(t *testing.T) { - var ip_reservation packngo.IPReservation - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPacketIPReservationDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccCheckPacketIPReservationConfig_basic, - Check: resource.ComposeTestCheckFunc( - testAccCheckPacketIPReservationExists("packet_ip_reservation.foobar", &ip_reservation), - testAccCheckPacketIPReservationAttributes(&ip_reservation), - resource.TestCheckResourceAttr( - "packet_ip_reservation.foobar", "type", "foobar"), - ), - }, - }, - }) -} - -func testAccCheckPacketIPReservationDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*packngo.Client) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "packet_ip_reservation" { - continue - } - if _, _, err := client.IpReservations.Get(rs.Primary.ID); err == nil { - return fmt.Errorf("IPReservation cstill exists") - } - } - - return nil -} - -func testAccCheckPacketIPReservationAttributes(ip_reservation *packngo.IPReservation) resource.TestCheckFunc { - return func(s *terraform.State) error { - if ip_reservation.Address != "foobar" { - return fmt.Errorf("Bad address: %s", ip_reservation.Address) - } - return nil - } -} - -func testAccCheckPacketIPReservationExists(n string, ip_reservation *packngo.IPReservation) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - if rs.Primary.ID == "" { - return fmt.Errorf("No Record ID is set") - } - - client := testAccProvider.Meta().(*packngo.Client) - - foundIPReservation, _, err := client.IpReservations.Get(rs.Primary.ID) - if err != nil { - return err - } - if foundIPReservation.ID != rs.Primary.ID { - return fmt.Errorf("Record not found: %v - %v", rs.Primary.ID, foundIPReservation) - } - - *ip_reservation = *foundIPReservation - - return nil - } -} - -var testAccCheckPacketIPReservationConfig_basic = fmt.Sprintf(` -resource "packet_ip_reservation" "foobar" { - type = "foobar" -}`) From 58103a0df9bbeadc5415a5d78527f7e21b0ba60e Mon Sep 17 00:00:00 2001 From: Aaron Welch Date: Wed, 10 Aug 2016 11:55:49 -0700 Subject: [PATCH 09/13] remove ip stuff for now --- builtin/providers/packet/provider.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/builtin/providers/packet/provider.go b/builtin/providers/packet/provider.go index 853e1c2473..58515210c5 100644 --- a/builtin/providers/packet/provider.go +++ b/builtin/providers/packet/provider.go @@ -22,8 +22,6 @@ func Provider() terraform.ResourceProvider { "packet_ssh_key": resourcePacketSSHKey(), "packet_project": resourcePacketProject(), "packet_volume": resourcePacketVolume(), - "packet_ip_address": resourcePacketIPAddress(), - "packet_ip_reservation": resourcePacketIPReservation(), }, ConfigureFunc: providerConfigure, From c5b15da76dd33f32a93de8ed84ae08e6090e6803 Mon Sep 17 00:00:00 2001 From: Aaron Welch Date: Wed, 10 Aug 2016 12:11:04 -0700 Subject: [PATCH 10/13] gofmt! --- builtin/providers/packet/provider.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/providers/packet/provider.go b/builtin/providers/packet/provider.go index 58515210c5..f3a8483370 100644 --- a/builtin/providers/packet/provider.go +++ b/builtin/providers/packet/provider.go @@ -18,10 +18,10 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "packet_device": resourcePacketDevice(), - "packet_ssh_key": resourcePacketSSHKey(), - "packet_project": resourcePacketProject(), - "packet_volume": resourcePacketVolume(), + "packet_device": resourcePacketDevice(), + "packet_ssh_key": resourcePacketSSHKey(), + "packet_project": resourcePacketProject(), + "packet_volume": resourcePacketVolume(), }, ConfigureFunc: providerConfigure, From 79fa978896fb4ebd6562c75b33d44382c867eb6b Mon Sep 17 00:00:00 2001 From: Aaron Welch Date: Thu, 11 Aug 2016 09:40:23 -0700 Subject: [PATCH 11/13] 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 d212a1561d..c5dc0a8876 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 1cf316a727..cffd55f13e 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 0000000000..3179518eae --- /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 f7464f198e..8591e23e3d 100644 --- a/website/source/layouts/packet.erb +++ b/website/source/layouts/packet.erb @@ -22,6 +22,9 @@ > packet_ssh_key + > + packet_volume + From 3671bae61025566071c0be2ba15725fd52e3265e Mon Sep 17 00:00:00 2001 From: Ayu Demura Date: Thu, 11 Aug 2016 13:20:15 -0400 Subject: [PATCH 12/13] Update packngo once again --- command/internal_plugin_list.go | 2 +- vendor/github.com/packethost/packngo/volumes.go | 4 ++-- vendor/vendor.json | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/command/internal_plugin_list.go b/command/internal_plugin_list.go index e4807b7992..e3fc743c32 100644 --- a/command/internal_plugin_list.go +++ b/command/internal_plugin_list.go @@ -61,6 +61,7 @@ import ( ) var InternalProviders = map[string]plugin.ProviderFunc{ + "archive": archiveprovider.Provider, "atlas": atlasprovider.Provider, "aws": awsprovider.Provider, "azure": azureprovider.Provider, @@ -105,7 +106,6 @@ var InternalProviders = map[string]plugin.ProviderFunc{ "ultradns": ultradnsprovider.Provider, "vcd": vcdprovider.Provider, "vsphere": vsphereprovider.Provider, - "archive": archiveprovider.Provider, } var InternalProvisioners = map[string]plugin.ProvisionerFunc{ diff --git a/vendor/github.com/packethost/packngo/volumes.go b/vendor/github.com/packethost/packngo/volumes.go index c3c3e561b2..96ab3b6e8e 100644 --- a/vendor/github.com/packethost/packngo/volumes.go +++ b/vendor/github.com/packethost/packngo/volumes.go @@ -56,7 +56,7 @@ type VolumeCreateRequest struct { ProjectID string `json:"project_id"` PlanID string `json:"plan_id"` FacilityID string `json:"facility_id"` - Description string `json:"Description,omitempty"` + Description string `json:"description,omitempty"` SnapshotPolicies []*SnapshotPolicy `json:"snapshot_policies,omitempty"` } @@ -82,7 +82,7 @@ type VolumeServiceOp struct { // Get returns a volume by id func (v *VolumeServiceOp) Get(volumeID string) (*Volume, *Response, error) { - path := fmt.Sprintf("%s/%s?include=facility", volumeBasePath, volumeID) + path := fmt.Sprintf("%s/%s?include=facility,snapshot_policies,attachments.device", volumeBasePath, volumeID) req, err := v.client.NewRequest("GET", path, nil) if err != nil { return nil, nil, err diff --git a/vendor/vendor.json b/vendor/vendor.json index 3190596930..d8c7daa5f0 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1464,10 +1464,10 @@ "revision": "3d184cea22ee1c41ec1697e0d830ff0c78f7ea97" }, { - "checksumSHA1": "bpR9eoBfEDcXrm5Q0By4cDDlXo8=", + "checksumSHA1": "MexE5QPVAwVfQcJBnMGMgD+s9L0=", "path": "github.com/packethost/packngo", - "revision": "5c74c3276242fb359a06b643437a9d8903eb76e6", - "revisionTime": "2016-07-26T11:06:53Z" + "revision": "7cd5fed006859e86dd5641a6cf9812e855b7574a", + "revisionTime": "2016-08-11T16:27:25Z" }, { "path": "github.com/pborman/uuid", From ffd04882e7072d93ab194a306df6f2a0421d0125 Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 15 Aug 2016 08:38:43 +0100 Subject: [PATCH 13/13] provider/packet: Restructure the Packet Volume test to no longer rely on environment variables --- .../packet/resource_packet_volume_test.go | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/builtin/providers/packet/resource_packet_volume_test.go b/builtin/providers/packet/resource_packet_volume_test.go index cffd55f13e..cc487d8979 100644 --- a/builtin/providers/packet/resource_packet_volume_test.go +++ b/builtin/providers/packet/resource_packet_volume_test.go @@ -2,9 +2,9 @@ package packet import ( "fmt" - "os" "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/packethost/packngo" @@ -13,20 +13,17 @@ import ( func TestAccPacketVolume_Basic(t *testing.T) { var volume packngo.Volume - project_id := os.Getenv("PACKET_PROJECT_ID") - facility := os.Getenv("PACKET_FACILITY") + rs := acctest.RandString(10) resource.Test(t, resource.TestCase{ - PreCheck: testAccPacketVolumePreCheck(t), + PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckPacketVolumeDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: fmt.Sprintf(testAccCheckPacketVolumeConfig_basic, project_id, facility), + Config: fmt.Sprintf(testAccCheckPacketVolumeConfig_basic, rs), Check: resource.ComposeTestCheckFunc( testAccCheckPacketVolumeExists("packet_volume.foobar", &volume), - resource.TestCheckResourceAttr( - "packet_volume.foobar", "project_id", project_id), resource.TestCheckResourceAttr( "packet_volume.foobar", "plan", "storage_1"), resource.TestCheckResourceAttr( @@ -80,24 +77,16 @@ func testAccCheckPacketVolumeExists(n string, volume *packngo.Volume) resource.T } } -func testAccPacketVolumePreCheck(t *testing.T) func() { - return func() { - testAccPreCheck(t) - if os.Getenv("PACKET_PROJECT_ID") == "" { - t.Fatal("PACKET_PROJECT_ID must be set") - } - if os.Getenv("PACKET_FACILITY") == "" { - t.Fatal("PACKET_FACILITY must be set") - } - } +const testAccCheckPacketVolumeConfig_basic = ` +resource "packet_project" "foobar" { + name = "%s" } -const testAccCheckPacketVolumeConfig_basic = ` resource "packet_volume" "foobar" { plan = "storage_1" billing_cycle = "hourly" size = 100 - project_id = "%s" - facility = "%s" + project_id = "${packet_project.foobar.id}" + facility = "ewr1" snapshot_policies = { snapshot_frequency = "1day", snapshot_count = 7 } }`