Merge pull request #1599 from avsm/packet-net-run

Add `moby run packet` to boot on baremetal Packet.net hosts
This commit is contained in:
Justin Cormack 2017-04-12 13:40:50 +01:00 committed by GitHub
commit f603d37638
20 changed files with 1828 additions and 0 deletions

50
examples/packet.yml Normal file
View File

@ -0,0 +1,50 @@
kernel:
image: "mobylinux/kernel:4.9.x"
cmdline: "console=ttyS1 page_poison=1"
init:
- mobylinux/init:e10e2efc1b78ef41d196175cbc07e069391f406e
- mobylinux/runc:b0fb122e10dbb7e4e45115177a61a3f8d68c19a9
- mobylinux/containerd:18eaf72f3f4f9a9f29ca1951f66df701f873060b
- mobylinux/ca-certificates:eabc5a6e59f05aa91529d80e9a595b85b046f935
onboot:
- name: sysctl
image: "mobylinux/sysctl:2cf2f9d5b4d314ba1bfc22b2fe931924af666d8c"
net: host
pid: host
ipc: host
capabilities:
- CAP_SYS_ADMIN
services:
- name: rngd
image: "mobylinux/rngd:3dad6dd43270fa632ac031e99d1947f20b22eec9@sha256:1c93c1db7196f6f71f8e300bc1d15f0376dd18e8891c8789d77c8ff19f3a9a92"
capabilities:
- CAP_SYS_ADMIN
oomScoreAdj: -800
- name: dhcpcd
image: "mobylinux/dhcpcd:0d4012269cb142972fed8542fbdc3ff5a7b695cd"
binds:
- /var:/var
- /tmp:/etc
capabilities:
- CAP_NET_ADMIN
- CAP_NET_BIND_SERVICE
- CAP_NET_RAW
net: host
oomScoreAdj: -800
- name: sshd
image: "mobylinux/sshd:160631d59fffc13d523ff7f09b3b49538d34b9cd"
capabilities:
- all
net: host
pid: host
binds:
- /root/.ssh:/root/.ssh
- /etc/resolv.conf:/etc/resolv.conf
trust:
image:
- mobylinux/kernel
files:
- path: root/.ssh/authorized_keys
contents: '#your ssh key here'
outputs:
- format: kernel+initrd

View File

