435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
258 lines
9.2 KiB
Go
258 lines
9.2 KiB
Go
package packngo
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const deviceBasePath = "/devices"
|
|
|
|
// DeviceService interface defines available device methods
|
|
type DeviceService interface {
|
|
List(ProjectID string, listOpt *ListOptions) ([]Device, *Response, error)
|
|
Get(string) (*Device, *Response, error)
|
|
GetExtra(deviceID string, includes, excludes []string) (*Device, *Response, error)
|
|
Create(*DeviceCreateRequest) (*Device, *Response, error)
|
|
Update(string, *DeviceUpdateRequest) (*Device, *Response, error)
|
|
Delete(string) (*Response, error)
|
|
Reboot(string) (*Response, error)
|
|
PowerOff(string) (*Response, error)
|
|
PowerOn(string) (*Response, error)
|
|
Lock(string) (*Response, error)
|
|
Unlock(string) (*Response, error)
|
|
}
|
|
|
|
type devicesRoot struct {
|
|
Devices []Device `json:"devices"`
|
|
Meta meta `json:"meta"`
|
|
}
|
|
|
|
// Device represents a Packet device
|
|
type Device struct {
|
|
ID string `json:"id"`
|
|
Href string `json:"href,omitempty"`
|
|
Hostname string `json:"hostname,omitempty"`
|
|
State string `json:"state,omitempty"`
|
|
Created string `json:"created_at,omitempty"`
|
|
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 []*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"`
|
|
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"`
|
|
NetworkPorts []Port `json:"network_ports,omitempty"`
|
|
CustomData map[string]interface{} `json:"customdata,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 {
|
|
return Stringify(d)
|
|
}
|
|
|
|
// DeviceCreateRequest type used to create a Packet device
|
|
type DeviceCreateRequest struct {
|
|
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"`
|
|
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"`
|
|
CustomData string `json:"customdata,omitempty"`
|
|
}
|
|
|
|
// DeviceUpdateRequest type used to update a Packet device
|
|
type DeviceUpdateRequest struct {
|
|
Hostname *string `json:"hostname,omitempty"`
|
|
Description *string `json:"description,omitempty"`
|
|
UserData *string `json:"userdata,omitempty"`
|
|
Locked *bool `json:"locked,omitempty"`
|
|
Tags *[]string `json:"tags,omitempty"`
|
|
AlwaysPXE *bool `json:"always_pxe,omitempty"`
|
|
IPXEScriptURL *string `json:"ipxe_script_url,omitempty"`
|
|
CustomData *string `json:"customdata,omitempty"`
|
|
}
|
|
|
|
func (d DeviceCreateRequest) String() string {
|
|
return Stringify(d)
|
|
}
|
|
|
|
// DeviceActionRequest type used to execute actions on devices
|
|
type DeviceActionRequest struct {
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
func (d DeviceActionRequest) String() string {
|
|
return Stringify(d)
|
|
}
|
|
|
|
// DeviceServiceOp implements DeviceService
|
|
type DeviceServiceOp struct {
|
|
client *Client
|
|
}
|
|
|
|
// List returns devices on a project
|
|
func (s *DeviceServiceOp) List(projectID string, listOpt *ListOptions) (devices []Device, resp *Response, err error) {
|
|
params := "include=facility"
|
|
if listOpt != nil {
|
|
params = listOpt.createURL()
|
|
}
|
|
path := fmt.Sprintf("%s/%s%s?%s", projectBasePath, projectID, deviceBasePath, params)
|
|
|
|
for {
|
|
subset := new(devicesRoot)
|
|
|
|
resp, err = s.client.DoRequest("GET", path, nil, subset)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
|
|
devices = append(devices, subset.Devices...)
|
|
|
|
if subset.Meta.Next != nil && (listOpt == nil || listOpt.Page == 0) {
|
|
path = subset.Meta.Next.Href
|
|
if params != "" {
|
|
path = fmt.Sprintf("%s&%s", path, params)
|
|
}
|
|
continue
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
// Get returns a device by id
|
|
func (s *DeviceServiceOp) Get(deviceID string) (*Device, *Response, error) {
|
|
return s.GetExtra(deviceID, []string{"facility"}, nil)
|
|
}
|
|
|
|
// GetExtra returns a device by id. Specifying either includes/excludes provides more or less desired
|
|
// detailed information about resources which would otherwise be represented with an href link
|
|
func (s *DeviceServiceOp) GetExtra(deviceID string, includes, excludes []string) (*Device, *Response, error) {
|
|
path := fmt.Sprintf("%s/%s", deviceBasePath, deviceID)
|
|
if includes != nil {
|
|
path += fmt.Sprintf("?include=%s", strings.Join(includes, ","))
|
|
} else if excludes != nil {
|
|
path += fmt.Sprintf("?exclude=%s", strings.Join(excludes, ","))
|
|
}
|
|
device := new(Device)
|
|
|
|
resp, err := s.client.DoRequest("GET", path, nil, device)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
|
|
return device, resp, err
|
|
}
|
|
|
|
// Create creates a new device
|
|
func (s *DeviceServiceOp) Create(createRequest *DeviceCreateRequest) (*Device, *Response, error) {
|
|
path := fmt.Sprintf("%s/%s%s", projectBasePath, createRequest.ProjectID, deviceBasePath)
|
|
device := new(Device)
|
|
|
|
resp, err := s.client.DoRequest("POST", path, createRequest, device)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
|
|
return device, resp, err
|
|
}
|
|
|
|
// 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)
|
|
device := new(Device)
|
|
|
|
resp, err := s.client.DoRequest("PUT", path, updateRequest, device)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
|
|
return device, resp, err
|
|
}
|
|
|
|
// Delete deletes a device
|
|
func (s *DeviceServiceOp) Delete(deviceID string) (*Response, error) {
|
|
path := fmt.Sprintf("%s/%s", deviceBasePath, deviceID)
|
|
|
|
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"}
|
|
|
|
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"}
|
|
|
|
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"}
|
|
|
|
return s.client.DoRequest("POST", path, action, nil)
|
|
}
|
|
|
|
type lockType struct {
|
|
Locked bool `json:"locked"`
|
|
}
|
|
|
|
// Lock sets a device to "locked"
|
|
func (s *DeviceServiceOp) Lock(deviceID string) (*Response, error) {
|
|
path := fmt.Sprintf("%s/%s", deviceBasePath, deviceID)
|
|
action := lockType{Locked: true}
|
|
|
|
return s.client.DoRequest("PATCH", path, action, nil)
|
|
}
|
|
|
|
// Unlock sets a device to "unlocked"
|
|
func (s *DeviceServiceOp) Unlock(deviceID string) (*Response, error) {
|
|
path := fmt.Sprintf("%s/%s", deviceBasePath, deviceID)
|
|
action := lockType{Locked: false}
|
|
|
|
return s.client.DoRequest("PATCH", path, action, nil)
|
|
}
|