chore: bump hcloud dependency

This commit is contained in:
Andre Hilsendeger 2019-08-05 10:10:39 +02:00
parent 0eebebaedb
commit d832cd6f67
22 changed files with 1099 additions and 186 deletions

3
go.mod
View File

@ -60,7 +60,7 @@ require (
github.com/hashicorp/serf v0.8.2 // indirect github.com/hashicorp/serf v0.8.2 // indirect
github.com/hashicorp/vault v1.1.0 github.com/hashicorp/vault v1.1.0
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
github.com/hetznercloud/hcloud-go v1.12.0 github.com/hetznercloud/hcloud-go v1.15.1
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961
github.com/joyent/triton-go v0.0.0-20180116165742-545edbe0d564 github.com/joyent/triton-go v0.0.0-20180116165742-545edbe0d564
@ -83,6 +83,7 @@ require (
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7 github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7
github.com/mitchellh/go-homedir v1.0.0 github.com/mitchellh/go-homedir v1.0.0
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
github.com/mitchellh/gox v1.0.1 // indirect
github.com/mitchellh/iochan v1.0.0 github.com/mitchellh/iochan v1.0.0
github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc
github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557 github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557

7
go.sum
View File

@ -191,6 +191,7 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
@ -210,8 +211,8 @@ github.com/hashicorp/vault v1.1.0 h1:v79NUgO5xCZnXVzUkIqFOXtP8YhpnHAi1fk3eo9cuOE
github.com/hashicorp/vault v1.1.0/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= github.com/hashicorp/vault v1.1.0/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hetznercloud/hcloud-go v1.12.0 h1:ugZO8a8ADekqSWi7xWlcs6pxr4QE0tw5VnyjXcL5n28= github.com/hetznercloud/hcloud-go v1.15.1 h1:G8Q+xyAqQ5IUY7yq4HKZgkabFa0S/VXJXq3TGCeT8JM=
github.com/hetznercloud/hcloud-go v1.12.0/go.mod h1:g5pff0YNAZywQaivY/CmhUYFVp7oP0nu3MiODC2W4Hw= github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2wrVdERJgCtxsF3Q=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 h1:MIteIoIQ5nFoOmwEHPDsqng8d0dtKj3lCnQCwGvtxXc= github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 h1:MIteIoIQ5nFoOmwEHPDsqng8d0dtKj3lCnQCwGvtxXc=
@ -284,6 +285,8 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed h1:FI2NIv6fpef6BQl2u3IZX/Cj20tfypRF4yd+uaHOMtI= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed h1:FI2NIv6fpef6BQl2u3IZX/Cj20tfypRF4yd+uaHOMtI=
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=

View File

@ -3,6 +3,7 @@ package hcloud
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"time" "time"
"github.com/hetznercloud/hcloud-go/hcloud/schema" "github.com/hetznercloud/hcloud-go/hcloud/schema"
@ -95,11 +96,24 @@ func (c *ActionClient) GetByID(ctx context.Context, id int) (*Action, *Response,
// ActionListOpts specifies options for listing actions. // ActionListOpts specifies options for listing actions.
type ActionListOpts struct { type ActionListOpts struct {
ListOpts ListOpts
Status []ActionStatus
Sort []string
}
func (l ActionListOpts) values() url.Values {
vals := l.ListOpts.values()
for _, status := range l.Status {
vals.Add("status", string(status))
}
for _, sort := range l.Sort {
vals.Add("sort", sort)
}
return vals
} }
// List returns a list of actions for a specific page. // List returns a list of actions for a specific page.
func (c *ActionClient) List(ctx context.Context, opts ActionListOpts) ([]*Action, *Response, error) { func (c *ActionClient) List(ctx context.Context, opts ActionListOpts) ([]*Action, *Response, error) {
path := "/actions?" + valuesForListOpts(opts.ListOpts).Encode() path := "/actions?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -170,7 +184,7 @@ func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-cha
a, _, err := c.GetByID(ctx, action.ID) a, _, err := c.GetByID(ctx, action.ID)
if err != nil { if err != nil {
errCh <- ctx.Err() errCh <- err
return return
} }

View File

@ -9,6 +9,7 @@ import (
"io/ioutil" "io/ioutil"
"math" "math"
"net/http" "net/http"
"net/http/httputil"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -55,6 +56,7 @@ type Client struct {
applicationName string applicationName string
applicationVersion string applicationVersion string
userAgent string userAgent string
debugWriter io.Writer
Action ActionClient Action ActionClient
Datacenter DatacenterClient Datacenter DatacenterClient
@ -62,6 +64,7 @@ type Client struct {
Image ImageClient Image ImageClient
ISO ISOClient ISO ISOClient
Location LocationClient Location LocationClient
Network NetworkClient
Pricing PricingClient Pricing PricingClient
Server ServerClient Server ServerClient
ServerType ServerTypeClient ServerType ServerTypeClient
@ -111,6 +114,14 @@ func WithApplication(name, version string) ClientOption {
} }
} }
// WithDebugWriter configures a Client to print debug information to the given
// writer. To, for example, print debug information on stderr, set it to os.Stderr.
func WithDebugWriter(debugWriter io.Writer) ClientOption {
return func(client *Client) {
client.debugWriter = debugWriter
}
}
// NewClient creates a new client. // NewClient creates a new client.
func NewClient(options ...ClientOption) *Client { func NewClient(options ...ClientOption) *Client {
client := &Client{ client := &Client{
@ -132,6 +143,7 @@ func NewClient(options ...ClientOption) *Client {
client.Image = ImageClient{client: client} client.Image = ImageClient{client: client}
client.ISO = ISOClient{client: client} client.ISO = ISOClient{client: client}
client.Location = LocationClient{client: client} client.Location = LocationClient{client: client}
client.Network = NetworkClient{client: client}
client.Pricing = PricingClient{client: client} client.Pricing = PricingClient{client: client}
client.Server = ServerClient{client: client} client.Server = ServerClient{client: client}
client.ServerType = ServerTypeClient{client: client} client.ServerType = ServerTypeClient{client: client}
@ -167,7 +179,6 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
return nil, err return nil, err
} }
response := &Response{Response: resp} response := &Response{Response: resp}
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
resp.Body.Close() resp.Body.Close()
@ -176,6 +187,20 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
resp.Body.Close() resp.Body.Close()
resp.Body = ioutil.NopCloser(bytes.NewReader(body)) resp.Body = ioutil.NopCloser(bytes.NewReader(body))
if c.debugWriter != nil {
dumpReq, err := httputil.DumpRequest(r, true)
if err != nil {
return nil, err
}
fmt.Fprintf(c.debugWriter, "--- Request:\n%s\n\n", dumpReq)
dumpResp, err := httputil.DumpResponse(resp, true)
if err != nil {
return nil, err
}
fmt.Fprintf(c.debugWriter, "--- Response:\n%s\n\n", dumpResp)
}
if err = response.readMeta(body); err != nil { if err = response.readMeta(body); err != nil {
return response, fmt.Errorf("hcloud: error reading response meta data: %s", err) return response, fmt.Errorf("hcloud: error reading response meta data: %s", err)
} }
@ -314,16 +339,16 @@ type ListOpts struct {
LabelSelector string // Label selector for filtering by labels LabelSelector string // Label selector for filtering by labels
} }
func valuesForListOpts(opts ListOpts) url.Values { func (l ListOpts) values() url.Values {
vals := url.Values{} vals := url.Values{}
if opts.Page > 0 { if l.Page > 0 {
vals.Add("page", strconv.Itoa(opts.Page)) vals.Add("page", strconv.Itoa(l.Page))
} }
if opts.PerPage > 0 { if l.PerPage > 0 {
vals.Add("per_page", strconv.Itoa(opts.PerPage)) vals.Add("per_page", strconv.Itoa(l.PerPage))
} }
if len(opts.LabelSelector) > 0 { if len(l.LabelSelector) > 0 {
vals.Add("label_selector", opts.LabelSelector) vals.Add("label_selector", l.LabelSelector)
} }
return vals return vals
} }

View File

@ -49,22 +49,11 @@ func (c *DatacenterClient) GetByID(ctx context.Context, id int) (*Datacenter, *R
// GetByName retrieves an datacenter by its name. If the datacenter does not exist, nil is returned. // GetByName retrieves an datacenter by its name. If the datacenter does not exist, nil is returned.
func (c *DatacenterClient) GetByName(ctx context.Context, name string) (*Datacenter, *Response, error) { func (c *DatacenterClient) GetByName(ctx context.Context, name string) (*Datacenter, *Response, error) {
path := "/datacenters?name=" + url.QueryEscape(name) datacenters, response, err := c.List(ctx, DatacenterListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(datacenters) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return datacenters[0], response, err
var body schema.DatacenterListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.Datacenters) == 0 {
return nil, resp, nil
}
return DatacenterFromSchema(body.Datacenters[0]), resp, nil
} }
// Get retrieves a datacenter by its ID if the input can be parsed as an integer, otherwise it // Get retrieves a datacenter by its ID if the input can be parsed as an integer, otherwise it
@ -79,11 +68,20 @@ func (c *DatacenterClient) Get(ctx context.Context, idOrName string) (*Datacente
// DatacenterListOpts specifies options for listing datacenters. // DatacenterListOpts specifies options for listing datacenters.
type DatacenterListOpts struct { type DatacenterListOpts struct {
ListOpts ListOpts
Name string
}
func (l DatacenterListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
return vals
} }
// List returns a list of datacenters for a specific page. // List returns a list of datacenters for a specific page.
func (c *DatacenterClient) List(ctx context.Context, opts DatacenterListOpts) ([]*Datacenter, *Response, error) { func (c *DatacenterClient) List(ctx context.Context, opts DatacenterListOpts) ([]*Datacenter, *Response, error) {
path := "/datacenters?" + valuesForListOpts(opts.ListOpts).Encode() path := "/datacenters?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -20,6 +20,8 @@ const (
ErrorCodeUniquenessError ErrorCode = "uniqueness_error" // One or more fields must be unique ErrorCodeUniquenessError ErrorCode = "uniqueness_error" // One or more fields must be unique
ErrorCodeProtected ErrorCode = "protected" // The actions you are trying is protected ErrorCodeProtected ErrorCode = "protected" // The actions you are trying is protected
ErrorCodeMaintenance ErrorCode = "maintenance" // Cannot perform operation due to maintenance ErrorCodeMaintenance ErrorCode = "maintenance" // Cannot perform operation due to maintenance
ErrorCodeConflict ErrorCode = "conflict" // The resource has changed during the request, please retry
ErrorCodeServerAlreadyAttached ErrorCode = "server_already_attached" // The server is already attached to the resource
// Deprecated error codes // Deprecated error codes

View File

@ -7,6 +7,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/url"
"time"
"github.com/hetznercloud/hcloud-go/hcloud/schema" "github.com/hetznercloud/hcloud-go/hcloud/schema"
) )
@ -15,6 +17,7 @@ import (
type FloatingIP struct { type FloatingIP struct {
ID int ID int
Description string Description string
Created time.Time
IP net.IP IP net.IP
Network *net.IPNet Network *net.IPNet
Type FloatingIPType Type FloatingIPType
@ -74,9 +77,13 @@ type FloatingIPListOpts struct {
ListOpts ListOpts
} }
func (l FloatingIPListOpts) values() url.Values {
return l.ListOpts.values()
}
// List returns a list of Floating IPs for a specific page. // List returns a list of Floating IPs for a specific page.
func (c *FloatingIPClient) List(ctx context.Context, opts FloatingIPListOpts) ([]*FloatingIP, *Response, error) { func (c *FloatingIPClient) List(ctx context.Context, opts FloatingIPListOpts) ([]*FloatingIP, *Response, error) {
path := "/floating_ips?" + valuesForListOpts(opts.ListOpts).Encode() path := "/floating_ips?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -2,4 +2,4 @@
package hcloud package hcloud
// Version is the library's version following Semantic Versioning. // Version is the library's version following Semantic Versioning.
const Version = "1.12.0" const Version = "1.15.1"

View File

@ -91,22 +91,11 @@ func (c *ImageClient) GetByID(ctx context.Context, id int) (*Image, *Response, e
// GetByName retrieves an image by its name. If the image does not exist, nil is returned. // GetByName retrieves an image by its name. If the image does not exist, nil is returned.
func (c *ImageClient) GetByName(ctx context.Context, name string) (*Image, *Response, error) { func (c *ImageClient) GetByName(ctx context.Context, name string) (*Image, *Response, error) {
path := "/images?name=" + url.QueryEscape(name) images, response, err := c.List(ctx, ImageListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(images) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return images[0], response, err
var body schema.ImageListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.Images) == 0 {
return nil, resp, nil
}
return ImageFromSchema(body.Images[0]), resp, nil
} }
// Get retrieves an image by its ID if the input can be parsed as an integer, otherwise it // Get retrieves an image by its ID if the input can be parsed as an integer, otherwise it
@ -121,11 +110,36 @@ func (c *ImageClient) Get(ctx context.Context, idOrName string) (*Image, *Respon
// ImageListOpts specifies options for listing images. // ImageListOpts specifies options for listing images.
type ImageListOpts struct { type ImageListOpts struct {
ListOpts ListOpts
Type []ImageType
BoundTo *Server
Name string
Sort []string
Status []ImageStatus
}
func (l ImageListOpts) values() url.Values {
vals := l.ListOpts.values()
for _, typ := range l.Type {
vals.Add("type", string(typ))
}
if l.BoundTo != nil {
vals.Add("bound_to", strconv.Itoa(l.BoundTo.ID))
}
if l.Name != "" {
vals.Add("name", l.Name)
}
for _, sort := range l.Sort {
vals.Add("sort", sort)
}
for _, status := range l.Status {
vals.Add("status", string(status))
}
return vals
} }
// List returns a list of images for a specific page. // List returns a list of images for a specific page.
func (c *ImageClient) List(ctx context.Context, opts ImageListOpts) ([]*Image, *Response, error) { func (c *ImageClient) List(ctx context.Context, opts ImageListOpts) ([]*Image, *Response, error) {
path := "/images?" + valuesForListOpts(opts.ListOpts).Encode() path := "/images?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -145,7 +159,7 @@ func (c *ImageClient) List(ctx context.Context, opts ImageListOpts) ([]*Image, *
// All returns all images. // All returns all images.
func (c *ImageClient) All(ctx context.Context) ([]*Image, error) { func (c *ImageClient) All(ctx context.Context) ([]*Image, error) {
return c.AllWithOpts(ctx, ImageListOpts{ListOpts{PerPage: 50}}) return c.AllWithOpts(ctx, ImageListOpts{ListOpts: ListOpts{PerPage: 50}})
} }
// AllWithOpts returns all images for the given options. // AllWithOpts returns all images for the given options.

View File

@ -60,22 +60,11 @@ func (c *ISOClient) GetByID(ctx context.Context, id int) (*ISO, *Response, error
// GetByName retrieves an ISO by its name. // GetByName retrieves an ISO by its name.
func (c *ISOClient) GetByName(ctx context.Context, name string) (*ISO, *Response, error) { func (c *ISOClient) GetByName(ctx context.Context, name string) (*ISO, *Response, error) {
path := "/isos?name=" + url.QueryEscape(name) isos, response, err := c.List(ctx, ISOListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(isos) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return isos[0], response, err
var body schema.ISOListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.ISOs) == 0 {
return nil, resp, nil
}
return ISOFromSchema(body.ISOs[0]), resp, nil
} }
// Get retrieves an ISO by its ID if the input can be parsed as an integer, otherwise it retrieves an ISO by its name. // Get retrieves an ISO by its ID if the input can be parsed as an integer, otherwise it retrieves an ISO by its name.
@ -89,11 +78,20 @@ func (c *ISOClient) Get(ctx context.Context, idOrName string) (*ISO, *Response,
// ISOListOpts specifies options for listing isos. // ISOListOpts specifies options for listing isos.
type ISOListOpts struct { type ISOListOpts struct {
ListOpts ListOpts
Name string
}
func (l ISOListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
return vals
} }
// List returns a list of ISOs for a specific page. // List returns a list of ISOs for a specific page.
func (c *ISOClient) List(ctx context.Context, opts ISOListOpts) ([]*ISO, *Response, error) { func (c *ISOClient) List(ctx context.Context, opts ISOListOpts) ([]*ISO, *Response, error) {
path := "/isos?" + valuesForListOpts(opts.ListOpts).Encode() path := "/isos?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -18,6 +18,7 @@ type Location struct {
City string City string
Latitude float64 Latitude float64
Longitude float64 Longitude float64
NetworkZone NetworkZone
} }
// LocationClient is a client for the location API. // LocationClient is a client for the location API.
@ -45,22 +46,11 @@ func (c *LocationClient) GetByID(ctx context.Context, id int) (*Location, *Respo
// GetByName retrieves an location by its name. If the location does not exist, nil is returned. // GetByName retrieves an location by its name. If the location does not exist, nil is returned.
func (c *LocationClient) GetByName(ctx context.Context, name string) (*Location, *Response, error) { func (c *LocationClient) GetByName(ctx context.Context, name string) (*Location, *Response, error) {
path := "/locations?name=" + url.QueryEscape(name) locations, response, err := c.List(ctx, LocationListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(locations) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return locations[0], response, err
var body schema.LocationListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.Locations) == 0 {
return nil, resp, nil
}
return LocationFromSchema(body.Locations[0]), resp, nil
} }
// Get retrieves a location by its ID if the input can be parsed as an integer, otherwise it // Get retrieves a location by its ID if the input can be parsed as an integer, otherwise it
@ -75,11 +65,20 @@ func (c *LocationClient) Get(ctx context.Context, idOrName string) (*Location, *
// LocationListOpts specifies options for listing location. // LocationListOpts specifies options for listing location.
type LocationListOpts struct { type LocationListOpts struct {
ListOpts ListOpts
Name string
}
func (l LocationListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
return vals
} }
// List returns a list of locations for a specific page. // List returns a list of locations for a specific page.
func (c *LocationClient) List(ctx context.Context, opts LocationListOpts) ([]*Location, *Response, error) { func (c *LocationClient) List(ctx context.Context, opts LocationListOpts) ([]*Location, *Response, error) {
path := "/locations?" + valuesForListOpts(opts.ListOpts).Encode() path := "/locations?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -0,0 +1,445 @@
package hcloud
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"strconv"
"time"
"github.com/hetznercloud/hcloud-go/hcloud/schema"
)
// NetworkZone specifies a network zone.
type NetworkZone string
// List of available Network Zones.
const (
NetworkZoneEUCentral NetworkZone = "eu-central"
)
// NetworkSubnetType specifies a type of a subnet.
type NetworkSubnetType string
// List of available network subnet types.
const (
NetworkSubnetTypeServer NetworkSubnetType = "server"
)
// Network represents a network in the Hetzner Cloud.
type Network struct {
ID int
Name string
Created time.Time
IPRange *net.IPNet
Subnets []NetworkSubnet
Routes []NetworkRoute
Servers []*Server
Protection NetworkProtection
Labels map[string]string
}
// NetworkSubnet represents a subnet of a network in the Hetzner Cloud.
type NetworkSubnet struct {
Type NetworkSubnetType
IPRange *net.IPNet
NetworkZone NetworkZone
Gateway net.IP
}
// NetworkRoute represents a route of a network.
type NetworkRoute struct {
Destination *net.IPNet
Gateway net.IP
}
// NetworkProtection represents the protection level of a network.
type NetworkProtection struct {
Delete bool
}
// NetworkClient is a client for the network API.
type NetworkClient struct {
client *Client
}
// GetByID retrieves a network by its ID. If the network does not exist, nil is returned.
func (c *NetworkClient) GetByID(ctx context.Context, id int) (*Network, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/networks/%d", id), nil)
if err != nil {
return nil, nil, err
}
var body schema.NetworkGetResponse
resp, err := c.client.Do(req, &body)
if err != nil {
if IsError(err, ErrorCodeNotFound) {
return nil, resp, nil
}
return nil, nil, err
}
return NetworkFromSchema(body.Network), resp, nil
}
// GetByName retrieves a network by its name. If the network does not exist, nil is returned.
func (c *NetworkClient) GetByName(ctx context.Context, name string) (*Network, *Response, error) {
Networks, response, err := c.List(ctx, NetworkListOpts{Name: name})
if len(Networks) == 0 {
return nil, response, err
}
return Networks[0], response, err
}
// Get retrieves a network by its ID if the input can be parsed as an integer, otherwise it
// retrieves a network by its name. If the network does not exist, nil is returned.
func (c *NetworkClient) Get(ctx context.Context, idOrName string) (*Network, *Response, error) {
if id, err := strconv.Atoi(idOrName); err == nil {
return c.GetByID(ctx, int(id))
}
return c.GetByName(ctx, idOrName)
}
// NetworkListOpts specifies options for listing networks.
type NetworkListOpts struct {
ListOpts
Name string
}
func (l NetworkListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
return vals
}
// List returns a list of networks for a specific page.
func (c *NetworkClient) List(ctx context.Context, opts NetworkListOpts) ([]*Network, *Response, error) {
path := "/networks?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil {
return nil, nil, err
}
var body schema.NetworkListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
Networks := make([]*Network, 0, len(body.Networks))
for _, s := range body.Networks {
Networks = append(Networks, NetworkFromSchema(s))
}
return Networks, resp, nil
}
// All returns all networks.
func (c *NetworkClient) All(ctx context.Context) ([]*Network, error) {
return c.AllWithOpts(ctx, NetworkListOpts{ListOpts: ListOpts{PerPage: 50}})
}
// AllWithOpts returns all networks for the given options.
func (c *NetworkClient) AllWithOpts(ctx context.Context, opts NetworkListOpts) ([]*Network, error) {
var allNetworks []*Network
_, err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
Networks, resp, err := c.List(ctx, opts)
if err != nil {
return resp, err
}
allNetworks = append(allNetworks, Networks...)
return resp, nil
})
if err != nil {
return nil, err
}
return allNetworks, nil
}
// Delete deletes a network.
func (c *NetworkClient) Delete(ctx context.Context, network *Network) (*Response, error) {
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/networks/%d", network.ID), nil)
if err != nil {
return nil, err
}
return c.client.Do(req, nil)
}
// NetworkUpdateOpts specifies options for updating a network.
type NetworkUpdateOpts struct {
Name string
Labels map[string]string
}
// Update updates a network.
func (c *NetworkClient) Update(ctx context.Context, network *Network, opts NetworkUpdateOpts) (*Network, *Response, error) {
reqBody := schema.NetworkUpdateRequest{
Name: opts.Name,
}
if opts.Labels != nil {
reqBody.Labels = &opts.Labels
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/networks/%d", network.ID)
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkUpdateResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return NetworkFromSchema(respBody.Network), resp, nil
}
// NetworkCreateOpts specifies options for creating a new network.
type NetworkCreateOpts struct {
Name string
IPRange *net.IPNet
Subnets []NetworkSubnet
Routes []NetworkRoute
Labels map[string]string
}
// Validate checks if options are valid.
func (o NetworkCreateOpts) Validate() error {
if o.Name == "" {
return errors.New("missing name")
}
if o.IPRange == nil || o.IPRange.String() == "" {
return errors.New("missing IP range")
}
return nil
}
// Create creates a new network.
func (c *NetworkClient) Create(ctx context.Context, opts NetworkCreateOpts) (*Network, *Response, error) {
if err := opts.Validate(); err != nil {
return nil, nil, err
}
reqBody := schema.NetworkCreateRequest{
Name: opts.Name,
IPRange: opts.IPRange.String(),
}
for _, subnet := range opts.Subnets {
reqBody.Subnets = append(reqBody.Subnets, schema.NetworkSubnet{
Type: string(subnet.Type),
IPRange: subnet.IPRange.String(),
NetworkZone: string(subnet.NetworkZone),
})
}
for _, route := range opts.Routes {
reqBody.Routes = append(reqBody.Routes, schema.NetworkRoute{
Destination: route.Destination.String(),
Gateway: route.Gateway.String(),
})
}
if opts.Labels != nil {
reqBody.Labels = &opts.Labels
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
req, err := c.client.NewRequest(ctx, "POST", "/networks", bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkCreateResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return NetworkFromSchema(respBody.Network), resp, nil
}
// NetworkChangeIPRangeOpts specifies options for changing the IP range of a network.
type NetworkChangeIPRangeOpts struct {
IPRange *net.IPNet
}
// ChangeIPRange changes the IP range of a network.
func (c *NetworkClient) ChangeIPRange(ctx context.Context, network *Network, opts NetworkChangeIPRangeOpts) (*Action, *Response, error) {
reqBody := schema.NetworkActionChangeIPRangeRequest{
IPRange: opts.IPRange.String(),
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/networks/%d/actions/change_ip_range", network.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkActionChangeIPRangeResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}
// NetworkAddSubnetOpts specifies options for adding a subnet to a network.
type NetworkAddSubnetOpts struct {
Subnet NetworkSubnet
}
// AddSubnet adds a subnet to a network.
func (c *NetworkClient) AddSubnet(ctx context.Context, network *Network, opts NetworkAddSubnetOpts) (*Action, *Response, error) {
reqBody := schema.NetworkActionAddSubnetRequest{
Type: string(opts.Subnet.Type),
IPRange: opts.Subnet.IPRange.String(),
NetworkZone: string(opts.Subnet.NetworkZone),
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/networks/%d/actions/add_subnet", network.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkActionAddSubnetResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}
// NetworkDeleteSubnetOpts specifies options for deleting a subnet from a network.
type NetworkDeleteSubnetOpts struct {
Subnet NetworkSubnet
}
// DeleteSubnet deletes a subnet from a network.
func (c *NetworkClient) DeleteSubnet(ctx context.Context, network *Network, opts NetworkDeleteSubnetOpts) (*Action, *Response, error) {
reqBody := schema.NetworkActionDeleteSubnetRequest{
IPRange: opts.Subnet.IPRange.String(),
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/networks/%d/actions/delete_subnet", network.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkActionDeleteSubnetResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}
// NetworkAddRouteOpts specifies options for adding a route to a network.
type NetworkAddRouteOpts struct {
Route NetworkRoute
}
// AddRoute adds a route to a network.
func (c *NetworkClient) AddRoute(ctx context.Context, network *Network, opts NetworkAddRouteOpts) (*Action, *Response, error) {
reqBody := schema.NetworkActionAddRouteRequest{
Destination: opts.Route.Destination.String(),
Gateway: opts.Route.Gateway.String(),
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/networks/%d/actions/add_route", network.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkActionAddSubnetResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}
// NetworkDeleteRouteOpts specifies options for deleting a route from a network.
type NetworkDeleteRouteOpts struct {
Route NetworkRoute
}
// DeleteRoute deletes a route from a network.
func (c *NetworkClient) DeleteRoute(ctx context.Context, network *Network, opts NetworkDeleteRouteOpts) (*Action, *Response, error) {
reqBody := schema.NetworkActionDeleteRouteRequest{
Destination: opts.Route.Destination.String(),
Gateway: opts.Route.Gateway.String(),
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/networks/%d/actions/delete_route", network.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkActionDeleteSubnetResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, nil
}
// NetworkChangeProtectionOpts specifies options for changing the resource protection level of a network.
type NetworkChangeProtectionOpts struct {
Delete *bool
}
// ChangeProtection changes the resource protection level of a network.
func (c *NetworkClient) ChangeProtection(ctx context.Context, network *Network, opts NetworkChangeProtectionOpts) (*Action, *Response, error) {
reqBody := schema.NetworkActionChangeProtectionRequest{
Delete: opts.Delete,
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/networks/%d/actions/change_protection", network.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.NetworkActionChangeProtectionResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, err
}

View File

@ -50,6 +50,7 @@ func FloatingIPFromSchema(s schema.FloatingIP) *FloatingIP {
ID: s.ID, ID: s.ID,
Type: FloatingIPType(s.Type), Type: FloatingIPType(s.Type),
HomeLocation: LocationFromSchema(s.HomeLocation), HomeLocation: LocationFromSchema(s.HomeLocation),
Created: s.Created,
Blocked: s.Blocked, Blocked: s.Blocked,
Protection: FloatingIPProtection{ Protection: FloatingIPProtection{
Delete: s.Protection.Delete, Delete: s.Protection.Delete,
@ -98,6 +99,7 @@ func LocationFromSchema(s schema.Location) *Location {
City: s.City, City: s.City,
Latitude: s.Latitude, Latitude: s.Latitude,
Longitude: s.Longitude, Longitude: s.Longitude,
NetworkZone: NetworkZone(s.NetworkZone),
} }
} }
@ -162,6 +164,9 @@ func ServerFromSchema(s schema.Server) *Server {
for _, id := range s.Volumes { for _, id := range s.Volumes {
server.Volumes = append(server.Volumes, &Volume{ID: id}) server.Volumes = append(server.Volumes, &Volume{ID: id})
} }
for _, privNet := range s.PrivateNet {
server.PrivateNet = append(server.PrivateNet, ServerPrivateNetFromSchema(privNet))
}
return server return server
} }
@ -202,6 +207,19 @@ func ServerPublicNetIPv6FromSchema(s schema.ServerPublicNetIPv6) ServerPublicNet
return ipv6 return ipv6
} }
// ServerPrivateNetFromSchema converts a schema.ServerPrivateNet to a ServerPrivateNet.
func ServerPrivateNetFromSchema(s schema.ServerPrivateNet) ServerPrivateNet {
n := ServerPrivateNet{
Network: &Network{ID: s.Network},
IP: net.ParseIP(s.IP),
MACAddress: s.MACAddress,
}
for _, ip := range s.AliasIPs {
n.Aliases = append(n.Aliases, net.ParseIP(ip))
}
return n
}
// ServerTypeFromSchema converts a schema.ServerType to a ServerType. // ServerTypeFromSchema converts a schema.ServerType to a ServerType.
func ServerTypeFromSchema(s schema.ServerType) *ServerType { func ServerTypeFromSchema(s schema.ServerType) *ServerType {
st := &ServerType{ st := &ServerType{
@ -311,6 +329,56 @@ func VolumeFromSchema(s schema.Volume) *Volume {
return v return v
} }
// NetworkFromSchema converts a schema.Network to a Network.
func NetworkFromSchema(s schema.Network) *Network {
n := &Network{
ID: s.ID,
Name: s.Name,
Created: s.Created,
Protection: NetworkProtection{
Delete: s.Protection.Delete,
},
Labels: map[string]string{},
}
_, n.IPRange, _ = net.ParseCIDR(s.IPRange)
for _, subnet := range s.Subnets {
n.Subnets = append(n.Subnets, NetworkSubnetFromSchema(subnet))
}
for _, route := range s.Routes {
n.Routes = append(n.Routes, NetworkRouteFromSchema(route))
}
for _, serverID := range s.Servers {
n.Servers = append(n.Servers, &Server{ID: serverID})
}
for key, value := range s.Labels {
n.Labels[key] = value
}
return n
}
// NetworkSubnetFromSchema converts a schema.NetworkSubnet to a NetworkSubnet.
func NetworkSubnetFromSchema(s schema.NetworkSubnet) NetworkSubnet {
sn := NetworkSubnet{
Type: NetworkSubnetType(s.Type),
NetworkZone: NetworkZone(s.NetworkZone),
Gateway: net.ParseIP(s.Gateway),
}
_, sn.IPRange, _ = net.ParseCIDR(s.IPRange)
return sn
}
// NetworkRouteFromSchema converts a schema.NetworkRoute to a NetworkRoute.
func NetworkRouteFromSchema(s schema.NetworkRoute) NetworkRoute {
r := NetworkRoute{
Gateway: net.ParseIP(s.Gateway),
}
_, r.Destination, _ = net.ParseCIDR(s.Destination)
return r
}
// PaginationFromSchema converts a schema.MetaPagination to a Pagination. // PaginationFromSchema converts a schema.MetaPagination to a Pagination.
func PaginationFromSchema(s schema.MetaPagination) Pagination { func PaginationFromSchema(s schema.MetaPagination) Pagination {
return Pagination{ return Pagination{

View File

@ -1,9 +1,12 @@
package schema package schema
import "time"
// FloatingIP defines the schema of a Floating IP. // FloatingIP defines the schema of a Floating IP.
type FloatingIP struct { type FloatingIP struct {
ID int `json:"id"` ID int `json:"id"`
Description *string `json:"description"` Description *string `json:"description"`
Created time.Time `json:"created"`
IP string `json:"ip"` IP string `json:"ip"`
Type string `json:"type"` Type string `json:"type"`
Server *int `json:"server"` Server *int `json:"server"`

View File

@ -9,6 +9,7 @@ type Location struct {
City string `json:"city"` City string `json:"city"`
Latitude float64 `json:"latitude"` Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"` Longitude float64 `json:"longitude"`
NetworkZone string `json:"network_zone"`
} }
// LocationGetResponse defines the schema of the response when retrieving a single location. // LocationGetResponse defines the schema of the response when retrieving a single location.

View File

@ -0,0 +1,150 @@
package schema
import "time"
// Network defines the schema of a network.
type Network struct {
ID int `json:"id"`
Name string `json:"name"`
Created time.Time `json:"created"`
IPRange string `json:"ip_range"`
Subnets []NetworkSubnet `json:"subnets"`
Routes []NetworkRoute `json:"routes"`
Servers []int `json:"servers"`
Protection NetworkProtection `json:"protection"`
Labels map[string]string `json:"labels"`
}
// NetworkSubnet represents a subnet of a network.
type NetworkSubnet struct {
Type string `json:"type"`
IPRange string `json:"ip_range"`
NetworkZone string `json:"network_zone"`
Gateway string `json:"gateway"`
}
// NetworkRoute represents a route of a network.
type NetworkRoute struct {
Destination string `json:"destination"`
Gateway string `json:"gateway"`
}
// NetworkProtection represents the protection level of a network.
type NetworkProtection struct {
Delete bool `json:"delete"`
}
// NetworkUpdateRequest defines the schema of the request to update a network.
type NetworkUpdateRequest struct {
Name string `json:"name,omitempty"`
Labels *map[string]string `json:"labels,omitempty"`
}
// NetworkUpdateResponse defines the schema of the response when updating a network.
type NetworkUpdateResponse struct {
Network Network `json:"network"`
}
// NetworkListResponse defines the schema of the response when
// listing networks.
type NetworkListResponse struct {
Networks []Network `json:"networks"`
}
// NetworkGetResponse defines the schema of the response when
// retrieving a single network.
type NetworkGetResponse struct {
Network Network `json:"network"`
}
// NetworkCreateRequest defines the schema of the request to create a network.
type NetworkCreateRequest struct {
Name string `json:"name"`
IPRange string `json:"ip_range"`
Subnets []NetworkSubnet `json:"subnets,omitempty"`
Routes []NetworkRoute `json:"routes,omitempty"`
Labels *map[string]string `json:"labels,omitempty"`
}
// NetworkCreateResponse defines the schema of the response when
// creating a network.
type NetworkCreateResponse struct {
Network Network `json:"network"`
}
// NetworkActionChangeIPRangeRequest defines the schema of the request to
// change the IP range of a network.
type NetworkActionChangeIPRangeRequest struct {
IPRange string `json:"ip_range"`
}
// NetworkActionChangeIPRangeResponse defines the schema of the response when
// changing the IP range of a network.
type NetworkActionChangeIPRangeResponse struct {
Action Action `json:"action"`
}
// NetworkActionAddSubnetRequest defines the schema of the request to
// add a subnet to a network.
type NetworkActionAddSubnetRequest struct {
Type string `json:"type"`
IPRange string `json:"ip_range,omitempty"`
NetworkZone string `json:"network_zone"`
Gateway string `json:"gateway"`
}
// NetworkActionAddSubnetResponse defines the schema of the response when
// adding a subnet to a network.
type NetworkActionAddSubnetResponse struct {
Action Action `json:"action"`
}
// NetworkActionDeleteSubnetRequest defines the schema of the request to
// delete a subnet from a network.
type NetworkActionDeleteSubnetRequest struct {
IPRange string `json:"ip_range"`
}
// NetworkActionDeleteSubnetResponse defines the schema of the response when
// deleting a subnet from a network.
type NetworkActionDeleteSubnetResponse struct {
Action Action `json:"action"`
}
// NetworkActionAddRouteRequest defines the schema of the request to
// add a route to a network.
type NetworkActionAddRouteRequest struct {
Destination string `json:"destination"`
Gateway string `json:"gateway"`
}
// NetworkActionAddRouteResponse defines the schema of the response when
// adding a route to a network.
type NetworkActionAddRouteResponse struct {
Action Action `json:"action"`
}
// NetworkActionDeleteRouteRequest defines the schema of the request to
// delete a route from a network.
type NetworkActionDeleteRouteRequest struct {
Destination string `json:"destination"`
Gateway string `json:"gateway"`
}
// NetworkActionDeleteRouteResponse defines the schema of the response when
// deleting a route from a network.
type NetworkActionDeleteRouteResponse struct {
Action Action `json:"action"`
}
// NetworkActionChangeProtectionRequest defines the schema of the request to
// change the resource protection of a network.
type NetworkActionChangeProtectionRequest struct {
Delete *bool `json:"delete,omitempty"`
}
// NetworkActionChangeProtectionResponse defines the schema of the response when
// changing the resource protection of a network.
type NetworkActionChangeProtectionResponse struct {
Action Action `json:"action"`
}

View File

@ -9,6 +9,7 @@ type Server struct {
Status string `json:"status"` Status string `json:"status"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
PublicNet ServerPublicNet `json:"public_net"` PublicNet ServerPublicNet `json:"public_net"`
PrivateNet []ServerPrivateNet `json:"private_net"`
ServerType ServerType `json:"server_type"` ServerType ServerType `json:"server_type"`
IncludedTraffic uint64 `json:"included_traffic"` IncludedTraffic uint64 `json:"included_traffic"`
OutgoingTraffic *uint64 `json:"outgoing_traffic"` OutgoingTraffic *uint64 `json:"outgoing_traffic"`
@ -61,6 +62,14 @@ type ServerPublicNetIPv6DNSPtr struct {
DNSPtr string `json:"dns_ptr"` DNSPtr string `json:"dns_ptr"`
} }
// ServerPrivateNet defines the schema of a server's private network information.
type ServerPrivateNet struct {
Network int `json:"network"`
IP string `json:"ip"`
AliasIPs []string `json:"alias_ips"`
MACAddress string `json:"mac_address"`
}
// ServerGetResponse defines the schema of the response when // ServerGetResponse defines the schema of the response when
// retrieving a single server. // retrieving a single server.
type ServerGetResponse struct { type ServerGetResponse struct {
@ -87,6 +96,7 @@ type ServerCreateRequest struct {
Labels *map[string]string `json:"labels,omitempty"` Labels *map[string]string `json:"labels,omitempty"`
Automount *bool `json:"automount,omitempty"` Automount *bool `json:"automount,omitempty"`
Volumes []int `json:"volumes,omitempty"` Volumes []int `json:"volumes,omitempty"`
Networks []int `json:"networks,omitempty"`
} }
// ServerCreateResponse defines the schema of the response when // ServerCreateResponse defines the schema of the response when
@ -291,13 +301,54 @@ type ServerActionChangeDNSPtrResponse struct {
Action Action `json:"action"` Action Action `json:"action"`
} }
// ServerActionChangeProtectionRequest defines the schema of the request to change the resource protection of a server. // ServerActionChangeProtectionRequest defines the schema of the request to
// change the resource protection of a server.
type ServerActionChangeProtectionRequest struct { type ServerActionChangeProtectionRequest struct {
Rebuild *bool `json:"rebuild,omitempty"` Rebuild *bool `json:"rebuild,omitempty"`
Delete *bool `json:"delete,omitempty"` Delete *bool `json:"delete,omitempty"`
} }
// ServerActionChangeProtectionResponse defines the schema of the response when changing the resource protection of a server. // ServerActionChangeProtectionResponse defines the schema of the response when
// changing the resource protection of a server.
type ServerActionChangeProtectionResponse struct { type ServerActionChangeProtectionResponse struct {
Action Action `json:"action"` Action Action `json:"action"`
} }
// ServerActionAttachToNetworkRequest defines the schema for the request to
// attach a network to a server.
type ServerActionAttachToNetworkRequest struct {
Network int `json:"network"`
IP *string `json:"ip,omitempty"`
AliasIPs []*string `json:"alias_ips,omitempty"`
}
// ServerActionAttachToNetworkResponse defines the schema of the response when
// creating an attach_to_network server action.
type ServerActionAttachToNetworkResponse struct {
Action Action `json:"action"`
}
// ServerActionDetachFromNetworkRequest defines the schema for the request to
// detach a network from a server.
type ServerActionDetachFromNetworkRequest struct {
Network int `json:"network"`
}
// ServerActionDetachFromNetworkResponse defines the schema of the response when
// creating a detach_from_network server action.
type ServerActionDetachFromNetworkResponse struct {
Action Action `json:"action"`
}
// ServerActionChangeAliasIPsRequest defines the schema for the request to
// change a server's alias IPs in a network.
type ServerActionChangeAliasIPsRequest struct {
Network int `json:"network"`
AliasIPs []string `json:"alias_ips"`
}
// ServerActionChangeAliasIPsResponse defines the schema of the response when
// creating an change_alias_ips server action.
type ServerActionChangeAliasIPsResponse struct {
Action Action `json:"action"`
}

View File

@ -21,6 +21,7 @@ type Server struct {
Status ServerStatus Status ServerStatus
Created time.Time Created time.Time
PublicNet ServerPublicNet PublicNet ServerPublicNet
PrivateNet []ServerPrivateNet
ServerType *ServerType ServerType *ServerType
Datacenter *Datacenter Datacenter *Datacenter
IncludedTraffic uint64 IncludedTraffic uint64
@ -53,6 +54,24 @@ const (
// ServerStatusRunning is the status when a server is running. // ServerStatusRunning is the status when a server is running.
ServerStatusRunning ServerStatus = "running" ServerStatusRunning ServerStatus = "running"
// ServerStatusStarting is the status when a server is being started.
ServerStatusStarting ServerStatus = "starting"
// ServerStatusStopping is the status when a server is being stopped.
ServerStatusStopping ServerStatus = "stopping"
// ServerStatusMigrating is the status when a server is being migrated.
ServerStatusMigrating ServerStatus = "migrating"
// ServerStatusRebuilding is the status when a server is being rebuilt.
ServerStatusRebuilding ServerStatus = "rebuilding"
// ServerStatusDeleting is the status when a server is being deleted.
ServerStatusDeleting ServerStatus = "deleting"
// ServerStatusUnknown is the status when a server's state is unknown.
ServerStatusUnknown ServerStatus = "unknown"
) )
// ServerPublicNet represents a server's public network. // ServerPublicNet represents a server's public network.
@ -77,6 +96,14 @@ type ServerPublicNetIPv6 struct {
DNSPtr map[string]string DNSPtr map[string]string
} }
// ServerPrivateNet defines the schema of a server's private network information.
type ServerPrivateNet struct {
Network *Network
IP net.IP
Aliases []net.IP
MACAddress string
}
// DNSPtrForIP returns the reverse dns pointer of the ip address. // DNSPtrForIP returns the reverse dns pointer of the ip address.
func (s *ServerPublicNetIPv6) DNSPtrForIP(ip net.IP) string { func (s *ServerPublicNetIPv6) DNSPtrForIP(ip net.IP) string {
return s.DNSPtr[ip.String()] return s.DNSPtr[ip.String()]
@ -115,24 +142,13 @@ func (c *ServerClient) GetByID(ctx context.Context, id int) (*Server, *Response,
return ServerFromSchema(body.Server), resp, nil return ServerFromSchema(body.Server), resp, nil
} }
// GetByName retreives a server by its name. If the server does not exist, nil is returned. // GetByName retrieves a server by its name. If the server does not exist, nil is returned.
func (c *ServerClient) GetByName(ctx context.Context, name string) (*Server, *Response, error) { func (c *ServerClient) GetByName(ctx context.Context, name string) (*Server, *Response, error) {
path := "/servers?name=" + url.QueryEscape(name) servers, response, err := c.List(ctx, ServerListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(servers) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return servers[0], response, err
var body schema.ServerListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.Servers) == 0 {
return nil, resp, nil
}
return ServerFromSchema(body.Servers[0]), resp, nil
} }
// Get retrieves a server by its ID if the input can be parsed as an integer, otherwise it // Get retrieves a server by its ID if the input can be parsed as an integer, otherwise it
@ -147,11 +163,24 @@ func (c *ServerClient) Get(ctx context.Context, idOrName string) (*Server, *Resp
// ServerListOpts specifies options for listing servers. // ServerListOpts specifies options for listing servers.
type ServerListOpts struct { type ServerListOpts struct {
ListOpts ListOpts
Name string
Status []ServerStatus
}
func (l ServerListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
for _, status := range l.Status {
vals.Add("status", string(status))
}
return vals
} }
// List returns a list of servers for a specific page. // List returns a list of servers for a specific page.
func (c *ServerClient) List(ctx context.Context, opts ServerListOpts) ([]*Server, *Response, error) { func (c *ServerClient) List(ctx context.Context, opts ServerListOpts) ([]*Server, *Response, error) {
path := "/servers?" + valuesForListOpts(opts.ListOpts).Encode() path := "/servers?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -171,7 +200,7 @@ func (c *ServerClient) List(ctx context.Context, opts ServerListOpts) ([]*Server
// All returns all servers. // All returns all servers.
func (c *ServerClient) All(ctx context.Context) ([]*Server, error) { func (c *ServerClient) All(ctx context.Context) ([]*Server, error) {
return c.AllWithOpts(ctx, ServerListOpts{ListOpts{PerPage: 50}}) return c.AllWithOpts(ctx, ServerListOpts{ListOpts: ListOpts{PerPage: 50}})
} }
// AllWithOpts returns all servers for the given options. // AllWithOpts returns all servers for the given options.
@ -207,6 +236,7 @@ type ServerCreateOpts struct {
Labels map[string]string Labels map[string]string
Automount *bool Automount *bool
Volumes []*Volume Volumes []*Volume
Networks []*Network
} }
// Validate checks if options are valid. // Validate checks if options are valid.
@ -264,6 +294,9 @@ func (c *ServerClient) Create(ctx context.Context, opts ServerCreateOpts) (Serve
for _, volume := range opts.Volumes { for _, volume := range opts.Volumes {
reqBody.Volumes = append(reqBody.Volumes, volume.ID) reqBody.Volumes = append(reqBody.Volumes, volume.ID)
} }
for _, network := range opts.Networks {
reqBody.Networks = append(reqBody.Networks, network.ID)
}
if opts.Location != nil { if opts.Location != nil {
if opts.Location.ID != 0 { if opts.Location.ID != 0 {
@ -763,7 +796,7 @@ type ServerChangeProtectionOpts struct {
} }
// ChangeProtection changes the resource protection level of a server. // ChangeProtection changes the resource protection level of a server.
func (c *ServerClient) ChangeProtection(ctx context.Context, image *Server, opts ServerChangeProtectionOpts) (*Action, *Response, error) { func (c *ServerClient) ChangeProtection(ctx context.Context, server *Server, opts ServerChangeProtectionOpts) (*Action, *Response, error) {
reqBody := schema.ServerActionChangeProtectionRequest{ reqBody := schema.ServerActionChangeProtectionRequest{
Rebuild: opts.Rebuild, Rebuild: opts.Rebuild,
Delete: opts.Delete, Delete: opts.Delete,
@ -773,7 +806,7 @@ func (c *ServerClient) ChangeProtection(ctx context.Context, image *Server, opts
return nil, nil, err return nil, nil, err
} }
path := fmt.Sprintf("/servers/%d/actions/change_protection", image.ID) path := fmt.Sprintf("/servers/%d/actions/change_protection", server.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData)) req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -786,3 +819,102 @@ func (c *ServerClient) ChangeProtection(ctx context.Context, image *Server, opts
} }
return ActionFromSchema(respBody.Action), resp, err return ActionFromSchema(respBody.Action), resp, err
} }
// ServerAttachToNetworkOpts specifies options for attaching a server to a network.
type ServerAttachToNetworkOpts struct {
Network *Network
IP net.IP
AliasIPs []net.IP
}
// AttachToNetwork attaches a server to a network.
func (c *ServerClient) AttachToNetwork(ctx context.Context, server *Server, opts ServerAttachToNetworkOpts) (*Action, *Response, error) {
reqBody := schema.ServerActionAttachToNetworkRequest{
Network: opts.Network.ID,
}
if opts.IP != nil {
reqBody.IP = String(opts.IP.String())
}
for _, aliasIP := range opts.AliasIPs {
reqBody.AliasIPs = append(reqBody.AliasIPs, String(aliasIP.String()))
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/servers/%d/actions/attach_to_network", server.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.ServerActionAttachToNetworkResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, err
}
// ServerDetachFromNetworkOpts specifies options for detaching a server from a network.
type ServerDetachFromNetworkOpts struct {
Network *Network
}
// DetachFromNetwork detaches a server from a network.
func (c *ServerClient) DetachFromNetwork(ctx context.Context, server *Server, opts ServerDetachFromNetworkOpts) (*Action, *Response, error) {
reqBody := schema.ServerActionDetachFromNetworkRequest{
Network: opts.Network.ID,
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/servers/%d/actions/detach_from_network", server.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.ServerActionDetachFromNetworkResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, err
}
// ServerChangeAliasIPsOpts specifies options for changing the alias ips of an already attached network.
type ServerChangeAliasIPsOpts struct {
Network *Network
AliasIPs []net.IP
}
// ChangeAliasIPs changes a server's alias IPs in a network.
func (c *ServerClient) ChangeAliasIPs(ctx context.Context, server *Server, opts ServerChangeAliasIPsOpts) (*Action, *Response, error) {
reqBody := schema.ServerActionChangeAliasIPsRequest{
Network: opts.Network.ID,
AliasIPs: []string{},
}
for _, aliasIP := range opts.AliasIPs {
reqBody.AliasIPs = append(reqBody.AliasIPs, aliasIP.String())
}
reqBodyData, err := json.Marshal(reqBody)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("/servers/%d/actions/change_alias_ips", server.ID)
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
if err != nil {
return nil, nil, err
}
respBody := schema.ServerActionDetachFromNetworkResponse{}
resp, err := c.client.Do(req, &respBody)
if err != nil {
return nil, resp, err
}
return ActionFromSchema(respBody.Action), resp, err
}

View File

@ -69,22 +69,11 @@ func (c *ServerTypeClient) GetByID(ctx context.Context, id int) (*ServerType, *R
// GetByName retrieves a server type by its name. If the server type does not exist, nil is returned. // GetByName retrieves a server type by its name. If the server type does not exist, nil is returned.
func (c *ServerTypeClient) GetByName(ctx context.Context, name string) (*ServerType, *Response, error) { func (c *ServerTypeClient) GetByName(ctx context.Context, name string) (*ServerType, *Response, error) {
path := "/server_types?name=" + url.QueryEscape(name) serverTypes, response, err := c.List(ctx, ServerTypeListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(serverTypes) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return serverTypes[0], response, err
var body schema.ServerTypeListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.ServerTypes) == 0 {
return nil, resp, nil
}
return ServerTypeFromSchema(body.ServerTypes[0]), resp, nil
} }
// Get retrieves a server type by its ID if the input can be parsed as an integer, otherwise it // Get retrieves a server type by its ID if the input can be parsed as an integer, otherwise it
@ -99,11 +88,20 @@ func (c *ServerTypeClient) Get(ctx context.Context, idOrName string) (*ServerTyp
// ServerTypeListOpts specifies options for listing server types. // ServerTypeListOpts specifies options for listing server types.
type ServerTypeListOpts struct { type ServerTypeListOpts struct {
ListOpts ListOpts
Name string
}
func (l ServerTypeListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
return vals
} }
// List returns a list of server types for a specific page. // List returns a list of server types for a specific page.
func (c *ServerTypeClient) List(ctx context.Context, opts ServerTypeListOpts) ([]*ServerType, *Response, error) { func (c *ServerTypeClient) List(ctx context.Context, opts ServerTypeListOpts) ([]*ServerType, *Response, error) {
path := "/server_types?" + valuesForListOpts(opts.ListOpts).Encode() path := "/server_types?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -46,42 +46,20 @@ func (c *SSHKeyClient) GetByID(ctx context.Context, id int) (*SSHKey, *Response,
// GetByName retrieves a SSH key by its name. If the SSH key does not exist, nil is returned. // GetByName retrieves a SSH key by its name. If the SSH key does not exist, nil is returned.
func (c *SSHKeyClient) GetByName(ctx context.Context, name string) (*SSHKey, *Response, error) { func (c *SSHKeyClient) GetByName(ctx context.Context, name string) (*SSHKey, *Response, error) {
path := "/ssh_keys?name=" + url.QueryEscape(name) sshKeys, response, err := c.List(ctx, SSHKeyListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(sshKeys) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return sshKeys[0], response, err
var body schema.SSHKeyListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.SSHKeys) == 0 {
return nil, resp, nil
}
return SSHKeyFromSchema(body.SSHKeys[0]), resp, nil
} }
// GetByFingerprint retreives a SSH key by its fingerprint. If the SSH key does not exist, nil is returned. // GetByFingerprint retreives a SSH key by its fingerprint. If the SSH key does not exist, nil is returned.
func (c *SSHKeyClient) GetByFingerprint(ctx context.Context, fingerprint string) (*SSHKey, *Response, error) { func (c *SSHKeyClient) GetByFingerprint(ctx context.Context, fingerprint string) (*SSHKey, *Response, error) {
path := "/ssh_keys?fingerprint=" + url.QueryEscape(fingerprint) sshKeys, response, err := c.List(ctx, SSHKeyListOpts{Fingerprint: fingerprint})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(sshKeys) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return sshKeys[0], response, err
var body schema.SSHKeyListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.SSHKeys) == 0 {
return nil, resp, nil
}
return SSHKeyFromSchema(body.SSHKeys[0]), resp, nil
} }
// Get retrieves a SSH key by its ID if the input can be parsed as an integer, otherwise it // Get retrieves a SSH key by its ID if the input can be parsed as an integer, otherwise it
@ -96,11 +74,24 @@ func (c *SSHKeyClient) Get(ctx context.Context, idOrName string) (*SSHKey, *Resp
// SSHKeyListOpts specifies options for listing SSH keys. // SSHKeyListOpts specifies options for listing SSH keys.
type SSHKeyListOpts struct { type SSHKeyListOpts struct {
ListOpts ListOpts
Name string
Fingerprint string
}
func (l SSHKeyListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
if l.Fingerprint != "" {
vals.Add("fingerprint", l.Fingerprint)
}
return vals
} }
// List returns a list of SSH keys for a specific page. // List returns a list of SSH keys for a specific page.
func (c *SSHKeyClient) List(ctx context.Context, opts SSHKeyListOpts) ([]*SSHKey, *Response, error) { func (c *SSHKeyClient) List(ctx context.Context, opts SSHKeyListOpts) ([]*SSHKey, *Response, error) {
path := "/ssh_keys?" + valuesForListOpts(opts.ListOpts).Encode() path := "/ssh_keys?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -120,7 +111,7 @@ func (c *SSHKeyClient) List(ctx context.Context, opts SSHKeyListOpts) ([]*SSHKey
// All returns all SSH keys. // All returns all SSH keys.
func (c *SSHKeyClient) All(ctx context.Context) ([]*SSHKey, error) { func (c *SSHKeyClient) All(ctx context.Context) ([]*SSHKey, error) {
return c.AllWithOpts(ctx, SSHKeyListOpts{ListOpts{PerPage: 50}}) return c.AllWithOpts(ctx, SSHKeyListOpts{ListOpts: ListOpts{PerPage: 50}})
} }
// AllWithOpts returns all SSH keys with the given options. // AllWithOpts returns all SSH keys with the given options.

View File

@ -36,6 +36,17 @@ type VolumeClient struct {
client *Client client *Client
} }
// VolumeStatus specifies a volume's status.
type VolumeStatus string
const (
// VolumeStatusCreating is the status when a volume is being created.
VolumeStatusCreating VolumeStatus = "creating"
// VolumeStatusAvailable is the status when a volume is available.
VolumeStatusAvailable VolumeStatus = "available"
)
// GetByID retrieves a volume by its ID. If the volume does not exist, nil is returned. // GetByID retrieves a volume by its ID. If the volume does not exist, nil is returned.
func (c *VolumeClient) GetByID(ctx context.Context, id int) (*Volume, *Response, error) { func (c *VolumeClient) GetByID(ctx context.Context, id int) (*Volume, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/volumes/%d", id), nil) req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/volumes/%d", id), nil)
@ -56,22 +67,11 @@ func (c *VolumeClient) GetByID(ctx context.Context, id int) (*Volume, *Response,
// GetByName retrieves a volume by its name. If the volume does not exist, nil is returned. // GetByName retrieves a volume by its name. If the volume does not exist, nil is returned.
func (c *VolumeClient) GetByName(ctx context.Context, name string) (*Volume, *Response, error) { func (c *VolumeClient) GetByName(ctx context.Context, name string) (*Volume, *Response, error) {
path := "/volumes?name=" + url.QueryEscape(name) volumes, response, err := c.List(ctx, VolumeListOpts{Name: name})
req, err := c.client.NewRequest(ctx, "GET", path, nil) if len(volumes) == 0 {
if err != nil { return nil, response, err
return nil, nil, err
} }
return volumes[0], response, err
var body schema.VolumeListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
if len(body.Volumes) == 0 {
return nil, resp, nil
}
return VolumeFromSchema(body.Volumes[0]), resp, nil
} }
// Get retrieves a volume by its ID if the input can be parsed as an integer, otherwise it // Get retrieves a volume by its ID if the input can be parsed as an integer, otherwise it
@ -86,11 +86,24 @@ func (c *VolumeClient) Get(ctx context.Context, idOrName string) (*Volume, *Resp
// VolumeListOpts specifies options for listing volumes. // VolumeListOpts specifies options for listing volumes.
type VolumeListOpts struct { type VolumeListOpts struct {
ListOpts ListOpts
Name string
Status []VolumeStatus
}
func (l VolumeListOpts) values() url.Values {
vals := l.ListOpts.values()
if l.Name != "" {
vals.Add("name", l.Name)
}
for _, status := range l.Status {
vals.Add("status", string(status))
}
return vals
} }
// List returns a list of volumes for a specific page. // List returns a list of volumes for a specific page.
func (c *VolumeClient) List(ctx context.Context, opts VolumeListOpts) ([]*Volume, *Response, error) { func (c *VolumeClient) List(ctx context.Context, opts VolumeListOpts) ([]*Volume, *Response, error) {
path := "/volumes?" + valuesForListOpts(opts.ListOpts).Encode() path := "/volumes?" + opts.values().Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil) req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -110,7 +123,7 @@ func (c *VolumeClient) List(ctx context.Context, opts VolumeListOpts) ([]*Volume
// All returns all volumes. // All returns all volumes.
func (c *VolumeClient) All(ctx context.Context) ([]*Volume, error) { func (c *VolumeClient) All(ctx context.Context) ([]*Volume, error) {
return c.AllWithOpts(ctx, VolumeListOpts{ListOpts{PerPage: 50}}) return c.AllWithOpts(ctx, VolumeListOpts{ListOpts: ListOpts{PerPage: 50}})
} }
// AllWithOpts returns all volumes with the given options. // AllWithOpts returns all volumes with the given options.

2
vendor/modules.txt vendored
View File

@ -295,7 +295,7 @@ github.com/hashicorp/vault/helper/compressutil
github.com/hashicorp/vault/helper/strutil github.com/hashicorp/vault/helper/strutil
# github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d # github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
github.com/hashicorp/yamux github.com/hashicorp/yamux
# github.com/hetznercloud/hcloud-go v1.12.0 # github.com/hetznercloud/hcloud-go v1.15.1
github.com/hetznercloud/hcloud-go/hcloud github.com/hetznercloud/hcloud-go/hcloud
github.com/hetznercloud/hcloud-go/hcloud/schema github.com/hetznercloud/hcloud-go/hcloud/schema
# github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 # github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775