@ -18,6 +18,7 @@ func runUsage() {
fmt.Printf(" hyperkit [macOS]\n") fmt.Printf(" hyperkit [macOS]\n")
fmt.Printf(" qemu [linux]\n") fmt.Printf(" qemu [linux]\n")
fmt.Printf(" vmware\n") fmt.Printf(" vmware\n")
fmt.Printf(" packet\n")
fmt.Printf("\n") fmt.Printf("\n")
fmt.Printf("'options' are the backend specific options.\n") fmt.Printf("'options' are the backend specific options.\n")
fmt.Printf("See 'moby run [backend] --help' for details.\n\n") fmt.Printf("See 'moby run [backend] --help' for details.\n\n")
@ -43,6 +44,8 @@ func run(args []string) {
runGcp(args[1:]) runGcp(args[1:])
case "qemu": case "qemu":
runQemu(args[1:]) runQemu(args[1:])
case "packet":
runPacket(args[1:])
default: default:
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin":

113
src/cmd/moby/run_packet.go Normal file
View File

@ -0,0 +1,113 @@
package main
import (
"encoding/json"
"flag"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/packethost/packngo"
"net/http"
"os"
)
const (
packetDefaultZone = "ams1"
packetDefaultMachine = "baremetal_0"
packetDefaultHostname = "moby"
packetBaseURL = "PACKET_BASE_URL"
packetZoneVar = "PACKET_ZONE"
packetMachineVar = "PACKET_MACHINE"
packetAPIKeyVar = "PACKET_API_KEY"
packetProjectIDVar = "PACKET_PROJECT_ID"
packetHostnameVar = "PACKET_HOSTNAME"
packetNameVar = "PACKET_NAME"
)
// ValidateHTTPURL does a sanity check that a URL returns a 200 or 300 response
func ValidateHTTPURL(url string) {
log.Printf("Validating URL: %s", url)
resp, err := http.Head(url)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode >= 400 {
log.Fatal("Got a non 200- or 300- HTTP response code: %s", resp)
}
log.Printf("OK: %d response code", resp.StatusCode)
}
// Process the run arguments and execute run
func runPacket(args []string) {
packetCmd := flag.NewFlagSet("packet", flag.ExitOnError)
packetCmd.Usage = func() {
fmt.Printf("USAGE: %s run packet [options] [name]\n\n", os.Args[0])
fmt.Printf("Options:\n\n")
packetCmd.PrintDefaults()
}
baseURLFlag := packetCmd.String("base-url", "", "Base URL that the kernel and initrd are served from.")
zoneFlag := packetCmd.String("zone", packetDefaultZone, "Packet Zone")
machineFlag := packetCmd.String("machine", packetDefaultMachine, "Packet Machine Type")
apiKeyFlag := packetCmd.String("api-key", "", "Packet API key")
projectFlag := packetCmd.String("project-id", "", "Packet Project ID")
hostNameFlag := packetCmd.String("hostname", packetDefaultHostname, "Hostname of new instance")
nameFlag := packetCmd.String("img-name", "", "Overrides the prefix used to identify the files. Defaults to [name]")
if err := packetCmd.Parse(args); err != nil {
log.Fatal("Unable to parse args")
}
remArgs := packetCmd.Args()
prefix := "packet"
if len(remArgs) > 0 {
prefix = remArgs[0]
}
url := getStringValue(packetBaseURL, *baseURLFlag, "")
if url == "" {
log.Fatal("Need to specify a value for --base-url where the images are hosted. This URL should contain <url>/%s-bzImage and <url>/%s-initrd.img")
}
facility := getStringValue(packetZoneVar, *zoneFlag, "")
plan := getStringValue(packetMachineVar, *machineFlag, defaultMachine)
apiKey := getStringValue(packetAPIKeyVar, *apiKeyFlag, "")
if apiKey == "" {
log.Fatal("Must specify a Packet.net API key with --api-key")
}
projectID := getStringValue(packetProjectIDVar, *projectFlag, "")
if projectID == "" {
log.Fatal("Must specify a Packet.net Project ID with --project-id")
}
hostname := getStringValue(packetHostnameVar, *hostNameFlag, "")
name := getStringValue(packetNameVar, *nameFlag, prefix)
osType := "custom_ipxe"
billing := "hourly"
userData := fmt.Sprintf("#!ipxe\n\ndhcp\nset base-url %s\nset kernel-params ip=dhcp nomodeset ro serial console=ttyS1,115200\nkernel ${base-url}/%s-bzImage ${kernel-params}\ninitrd ${base-url}/%s-initrd.img\nboot", url, name, name)
log.Debugf("Using userData of:\n%s\n", userData)
initrdURL := fmt.Sprintf("%s/%s-initrd.img", url, name)
kernelURL := fmt.Sprintf("%s/%s-bzImage", url, name)
ValidateHTTPURL(kernelURL)
ValidateHTTPURL(initrdURL)
client := packngo.NewClient("", apiKey, nil)
tags := []string{}
req := packngo.DeviceCreateRequest{
HostName: hostname,
Plan: plan,
Facility: facility,
OS: osType,
BillingCycle: billing,
ProjectID: projectID,
UserData: userData,
Tags: tags,
}
d, _, err := client.Devices.Create(&req)
if err != nil {
log.Fatal(err)
}
b, err := json.MarshalIndent(d, "", " ")
if err != nil {
log.Fatal(err)
}
// log response json if in verbose mode
log.Debugf("%s\n", string(b))
// TODO: poll events api for bringup (requires extpacknogo)
// TODO: connect to serial console (requires API extension to get SSH URI)
// TODO: add ssh keys via API registered keys
}

View File

@ -23,6 +23,7 @@ github.com/mattn/go-colorable d228849
github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062
github.com/opencontainers/runtime-spec d094a5c9c1997ab086197b57e9378fabed394d92 github.com/opencontainers/runtime-spec d094a5c9c1997ab086197b57e9378fabed394d92
github.com/pkg/errors ff09b135c25aae272398c51a07235b90a75aa4f0 github.com/pkg/errors ff09b135c25aae272398c51a07235b90a75aa4f0
github.com/packethost/packngo 91d54000aa56874149d348a884ba083c41d38091
github.com/rneugeba/iso9660wrap 4606f848a055435cdef85305960b0e1bb788d506 github.com/rneugeba/iso9660wrap 4606f848a055435cdef85305960b0e1bb788d506
github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a
github.com/spf13/afero 9be650865eab0c12963d8753212f4f9c66cdcf12 github.com/spf13/afero 9be650865eab0c12963d8753212f4f9c66cdcf12

56
vendor/github.com/packethost/packngo/LICENSE.txt generated vendored Normal file
View File

@ -0,0 +1,56 @@
Copyright (c) 2014 The packngo AUTHORS. All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
======================
Portions of the client are based on code at:
https://github.com/google/go-github/ and
https://github.com/digitalocean/godo
Copyright (c) 2013 The go-github AUTHORS. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

9
vendor/github.com/packethost/packngo/README.md generated vendored Normal file
View File

@ -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/))

