cmd/packet: Update the vendored packet.net go binding

Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
This commit is contained in:
Rolf Neugebauer 2018-01-08 16:23:33 +00:00
parent aa59ffb82d
commit b272819540
18 changed files with 468 additions and 624 deletions

View File

@ -31,7 +31,7 @@ github.com/moby/vpnkit 0e4293bb1058598c4b0a406ed171f52573ef414c
github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
github.com/opencontainers/image-spec v1.0.0
github.com/opencontainers/runtime-spec v1.0.0
github.com/packethost/packngo 131798f2804a1b3e895ca98047d56f0d7e094e2a
github.com/packethost/packngo f1be085ecd6fca1b0a0e25eda71f208dcfcee5ab
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0
github.com/radu-matei/azure-sdk-for-go 3b12823551999669c9a325a32472508e0af7978e

View File

@ -1,60 +0,0 @@
## About
This directory contains a collection of scripts used to build and manage this
repository. If there are any issues regarding the intention of a particular
script (or even part of a certain script), please reach out to us.
It may help us either refine our current scripts, or add on new ones
that are appropriate for a given use case.
## DinD (dind.sh)
DinD is a wrapper script which allows Docker to be run inside a Docker
container. DinD requires the container to
be run with privileged mode enabled.
## Generate Authors (generate-authors.sh)
Generates AUTHORS; a file with all the names and corresponding emails of
individual contributors. AUTHORS can be found in the home directory of
this repository.
## Make
There are two make files, each with different extensions. Neither are supposed
to be called directly; only invoke `make`. Both scripts run inside a Docker
container.
### make.ps1
- The Windows native build script that uses PowerShell semantics; it is limited
unlike `hack\make.sh` since it does not provide support for the full set of
operations provided by the Linux counterpart, `make.sh`. However, `make.ps1`
does provide support for local Windows development and Windows to Windows CI.
More information is found within `make.ps1` by the author, @jhowardmsft
### make.sh
- Referenced via `make test` when running tests on a local machine,
or directly referenced when running tests inside a Docker development container.
- When running on a local machine, `make test` to run all tests found in
`test`, `test-unit`, `test-integration`, and `test-docker-py` on
your local machine. The default timeout is set in `make.sh` to 60 minutes
(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run
all of the tests.
- When running inside a Docker development container, `hack/make.sh` does
not have a single target that runs all the tests. You need to provide a
single command line with multiple targets that performs the same thing.
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration test-docker-py`
- For more information related to testing outside the scope of this README,
refer to
[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/)
## Release (release.sh)
Releases any bundles built by `make` on a public AWS S3 bucket.
For information regarding configuration, please view `release.sh`.
## Vendor (vendor.sh)
A shell script that is a wrapper around Vndr. For information on how to use
this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md)

View File

@ -1,69 +0,0 @@
# Integration Testing on Swarm
IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster
## Architecture
### Master service
- Works as a funker caller
- Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`)
### Worker service
- Works as a funker callee
- Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration-cli` using the bind-mounted API socket (`docker.sock`)
### Client
- Controls master and workers via `docker stack`
- No need to have a local daemon
Typically, the master and workers are supposed to be running on a cloud environment,
while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows.
## Requirement
- Docker daemon 1.13 or later
- Private registry for distributed execution with multiple nodes
## Usage
### Step 1: Prepare images
$ make build-integration-cli-on-swarm
Following environment variables are known to work in this step:
- `BUILDFLAGS`
- `DOCKER_INCREMENTAL_BINARY`
Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`.
### Step 2: Execute tests
$ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest
Following environment variables are known to work in this step:
- `DOCKER_GRAPHDRIVER`
- `DOCKER_EXPERIMENTAL`
#### Flags
Basic flags:
- `-replicas N`: the number of worker service replicas. i.e. degree of parallelism.
- `-chunks N`: the number of chunks. By default, `chunks` == `replicas`.
- `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`.
Experimental flags for mitigating makespan nonuniformity:
- `-shuffle`: Shuffle the test filter strings
Flags for debugging IT on Swarm itself:
- `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used.
- `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated.
- `-dry-run`: skip the actual workload
- `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm

View File

@ -1,2 +0,0 @@
# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here
github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773

View File

@ -7,3 +7,20 @@ Committing
----------
Before committing, it's a good idea to run `gofmt -w *.go`. ([gofmt](https://golang.org/cmd/gofmt/))
Acceptance Tests
----------------
If you want to run tests against the actual Packet API, you must set envvar `PACKET_TEST_ACTUAL_API` to non-empty string for the `go test`. The device tests wait for the device creation, so it's best to run a few in parallel.
To run all the tests, you can do
```
$ PACKNGO_TEST_ACTUAL_API=1 go test -v -parallel 8
```
It's also useful to run only single acceptance test at a time:
```
$ PACKNGO_TEST_ACTUAL_API=1 go test -v -run=TestAccDeviceBasic
```

View File

@ -32,16 +32,35 @@ type Device struct {
Updated string `json:"updated_at,omitempty"`
Locked bool `json:"locked,omitempty"`
BillingCycle string `json:"billing_cycle,omitempty"`
Storage map[string]interface{} `json:"storage,omitempty"`
Tags []string `json:"tags,omitempty"`
Network []*IPAddress `json:"ip_addresses"`
Network []*IPAddressAssignment `json:"ip_addresses"`
Volumes []*Volume `json:"volumes"`
OS *OS `json:"operating_system,omitempty"`
Plan *Plan `json:"plan,omitempty"`
Facility *Facility `json:"facility,omitempty"`
Project *Project `json:"project,omitempty"`
ProvisionEvents []*ProvisionEvent `json:"provisioning_events,omitempty"`
ProvisionPer float32 `json:"provisioning_percentage,omitempty"`
UserData string `json:"userdata",omitempty`
IPXEScriptUrl string `json:"ipxe_script_url,omitempty"`
UserData string `json:"userdata,omitempty"`
RootPassword string `json:"root_password,omitempty"`
IPXEScriptURL string `json:"ipxe_script_url,omitempty"`
AlwaysPXE bool `json:"always_pxe,omitempty"`
HardwareReservation Href `json:"hardware_reservation,omitempty"`
SpotInstance bool `json:"spot_instance,omitempty"`
SpotPriceMax float64 `json:"spot_price_max,omitempty"`
TerminationTime *Timestamp `json:"termination_time,omitempty"`
}
type ProvisionEvent struct {
ID string `json:"id"`
Body string `json:"body"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
Href string `json:"href"`
Interpolated string `json:"interpolated"`
Relationships []Href `json:"relationships"`
State string `json:"state"`
Type string `json:"type"`
}
func (d Device) String() string {
@ -50,28 +69,33 @@ func (d Device) String() string {
// DeviceCreateRequest type used to create a Packet device
type DeviceCreateRequest struct {
HostName string `json:"hostname"`
Hostname string `json:"hostname"`
Plan string `json:"plan"`
Facility string `json:"facility"`
OS string `json:"operating_system"`
BillingCycle string `json:"billing_cycle"`
ProjectID string `json:"project_id"`
UserData string `json:"userdata"`
Storage string `json:"storage,omitempty"`
Tags []string `json:"tags"`
IPXEScriptUrl string `json:"ipxe_script_url,omitempty"`
IPXEScriptURL string `json:"ipxe_script_url,omitempty"`
PublicIPv4SubnetSize int `json:"public_ipv4_subnet_size,omitempty"`
AlwaysPXE bool `json:"always_pxe,omitempty"`
HardwareReservationID string `json:"hardware_reservation_id,omitempty"`
SpotInstance bool `json:"spot_instance,omitempty"`
SpotPriceMax float64 `json:"spot_price_max,omitempty,string"`
TerminationTime *Timestamp `json:"termination_time,omitempty"`
}
// DeviceUpdateRequest type used to update a Packet device
type DeviceUpdateRequest struct {
HostName string `json:"hostname"`
Hostname string `json:"hostname"`
Description string `json:"description"`
UserData string `json:"userdata"`
Locked bool `json:"locked"`
Tags []string `json:"tags"`
AlwaysPXE bool `json:"always_pxe,omitempty"`
IPXEScriptUrl string `json:"ipxe_script_url,omitempty"`
IPXEScriptURL string `json:"ipxe_script_url,omitempty"`
}
func (d DeviceCreateRequest) String() string {
@ -95,14 +119,9 @@ type DeviceServiceOp struct {
// List returns devices on a project
func (s *DeviceServiceOp) List(projectID string) ([]Device, *Response, error) {
path := fmt.Sprintf("%s/%s/devices?include=facility", projectBasePath, projectID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(devicesRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.DoRequest("GET", path, nil, root)
if err != nil {
return nil, resp, err
}
@ -113,14 +132,9 @@ func (s *DeviceServiceOp) List(projectID string) ([]Device, *Response, error) {
// Get returns a device by id
func (s *DeviceServiceOp) Get(deviceID string) (*Device, *Response, error) {
path := fmt.Sprintf("%s/%s?include=facility", deviceBasePath, deviceID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
device := new(Device)
resp, err := s.client.Do(req, device)
resp, err := s.client.DoRequest("GET", path, nil, device)
if err != nil {
return nil, resp, err
}
@ -131,14 +145,9 @@ func (s *DeviceServiceOp) Get(deviceID string) (*Device, *Response, error) {
// Create creates a new device
func (s *DeviceServiceOp) Create(createRequest *DeviceCreateRequest) (*Device, *Response, error) {
path := fmt.Sprintf("%s/%s/devices", projectBasePath, createRequest.ProjectID)
req, err := s.client.NewRequest("POST", path, createRequest)
if err != nil {
return nil, nil, err
}
device := new(Device)
resp, err := s.client.Do(req, device)
resp, err := s.client.DoRequest("POST", path, createRequest, device)
if err != nil {
return nil, resp, err
}
@ -149,14 +158,9 @@ func (s *DeviceServiceOp) Create(createRequest *DeviceCreateRequest) (*Device, *
// Update updates an existing device
func (s *DeviceServiceOp) Update(deviceID string, updateRequest *DeviceUpdateRequest) (*Device, *Response, error) {
path := fmt.Sprintf("%s/%s?include=facility", deviceBasePath, deviceID)
req, err := s.client.NewRequest("PUT", path, updateRequest)
if err != nil {
return nil, nil, err
}
device := new(Device)
resp, err := s.client.Do(req, device)
resp, err := s.client.DoRequest("PUT", path, updateRequest, device)
if err != nil {
return nil, resp, err
}
@ -168,59 +172,31 @@ func (s *DeviceServiceOp) Update(deviceID string, updateRequest *DeviceUpdateReq
func (s *DeviceServiceOp) Delete(deviceID string) (*Response, error) {
path := fmt.Sprintf("%s/%s", deviceBasePath, deviceID)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
return s.client.DoRequest("DELETE", path, nil, nil)
}
// Reboot reboots on a device
func (s *DeviceServiceOp) Reboot(deviceID string) (*Response, error) {
path := fmt.Sprintf("%s/%s/actions", deviceBasePath, deviceID)
action := &DeviceActionRequest{Type: "reboot"}
req, err := s.client.NewRequest("POST", path, action)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
return s.client.DoRequest("POST", path, action, nil)
}
// PowerOff powers on a device
func (s *DeviceServiceOp) PowerOff(deviceID string) (*Response, error) {
path := fmt.Sprintf("%s/%s/actions", deviceBasePath, deviceID)
action := &DeviceActionRequest{Type: "power_off"}
req, err := s.client.NewRequest("POST", path, action)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
return s.client.DoRequest("POST", path, action, nil)
}
// PowerOn powers on a device
func (s *DeviceServiceOp) PowerOn(deviceID string) (*Response, error) {
path := fmt.Sprintf("%s/%s/actions", deviceBasePath, deviceID)
action := &DeviceActionRequest{Type: "power_on"}
req, err := s.client.NewRequest("POST", path, action)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
return s.client.DoRequest("POST", path, action, nil)
}
type lockDeviceType struct {
@ -230,30 +206,15 @@ type lockDeviceType struct {
// Lock sets a device to "locked"
func (s *DeviceServiceOp) Lock(deviceID string) (*Response, error) {
path := fmt.Sprintf("%s/%s", deviceBasePath, deviceID)
action := lockDeviceType{Locked: true}
req, err := s.client.NewRequest("PATCH", path, action)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
return s.client.DoRequest("PATCH", path, action, nil)
}
// Unlock sets a device to "locked"
func (s *DeviceServiceOp) Unlock(deviceID string) (*Response, error) {
path := fmt.Sprintf("%s/%s", deviceBasePath, deviceID)
action := lockDeviceType{Locked: false}
req, err := s.client.NewRequest("PATCH", path, action)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
return s.client.DoRequest("PATCH", path, action, nil)
}

View File

@ -26,13 +26,9 @@ type EmailServiceOp struct {
// 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 {
return nil, nil, err
}
email := new(Email)
resp, err := s.client.Do(req, email)
resp, err := s.client.DoRequest("GET", emailBasePath, nil, email)
if err != nil {
return nil, resp, err
}

View File

@ -41,13 +41,9 @@ type FacilityServiceOp struct {
// List returns all available Packet facilities
func (s *FacilityServiceOp) List() ([]Facility, *Response, error) {
req, err := s.client.NewRequest("GET", facilityBasePath, nil)
if err != nil {
return nil, nil, err
}
root := new(facilityRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.DoRequest("GET", facilityBasePath, nil, root)
if err != nil {
return nil, resp, err
}

View File

@ -1,18 +1,28 @@
package packngo
import "fmt"
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)
// DeviceIPService handles assignment of addresses from reserved blocks to instances in a project.
type DeviceIPService interface {
Assign(deviceID string, assignRequest *AddressStruct) (*IPAddressAssignment, *Response, error)
Unassign(assignmentID string) (*Response, error)
Get(assignmentID string) (*IPAddressAssignment, *Response, error)
}
// IPAddress represents a ip address
type IPAddress struct {
// ProjectIPService handles reservation of IP address blocks for a project.
type ProjectIPService interface {
Get(reservationID string) (*IPAddressReservation, *Response, error)
List(projectID string) ([]IPAddressReservation, *Response, error)
Request(projectID string, ipReservationReq *IPReservationRequest) (*IPAddressReservation, *Response, error)
Remove(ipReservationID string) (*Response, error)
AvailableAddresses(ipReservationID string, r *AvailableRequest) ([]string, *Response, error)
}
type ipAddressCommon struct {
ID string `json:"id"`
Address string `json:"address"`
Gateway string `json:"gateway"`
@ -20,91 +30,42 @@ type IPAddress struct {
AddressFamily int `json:"address_family"`
Netmask string `json:"netmask"`
Public bool `json:"public"`
Cidr int `json:"cidr"`
AssignedTo map[string]string `json:"assigned_to"`
CIDR int `json:"cidr"`
Created string `json:"created_at,omitempty"`
Updated string `json:"updated_at,omitempty"`
Href string `json:"href"`
Management bool `json:"management"`
Manageable bool `json:"manageable"`
Project Href `json:"project"`
}
// IPAddressReservation is created when user sends IP reservation request for a project (considering it's within quota).
type IPAddressReservation struct {
ipAddressCommon
Assignments []Href `json:"assignments"`
Facility Facility `json:"facility,omitempty"`
Available string `json:"available"`
Addon bool `json:"addon"`
Bill bool `json:"bill"`
}
// IPAddressAssignRequest represents the body if a ip assign request
type IPAddressAssignRequest struct {
Address string `json:"address"`
// AvailableResponse is a type for listing of available addresses from a reserved block.
type AvailableResponse struct {
Available []string `json:"available"`
}
func (i IPAddress) String() string {
return Stringify(i)
// AvailableRequest is a type for listing available addresses from a reserved block.
type AvailableRequest struct {
CIDR int `json:"cidr"`
}
// IPServiceOp implements IPService
type IPServiceOp struct {
client *Client
// IPAddressAssignment is created when an IP address from reservation block is assigned to a device.
type IPAddressAssignment struct {
ipAddressCommon
AssignedTo Href `json:"assigned_to"`
}
// 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 devices 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) (*IPReservation, *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
// IPReservationRequest represents the body of a reservation request.
type IPReservationRequest struct {
Type string `json:"type"`
Quantity int `json:"quantity"`
@ -112,95 +73,122 @@ type IPReservationRequest struct {
Facility string `json:"facility"`
}
// IPReservation represent an IP reservation for a single project
type IPReservation struct {
ID string `json:"id"`
Network string `json:"network"`
// AddressStruct is a helper type for request/response with dict like {"address": ... }
type AddressStruct struct {
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"`
Created string `json:"created_at,omitempty"`
Updated string `json:"updated_at,omitempty"`
Href string `json:"href"`
Facility Facility `json:"facility,omitempty"`
}
type ipReservationRoot struct {
IPReservations []IPReservation `json:"ip_addresses"`
func deleteFromIP(client *Client, resourceID string) (*Response, error) {
path := fmt.Sprintf("%s/%s", ipBasePath, resourceID)
return client.DoRequest("DELETE", path, nil, nil)
}
func (i IPAddressReservation) String() string {
return Stringify(i)
}
func (i IPAddressAssignment) String() string {
return Stringify(i)
}
// DeviceIPServiceOp is interface for IP-address assignment methods.
type DeviceIPServiceOp struct {
client *Client
}
// Unassign unassigns an IP address from the device to which it is currently assigned.
// 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 *DeviceIPServiceOp) Unassign(assignmentID string) (*Response, error) {
return deleteFromIP(i.client, assignmentID)
}
// Assign assigns an IP address to a device.
// The IP address must be in one of the IP ranges assigned to the devices project.
func (i *DeviceIPServiceOp) Assign(deviceID string, assignRequest *AddressStruct) (*IPAddressAssignment, *Response, error) {
path := fmt.Sprintf("%s/%s%s", deviceBasePath, deviceID, ipBasePath)
ipa := new(IPAddressAssignment)
resp, err := i.client.DoRequest("POST", path, assignRequest, ipa)
if err != nil {
return nil, resp, err
}
return ipa, resp, err
}
// Get returns assignment by ID.
func (i *DeviceIPServiceOp) Get(assignmentID string) (*IPAddressAssignment, *Response, error) {
path := fmt.Sprintf("%s/%s", ipBasePath, assignmentID)
ipa := new(IPAddressAssignment)
resp, err := i.client.DoRequest("GET", path, nil, ipa)
if err != nil {
return nil, resp, err
}
return ipa, resp, err
}
// ProjectIPServiceOp is interface for IP assignment methods.
type ProjectIPServiceOp struct {
client *Client
}
// Get returns reservation by ID.
func (i *ProjectIPServiceOp) Get(reservationID string) (*IPAddressReservation, *Response, error) {
path := fmt.Sprintf("%s/%s", ipBasePath, reservationID)
ipr := new(IPAddressReservation)
resp, err := i.client.DoRequest("GET", path, nil, ipr)
if err != nil {
return nil, resp, err
}
return ipr, resp, err
}
// List provides a list of IP resevations for a single project.
func (i *IPReservationServiceOp) List(projectID string) ([]IPReservation, *Response, error) {
func (i *ProjectIPServiceOp) List(projectID string) ([]IPAddressReservation, *Response, error) {
path := fmt.Sprintf("%s/%s%s", projectBasePath, projectID, ipBasePath)
reservations := new(struct {
Reservations []IPAddressReservation `json:"ip_addresses"`
})
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)
resp, err := i.client.DoRequest("GET", path, nil, reservations)
if err != nil {
return nil, resp, err
}
return reservations.IPReservations, resp, err
return reservations.Reservations, resp, nil
}
// 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) (*IPReservation, *Response, error) {
// Request requests more IP space for a project in order to have additional IP addresses to assign to devices.
func (i *ProjectIPServiceOp) Request(projectID string, ipReservationReq *IPReservationRequest) (*IPAddressReservation, *Response, error) {
path := fmt.Sprintf("%s/%s%s", projectBasePath, projectID, ipBasePath)
ipr := new(IPAddressReservation)
req, err := i.client.NewRequest("POST", path, &ipReservationReq)
if err != nil {
return nil, nil, err
}
ip := new(IPReservation)
resp, err := i.client.Do(req, ip)
resp, err := i.client.DoRequest("POST", path, ipReservationReq, ipr)
if err != nil {
return nil, resp, err
}
return ip, 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
return ipr, 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)
func (i *ProjectIPServiceOp) Remove(ipReservationID string) (*Response, error) {
return deleteFromIP(i.client, ipReservationID)
}
req, err := i.client.NewRequest("DELETE", path, nil)
// AvailableAddresses lists addresses available from a reserved block
func (i *ProjectIPServiceOp) AvailableAddresses(ipReservationID string, r *AvailableRequest) ([]string, *Response, error) {
path := fmt.Sprintf("%s/%s/available", ipBasePath, ipReservationID)
ar := new(AvailableResponse)
resp, err := i.client.DoRequest("GET", path, r, ar)
if err != nil {
return nil, err
return nil, resp, err
}
return ar.Available, resp, nil
resp, err := i.client.Do(req, nil)
if err != nil {
return nil, err
}
return resp, err
}

View File

@ -30,13 +30,9 @@ type OSServiceOp struct {
// List returns all available operating systems
func (s *OSServiceOp) List() ([]OS, *Response, error) {
req, err := s.client.NewRequest("GET", osBasePath, nil)
if err != nil {
return nil, nil, err
}
root := new(osRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.DoRequest("GET", osBasePath, nil, root)
if err != nil {
return nil, resp, err
}

View File

@ -6,8 +6,11 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strconv"
"strings"
"time"
@ -18,6 +21,7 @@ const (
baseURL = "https://api.packet.net/"
userAgent = "packngo/" + libraryVersion
mediaType = "application/json"
debugEnvVar = "PACKNGO_DEBUG"
headerRateLimit = "X-RateLimit-Limit"
headerRateRemaining = "X-RateLimit-Remaining"
@ -42,6 +46,11 @@ type Response struct {
Rate
}
// Href is an API link
type Href struct {
Href string `json:"href"`
}
func (r *Response) populateRate() {
// parse the rate limit headers and populate Response.Rate
if limit := r.Header.Get(headerRateLimit); limit != "" {
@ -61,16 +70,18 @@ func (r *Response) populateRate() {
type ErrorResponse struct {
Response *http.Response
Errors []string `json:"errors"`
SingleError string `json:"error"`
}
func (r *ErrorResponse) Error() string {
return fmt.Sprintf("%v %v: %d %v",
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, strings.Join(r.Errors, ", "))
return fmt.Sprintf("%v %v: %d %v %v",
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, strings.Join(r.Errors, ", "), r.SingleError)
}
// Client is the base API Client
type Client struct {
client *http.Client
debug bool
BaseURL *url.URL
@ -89,9 +100,11 @@ type Client struct {
Projects ProjectService
Facilities FacilityService
OperatingSystems OSService
Ips IPService
IpReservations IPReservationService
DeviceIPs DeviceIPService
ProjectIPs ProjectIPService
Volumes VolumeService
VolumeAttachments VolumeAttachmentService
SpotMarket SpotMarketService
}
// NewRequest inits a new http request with the proper headers
@ -140,6 +153,10 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
response := Response{Response: resp}
response.populateRate()
if c.debug {
o, _ := httputil.DumpResponse(response.Response, true)
log.Printf("%s\n", string(o))
}
c.RateLimit = response.Rate
err = checkResponse(resp)
@ -163,6 +180,19 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
return &response, err
}
// DoRequest is a convenience method, it calls NewRequest follwed by Do
func (c *Client) DoRequest(method, path string, body, v interface{}) (*Response, error) {
req, err := c.NewRequest(method, path, body)
if c.debug {
o, _ := httputil.DumpRequestOut(req, true)
log.Printf("%s\n", string(o))
}
if err != nil {
return nil, err
}
return c.Do(req, v)
}
// NewClient initializes and returns a Client, use this to get an API Client to operate on
// N.B.: Packet's API certificate requires Go 1.5+ to successfully parse. If you are using
// an older version of Go, pass in a custom http.Client with a custom TLS configuration
@ -171,6 +201,9 @@ func NewClient(consumerToken string, apiKey string, httpClient *http.Client) *Cl
client, _ := NewClientWithBaseURL(consumerToken, apiKey, httpClient, baseURL)
return client
}
// NewClientWithBaseURL returns a Client pointing to nonstandard API URL, e.g.
// for mocking the remote API
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
@ -185,6 +218,7 @@ func NewClientWithBaseURL(consumerToken string, apiKey string, httpClient *http.
}
c := &Client{client: httpClient, BaseURL: u, UserAgent: userAgent, ConsumerToken: consumerToken, APIKey: apiKey}
c.debug = os.Getenv(debugEnvVar) != ""
c.Plans = &PlanServiceOp{client: c}
c.Users = &UserServiceOp{client: c}
c.Emails = &EmailServiceOp{client: c}
@ -193,9 +227,11 @@ func NewClientWithBaseURL(consumerToken string, apiKey string, httpClient *http.
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.DeviceIPs = &DeviceIPServiceOp{client: c}
c.ProjectIPs = &ProjectIPServiceOp{client: c}
c.Volumes = &VolumeServiceOp{client: c}
c.VolumeAttachments = &VolumeAttachmentServiceOp{client: c}
c.SpotMarket = &SpotMarketServiceOp{client: c}
return c, nil
}

View File

@ -106,14 +106,9 @@ type PlanServiceOp struct {
// List method returns all available plans
func (s *PlanServiceOp) List() ([]Plan, *Response, error) {
req, err := s.client.NewRequest("GET", planBasePath, nil)
if err != nil {
return nil, nil, err
}
root := new(planRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.DoRequest("GET", planBasePath, nil, root)
if err != nil {
return nil, resp, err
}

View File

@ -11,14 +11,9 @@ 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"`
}
@ -69,31 +64,11 @@ 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)
if err != nil {
return nil, nil, err
}
root := new(projectsRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.DoRequest("GET", projectBasePath, nil, root)
if err != nil {
return nil, resp, err
}
@ -104,13 +79,9 @@ func (s *ProjectServiceOp) List() ([]Project, *Response, error) {
// Get returns a project by id
func (s *ProjectServiceOp) Get(projectID string) (*Project, *Response, error) {
path := fmt.Sprintf("%s/%s", projectBasePath, projectID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
project := new(Project)
resp, err := s.client.Do(req, project)
resp, err := s.client.DoRequest("GET", path, nil, project)
if err != nil {
return nil, resp, err
}
@ -120,13 +91,9 @@ func (s *ProjectServiceOp) Get(projectID string) (*Project, *Response, error) {
// Create creates a new project
func (s *ProjectServiceOp) Create(createRequest *ProjectCreateRequest) (*Project, *Response, error) {
req, err := s.client.NewRequest("POST", projectBasePath, createRequest)
if err != nil {
return nil, nil, err
}
project := new(Project)
resp, err := s.client.Do(req, project)
resp, err := s.client.DoRequest("POST", projectBasePath, createRequest, project)
if err != nil {
return nil, resp, err
}
@ -137,13 +104,9 @@ func (s *ProjectServiceOp) Create(createRequest *ProjectCreateRequest) (*Project
// Update updates a project
func (s *ProjectServiceOp) Update(updateRequest *ProjectUpdateRequest) (*Project, *Response, error) {
path := fmt.Sprintf("%s/%s", projectBasePath, updateRequest.ID)
req, err := s.client.NewRequest("PATCH", path, updateRequest)
if err != nil {
return nil, nil, err
}
project := new(Project)
resp, err := s.client.Do(req, project)
resp, err := s.client.DoRequest("PATCH", path, updateRequest, project)
if err != nil {
return nil, resp, err
}
@ -155,26 +118,15 @@ func (s *ProjectServiceOp) Update(updateRequest *ProjectUpdateRequest) (*Project
func (s *ProjectServiceOp) Delete(projectID string) (*Response, error) {
path := fmt.Sprintf("%s/%s", projectBasePath, projectID)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
return s.client.DoRequest("DELETE", path, nil, nil)
}
resp, err := s.client.Do(req, nil)
return resp, err
}
// List returns Volumes for a project
// ListVolumes 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)
resp, err := s.client.DoRequest("GET", url, nil, root)
if err != nil {
return nil, resp, err
}

View File

@ -0,0 +1,39 @@
package packngo
const spotMarketBasePath = "/market/spot/prices"
// SpotMarketService expooses Spot Market methods
type SpotMarketService interface {
Prices() (PriceMap, *Response, error)
}
// SpotMarketServiceOp implements SpotMarketService
type SpotMarketServiceOp struct {
client *Client
}
// PriceMap is a map of [facility][plan]-> float Price
type PriceMap map[string]map[string]float64
// Prices gets current PriceMap from the API
func (s *SpotMarketServiceOp) Prices() (PriceMap, *Response, error) {
root := new(struct {
SMPs map[string]map[string]struct {
Price float64 `json:"price"`
} `json:"spot_market_prices"`
})
resp, err := s.client.DoRequest("GET", spotMarketBasePath, nil, root)
if err != nil {
return nil, resp, err
}
prices := make(PriceMap)
for facility, planMap := range root.SMPs {
prices[facility] = map[string]float64{}
for plan, v := range planMap {
prices[facility][plan] = v.Price
}
}
return prices, resp, err
}

View File

@ -2,11 +2,14 @@ package packngo
import "fmt"
const sshKeyBasePath = "/ssh-keys"
const (
sshKeyBasePath = "/ssh-keys"
)
// SSHKeyService interface defines available device methods
type SSHKeyService interface {
List() ([]SSHKey, *Response, error)
ProjectList(string) ([]SSHKey, *Response, error)
Get(string) (*SSHKey, *Response, error)
Create(*SSHKeyCreateRequest) (*SSHKey, *Response, error)
Update(*SSHKeyUpdateRequest) (*SSHKey, *Response, error)
@ -60,15 +63,10 @@ type SSHKeyServiceOp struct {
client *Client
}
// List returns a user's ssh keys
func (s *SSHKeyServiceOp) List() ([]SSHKey, *Response, error) {
req, err := s.client.NewRequest("GET", sshKeyBasePath, nil)
if err != nil {
return nil, nil, err
}
func (s *SSHKeyServiceOp) list(url string) ([]SSHKey, *Response, error) {
root := new(sshKeyRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.DoRequest("GET", url, nil, root)
if err != nil {
return nil, resp, err
}
@ -76,17 +74,23 @@ func (s *SSHKeyServiceOp) List() ([]SSHKey, *Response, error) {
return root.SSHKeys, resp, err
}
// ProjectList lists ssh keys of a project
func (s *SSHKeyServiceOp) ProjectList(projectID string) ([]SSHKey, *Response, error) {
return s.list(fmt.Sprintf("%s/%s%s", projectBasePath, projectID, sshKeyBasePath))
}
// List returns a user's ssh keys
func (s *SSHKeyServiceOp) List() ([]SSHKey, *Response, error) {
return s.list(sshKeyBasePath)
}
// Get returns an ssh key by id
func (s *SSHKeyServiceOp) Get(sshKeyID string) (*SSHKey, *Response, error) {
path := fmt.Sprintf("%s/%s", sshKeyBasePath, sshKeyID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
sshKey := new(SSHKey)
resp, err := s.client.Do(req, sshKey)
resp, err := s.client.DoRequest("GET", path, nil, sshKey)
if err != nil {
return nil, resp, err
}
@ -98,15 +102,11 @@ func (s *SSHKeyServiceOp) Get(sshKeyID string) (*SSHKey, *Response, error) {
func (s *SSHKeyServiceOp) Create(createRequest *SSHKeyCreateRequest) (*SSHKey, *Response, error) {
path := sshKeyBasePath
if createRequest.ProjectID != "" {
path = "/projects/" + createRequest.ProjectID + sshKeyBasePath
path = fmt.Sprintf("%s/%s%s", projectBasePath, createRequest.ProjectID, sshKeyBasePath)
}
req, err := s.client.NewRequest("POST", path, createRequest)
if err != nil {
return nil, nil, err
}
sshKey := new(SSHKey)
resp, err := s.client.Do(req, sshKey)
resp, err := s.client.DoRequest("POST", path, createRequest, sshKey)
if err != nil {
return nil, resp, err
}
@ -117,13 +117,9 @@ func (s *SSHKeyServiceOp) Create(createRequest *SSHKeyCreateRequest) (*SSHKey, *
// Update updates an ssh key
func (s *SSHKeyServiceOp) Update(updateRequest *SSHKeyUpdateRequest) (*SSHKey, *Response, error) {
path := fmt.Sprintf("%s/%s", sshKeyBasePath, updateRequest.ID)
req, err := s.client.NewRequest("PATCH", path, updateRequest)
if err != nil {
return nil, nil, err
}
sshKey := new(SSHKey)
resp, err := s.client.Do(req, sshKey)
resp, err := s.client.DoRequest("PATCH", path, updateRequest, sshKey)
if err != nil {
return nil, resp, err
}
@ -135,12 +131,5 @@ func (s *SSHKeyServiceOp) Update(updateRequest *SSHKeyUpdateRequest) (*SSHKey, *
func (s *SSHKeyServiceOp) Delete(sshKeyID string) (*Response, error) {
path := fmt.Sprintf("%s/%s", sshKeyBasePath, sshKeyID)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
return s.client.DoRequest("DELETE", path, nil, nil)
}

View File

@ -22,7 +22,7 @@ type User struct {
Created string `json:"created_at,omitempty"`
Updated string `json:"updated_at,omitempty"`
TimeZone string `json:"timezone,omitempty"`
Emails []Email `json:"email,omitempty"`
Emails []Email `json:"emails,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"`
URL string `json:"href,omitempty"`
}
@ -38,13 +38,9 @@ type UserServiceOp struct {
// Get method gets a user by userID
func (s *UserServiceOp) Get(userID string) (*User, *Response, error) {
req, err := s.client.NewRequest("GET", userBasePath, nil)
if err != nil {
return nil, nil, err
}
user := new(User)
resp, err := s.client.Do(req, user)
resp, err := s.client.DoRequest("GET", userBasePath, nil, user)
if err != nil {
return nil, resp, err
}

View File

@ -17,27 +17,6 @@ func Stringify(message interface{}) string {
return buf.String()
}
// String allocates a new string value to store v and returns a pointer to it
func String(v string) *string {
p := new(string)
*p = v
return p
}
// Int allocates a new int32 value to store v and returns a pointer to it, but unlike Int32 its argument value is an int.
func Int(v int) *int {
p := new(int)
*p = v
return p
}
// Bool allocates a new bool value to store v and returns a pointer to it.
func Bool(v bool) *bool {
p := new(bool)
*p = v
return p
}
// StreamToString converts a reader to a string
func StreamToString(stream io.Reader) string {
buf := new(bytes.Buffer)

View File

@ -2,14 +2,24 @@ package packngo
import "fmt"
const volumeBasePath = "/storage"
const (
volumeBasePath = "/storage"
attachmentsBasePath = "/attachments"
)
// 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)
Create(*VolumeCreateRequest, string) (*Volume, *Response, error)
}
// VolumeAttachmentService defines attachment methdods
type VolumeAttachmentService interface {
Get(string) (*VolumeAttachment, *Response, error)
Create(string, string) (*VolumeAttachment, *Response, error)
Delete(string) (*Response, error)
}
// Volume represents a volume
@ -25,7 +35,7 @@ type Volume struct {
Updated string `json:"updated_at,omitempty"`
Href string `json:"href,omitempty"`
SnapshotPolicies []*SnapshotPolicy `json:"snapshot_policies,omitempty"`
Attachments []*Attachment `json:"attachments,omitempty"`
Attachments []*VolumeAttachment `json:"attachments,omitempty"`
Plan *Plan `json:"plan,omitempty"`
Facility *Facility `json:"facility,omitempty"`
Project *Project `json:"project,omitempty"`
@ -39,12 +49,6 @@ type SnapshotPolicy struct {
SnapshotCount 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)
}
@ -71,10 +75,23 @@ type VolumeUpdateRequest struct {
Plan string `json:"plan,omitempty"`
}
// VolumeAttachment is a type from Packet API
type VolumeAttachment struct {
Href string `json:"href"`
ID string `json:"id"`
Volume Volume `json:"volume"`
Device Device `json:"device"`
}
func (v VolumeUpdateRequest) String() string {
return Stringify(v)
}
// VolumeAttachmentServiceOp implements VolumeService
type VolumeAttachmentServiceOp struct {
client *Client
}
// VolumeServiceOp implements VolumeService
type VolumeServiceOp struct {
client *Client
@ -83,13 +100,9 @@ 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,snapshot_policies,attachments.device", 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)
resp, err := v.client.DoRequest("GET", path, nil, volume)
if err != nil {
return nil, resp, err
}
@ -100,13 +113,9 @@ func (v *VolumeServiceOp) Get(volumeID string) (*Volume, *Response, error) {
// 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)
resp, err := v.client.DoRequest("PATCH", path, updateRequest, volume)
if err != nil {
return nil, resp, err
}
@ -118,29 +127,55 @@ func (v *VolumeServiceOp) Update(updateRequest *VolumeUpdateRequest) (*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
return v.client.DoRequest("DELETE", path, nil, nil)
}
// 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
}
func (v *VolumeServiceOp) Create(createRequest *VolumeCreateRequest, projectID string) (*Volume, *Response, error) {
url := fmt.Sprintf("%s/%s%s", projectBasePath, projectID, volumeBasePath)
volume := new(Volume)
resp, err := v.client.Do(req, volume)
resp, err := v.client.DoRequest("POST", url, createRequest, volume)
if err != nil {
return nil, resp, err
}
return volume, resp, err
}
// Attachments
// Create Attachment, i.e. attach volume to a device
func (v *VolumeAttachmentServiceOp) Create(volumeID, deviceID string) (*VolumeAttachment, *Response, error) {
url := fmt.Sprintf("%s/%s%s", volumeBasePath, volumeID, attachmentsBasePath)
volAttachParam := map[string]string{
"device_id": deviceID,
}
volumeAttachment := new(VolumeAttachment)
resp, err := v.client.DoRequest("POST", url, volAttachParam, volumeAttachment)
if err != nil {
return nil, resp, err
}
return volumeAttachment, resp, nil
}
// Get gets attachment by id
func (v *VolumeAttachmentServiceOp) Get(attachmentID string) (*VolumeAttachment, *Response, error) {
path := fmt.Sprintf("%s%s/%s", volumeBasePath, attachmentsBasePath, attachmentID)
volumeAttachment := new(VolumeAttachment)
resp, err := v.client.DoRequest("GET", path, nil, volumeAttachment)
if err != nil {
return nil, resp, err
}
return volumeAttachment, resp, nil
}
// Delete deletes attachment by id
func (v *VolumeAttachmentServiceOp) Delete(attachmentID string) (*Response, error) {
path := fmt.Sprintf("%s%s/%s", volumeBasePath, attachmentsBasePath, attachmentID)
return v.client.DoRequest("DELETE", path, nil, nil)
}