223
vendor/github.com/packethost/packngo/devices.go generated vendored Normal file
View File

@ -0,0 +1,223 @@
package packngo
import "fmt"
const deviceBasePath = "/devices"
// DeviceService interface defines available device methods
type DeviceService interface {
List(ProjectID string) ([]Device, *Response, error)
Get(string) (*Device, *Response, error)
Create(*DeviceCreateRequest) (*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"`
}
// 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"`
Tags []string `json:"tags,omitempty"`
Network []*IPAddress `json:"ip_addresses"`
OS *OS `json:"operating_system,omitempty"`
Plan *Plan `json:"plan,omitempty"`
Facility *Facility `json:"facility,omitempty"`
Project *Project `json:"project,omitempty"`
ProvisionPer float32 `json:"provisioning_percentage,omitempty"`
}
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"`
Tags []string `json:"tags"`
}
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) ([]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)
if err != nil {
return nil, resp, err
}
return root.Devices, resp, err
}
// 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)
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/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)
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)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
return resp, err
}
// 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
}
// 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
}
// 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
}
type lockDeviceType 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 := 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
}
// 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
}

41
vendor/github.com/packethost/packngo/email.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
package packngo
const emailBasePath = "/emails"
// EmailService interface defines available email methods
type EmailService interface {
Get(string) (*Email, *Response, error)
}
// 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"`
}
func (e Email) String() string {
return Stringify(e)
}
// EmailServiceOp implements EmailService
type EmailServiceOp struct {
client *Client
}
// 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)
if err != nil {
return nil, resp, err
}
return email, resp, err
}

56
vendor/github.com/packethost/packngo/facilities.go generated vendored Normal file
View File

@ -0,0 +1,56 @@
package packngo
const facilityBasePath = "/facilities"
// FacilityService interface defines available facility methods
type FacilityService interface {
List() ([]Facility, *Response, error)
}
type facilityRoot struct {
Facilities []Facility `json:"facilities"`
}
// 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"`
}
func (f Facility) String() string {
return Stringify(f)
}
// Address - the physical address of the facility
type Address struct {
ID string `json:"id,omitempty"`
}
func (a Address) String() string {
return Stringify(a)
}
// FacilityServiceOp implements FacilityService
type FacilityServiceOp struct {
client *Client
}
// 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)
if err != nil {
return nil, resp, err
}
return root.Facilities, resp, err
}

204
vendor/github.com/packethost/packngo/ip.go generated vendored Normal file
View File

@ -0,0 +1,204 @@
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"`
Created string `json:"created_at,omitempty"`
Updated string `json:"updated_at,omitempty"`
Href string `json:"href"`
Facility Facility `json:"facility,omitempty"`
}
// 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 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
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"`
Created string `json:"created_at,omitempty"`
Updated string `json:"updated_at,omitempty"`
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) (*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, nil, err
}
ip := new(IPReservation)
resp, err := i.client.Do(req, ip)
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
}
// 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
}

View File

@ -0,0 +1,45 @@
package packngo
const osBasePath = "/operating-systems"
// OSService interface defines available operating_systems methods
type OSService interface {
List() ([]OS, *Response, error)
}
type osRoot struct {
OperatingSystems []OS `json:"operating_systems"`
}
// OS represents a Packet operating system
type OS struct {
Name string `json:"name"`
Slug string `json:"slug"`
Distro string `json:"distro"`
Version string `json:"version"`
}
func (o OS) String() string {
return Stringify(o)
}
// OSServiceOp implements OSService
type OSServiceOp struct {
client *Client
}
// 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)
if err != nil {
return nil, resp, err
}
return root.OperatingSystems, resp, err
}

218
vendor/github.com/packethost/packngo/packngo.go generated vendored Normal file
View File

@ -0,0 +1,218 @@
package packngo
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
const (
libraryVersion = "0.1.0"
baseURL = "https://api.packet.net/"
userAgent = "packngo/" + libraryVersion
mediaType = "application/json"
headerRateLimit = "X-RateLimit-Limit"
headerRateRemaining = "X-RateLimit-Remaining"
headerRateReset = "X-RateLimit-Reset"
)
// ListOptions specifies optional global API parameters
type ListOptions struct {
// for paginated result sets, page of results to retrieve
Page int `url:"page,omitempty"`
// for paginated result sets, the number of results to return per page
PerPage int `url:"per_page,omitempty"`
// specify which resources you want to return as collections instead of references
Includes string
}
// Response is the http response from api calls
type Response struct {
*http.Response
Rate
}
func (r *Response) populateRate() {
// parse the rate limit headers and populate Response.Rate
if limit := r.Header.Get(headerRateLimit); limit != "" {
r.Rate.RequestLimit, _ = strconv.Atoi(limit)
}
if remaining := r.Header.Get(headerRateRemaining); remaining != "" {
r.Rate.RequestsRemaining, _ = strconv.Atoi(remaining)
}
if reset := r.Header.Get(headerRateReset); reset != "" {
if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 {
r.Rate.Reset = Timestamp{time.Unix(v, 0)}
}
}
}
// ErrorResponse is the http response used on errrors
type ErrorResponse struct {
Response *http.Response
Errors []string `json:"errors"`
}
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, ", "))
}
// Client is the base API Client
type Client struct {
client *http.Client
BaseURL *url.URL
UserAgent string
ConsumerToken string
APIKey string
RateLimit Rate
// Packet Api Objects
Plans PlanService
Users UserService
Emails EmailService
SSHKeys SSHKeyService
Devices DeviceService
Projects ProjectService
Facilities FacilityService
OperatingSystems OSService
Ips IPService
IpReservations IPReservationService
Volumes VolumeService
}
// NewRequest inits a new http request with the proper headers
func (c *Client) NewRequest(method, path string, body interface{}) (*http.Request, error) {
// relative path to append to the endpoint url, no leading slash please
rel, err := url.Parse(path)
if err != nil {
return nil, err
}
u := c.BaseURL.ResolveReference(rel)
// json encode the request body, if any
buf := new(bytes.Buffer)
if body != nil {
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
req.Close = true
req.Header.Add("X-Auth-Token", c.APIKey)
req.Header.Add("X-Consumer-Token", c.ConsumerToken)
req.Header.Add("Content-Type", mediaType)
req.Header.Add("Accept", mediaType)
req.Header.Add("User-Agent", userAgent)
return req, nil
}
// Do executes the http request
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
response := Response{Response: resp}
response.populateRate()
c.RateLimit = response.Rate
err = checkResponse(resp)
// if the response is an error, return the ErrorResponse
if err != nil {
return &response, err
}
if v != nil {
// if v implements the io.Writer interface, return the raw response
if w, ok := v.(io.Writer); ok {
io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(v)
if err != nil {
return &response, err
}
}
}
return &response, err
}
// 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
// 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
// pass it in explicitly.
httpClient = &http.Client{}
}
u, err := url.Parse(apiBaseURL)
if err != nil {
return nil, err
}
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}
c.SSHKeys = &SSHKeyServiceOp{client: c}
c.Devices = &DeviceServiceOp{client: c}
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, nil
}
func checkResponse(r *http.Response) error {
// return if http status code is within 200 range
if c := r.StatusCode; c >= 200 && c <= 299 {
// response is good, return
return nil
}
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
// if the response has a body, populate the message in errorResponse
if err == nil && len(data) > 0 {
json.Unmarshal(data, errorResponse)
}
return errorResponse
}

122
vendor/github.com/packethost/packngo/plans.go generated vendored Normal file
View File

@ -0,0 +1,122 @@
package packngo
const planBasePath = "/plans"
// PlanService interface defines available plan methods
type PlanService interface {
List() ([]Plan, *Response, error)
}
type planRoot struct {
Plans []Plan `json:"plans"`
}
// Plan represents a Packet service plan
type Plan struct {
ID string `json:"id"`
Slug string `json:"slug,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Line string `json:"line,omitempty"`
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"`
}
func (s Specs) String() string {
return Stringify(s)
}
// Cpus - the CPU config details for specs on a plan
type Cpus struct {
Count int `json:"count,omitempty"`
Type string `json:"type,omitempty"`
}
func (c Cpus) String() string {
return Stringify(c)
}
// Memory - the RAM config details for specs on a plan
type Memory struct {
Total string `json:"total,omitempty"`
}
func (m Memory) String() string {
return Stringify(m)
}
// Drives - the storage config details for specs on a plan
type Drives struct {
Count int `json:"count,omitempty"`
Size string `json:"size,omitempty"`
Type string `json:"type,omitempty"`
}
func (d Drives) String() string {
return Stringify(d)
}
// Nics - the network hardware details for specs on a plan
type Nics struct {
Count int `json:"count,omitempty"`
Type string `json:"type,omitempty"`
}
func (n Nics) String() string {
return Stringify(n)
}
// Features - other features in the specs for a plan
type Features struct {
Raid bool `json:"raid,omitempty"`
Txt bool `json:"txt,omitempty"`
}
func (f Features) String() string {
return Stringify(f)
}
// Pricing - the pricing options on a plan
type Pricing struct {
Hourly float32 `json:"hourly,omitempty"`
Monthly float32 `json:"monthly,omitempty"`
}
func (p Pricing) String() string {
return Stringify(p)
}
// PlanServiceOp implements PlanService
type PlanServiceOp struct {
client *Client
}
// 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)
if err != nil {
return nil, resp, err
}
return root.Plans, resp, err
}

183
vendor/github.com/packethost/packngo/projects.go generated vendored Normal file
View File

@ -0,0 +1,183 @@
package packngo
import "fmt"
const projectBasePath = "/projects"
// ProjectService interface defines available project methods
type ProjectService interface {
List() ([]Project, *Response, error)
Get(string) (*Project, *Response, error)
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"`
Name string `json:"name,omitempty"`
Created string `json:"created_at,omitempty"`
Updated string `json:"updated_at,omitempty"`
Users []User `json:"members,omitempty"`
Devices []Device `json:"devices,omitempty"`
SSHKeys []SSHKey `json:"ssh_keys,omitempty"`
URL string `json:"href,omitempty"`
}
func (p Project) String() string {
return Stringify(p)
}
// ProjectCreateRequest type used to create a Packet project
type ProjectCreateRequest struct {
Name string `json:"name"`
PaymentMethod string `json:"payment_method,omitempty"`
}
func (p ProjectCreateRequest) String() string {
return Stringify(p)
}
// ProjectUpdateRequest type used to update a Packet project
type ProjectUpdateRequest struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
PaymentMethod string `json:"payment_method,omitempty"`
}
func (p ProjectUpdateRequest) String() string {
return Stringify(p)
}
// ProjectServiceOp implements ProjectService
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)
if err != nil {
return nil, resp, err
}
return root.Projects, resp, err
}
// 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)
if err != nil {
return nil, resp, err
}
return project, resp, err
}
// 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)
if err != nil {
return nil, resp, err
}
return project, resp, err
}
// 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)
if err != nil {
return nil, resp, err
}
return project, resp, err
}
// Delete deletes a 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
}
resp, err := s.client.Do(req, nil)
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
}

12
vendor/github.com/packethost/packngo/rate.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
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"`
}
func (r Rate) String() string {
return Stringify(r)
}

146
vendor/github.com/packethost/packngo/sshkeys.go generated vendored Normal file
View File

@ -0,0 +1,146 @@
package packngo
import "fmt"
const sshKeyBasePath = "/ssh-keys"
// SSHKeyService interface defines available device methods
type SSHKeyService interface {
List() ([]SSHKey, *Response, error)
Get(string) (*SSHKey, *Response, error)
Create(*SSHKeyCreateRequest) (*SSHKey, *Response, error)
Update(*SSHKeyUpdateRequest) (*SSHKey, *Response, error)
Delete(string) (*Response, error)
}
type sshKeyRoot struct {
SSHKeys []SSHKey `json:"ssh_keys"`
}
// 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"`
}
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"`
ProjectID string `json:"-"`
}
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"`
}
func (s SSHKeyUpdateRequest) String() string {
return Stringify(s)
}
// SSHKeyServiceOp implements SSHKeyService
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
}
root := new(sshKeyRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root.SSHKeys, resp, err
}
// 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)
if err != nil {
return nil, resp, err
}
return sshKey, resp, err
}
// Create creates a new ssh key
func (s *SSHKeyServiceOp) Create(createRequest *SSHKeyCreateRequest) (*SSHKey, *Response, error) {
path := sshKeyBasePath
if createRequest.ProjectID != "" {
path = "/projects/" + 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)
if err != nil {
return nil, resp, err
}
return sshKey, resp, err
}
// 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)
if err != nil {
return nil, resp, err
}
return sshKey, resp, err
}
// Delete deletes an ssh key
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
}

35
vendor/github.com/packethost/packngo/timestamp.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
package packngo
import (
"strconv"
"time"
)
// Timestamp represents a time that can be unmarshalled from a JSON string
// formatted as either an RFC3339 or Unix timestamp. All
// exported methods of time.Time can be called on Timestamp.
type Timestamp struct {
time.Time
}
func (t Timestamp) String() string {
return t.Time.String()
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// Time is expected in RFC3339 or Unix format.
func (t *Timestamp) UnmarshalJSON(data []byte) (err error) {
str := string(data)
i, err := strconv.ParseInt(str, 10, 64)
if err == nil {
t.Time = time.Unix(i, 0)
} else {
t.Time, err = time.Parse(`"`+time.RFC3339+`"`, str)
}
return
}
// Equal reports whether t and u are equal based on time.Equal
func (t Timestamp) Equal(u Timestamp) bool {
return t.Time.Equal(u.Time)
}

53
vendor/github.com/packethost/packngo/user.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
package packngo
const userBasePath = "/users"
// UserService interface defines available user methods
type UserService interface {
Get(string) (*User, *Response, error)
}
// 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"`
}
func (u User) String() string {
return Stringify(u)
}
// UserServiceOp implements UserService
type UserServiceOp struct {
client *Client
}
// 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)
if err != nil {
return nil, resp, err
}
return user, resp, err
}

112
vendor/github.com/packethost/packngo/utils.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
package packngo
import (
"bytes"
"fmt"
"io"
"reflect"
)
var timestampType = reflect.TypeOf(Timestamp{})
// Stringify creates a string representation of the provided message
func Stringify(message interface{}) string {
var buf bytes.Buffer
v := reflect.ValueOf(message)
stringifyValue(&buf, v)
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)
buf.ReadFrom(stream)
return buf.String()
}
// stringifyValue was graciously cargoculted from the goprotubuf library
func stringifyValue(w io.Writer, val reflect.Value) {
if val.Kind() == reflect.Ptr && val.IsNil() {
w.Write([]byte("<nil>"))
return
}
v := reflect.Indirect(val)
switch v.Kind() {
case reflect.String:
fmt.Fprintf(w, `"%s"`, v)
case reflect.Slice:
w.Write([]byte{'['})
for i := 0; i < v.Len(); i++ {
if i > 0 {
w.Write([]byte{' '})
}
stringifyValue(w, v.Index(i))
}
w.Write([]byte{']'})
return
case reflect.Struct:
if v.Type().Name() != "" {
w.Write([]byte(v.Type().String()))
}
// special handling of Timestamp values
if v.Type() == timestampType {
fmt.Fprintf(w, "{%s}", v.Interface())
return
}
w.Write([]byte{'{'})
var sep bool
for i := 0; i < v.NumField(); i++ {
fv := v.Field(i)
if fv.Kind() == reflect.Ptr && fv.IsNil() {
continue
}
if fv.Kind() == reflect.Slice && fv.IsNil() {
continue
}
if sep {
w.Write([]byte(", "))
} else {
sep = true
}
w.Write([]byte(v.Type().Field(i).Name))
w.Write([]byte{':'})
stringifyValue(w, fv)
}
w.Write([]byte{'}'})
default:
if v.CanInterface() {
fmt.Fprint(w, v.Interface())
}
}
}

146
vendor/github.com/packethost/packngo/volumes.go generated vendored Normal file
View File

@ -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"`
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)
}
// 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?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)
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
}