From d832cd6f67c989de7a3e8b4d94dbbdb31b936be0 Mon Sep 17 00:00:00 2001 From: Andre Hilsendeger Date: Mon, 5 Aug 2019 10:10:39 +0200 Subject: [PATCH] chore: bump hcloud dependency --- go.mod | 3 +- go.sum | 7 +- .../hetznercloud/hcloud-go/hcloud/action.go | 18 +- .../hetznercloud/hcloud-go/hcloud/client.go | 41 +- .../hcloud-go/hcloud/datacenter.go | 30 +- .../hetznercloud/hcloud-go/hcloud/error.go | 2 + .../hcloud-go/hcloud/floating_ip.go | 9 +- .../hetznercloud/hcloud-go/hcloud/hcloud.go | 2 +- .../hetznercloud/hcloud-go/hcloud/image.go | 48 +- .../hetznercloud/hcloud-go/hcloud/iso.go | 30 +- .../hetznercloud/hcloud-go/hcloud/location.go | 31 +- .../hetznercloud/hcloud-go/hcloud/network.go | 445 ++++++++++++++++++ .../hetznercloud/hcloud-go/hcloud/schema.go | 68 +++ .../hcloud-go/hcloud/schema/floating_ip.go | 3 + .../hcloud-go/hcloud/schema/location.go | 1 + .../hcloud-go/hcloud/schema/network.go | 150 ++++++ .../hcloud-go/hcloud/schema/server.go | 91 +++- .../hetznercloud/hcloud-go/hcloud/server.go | 172 ++++++- .../hcloud-go/hcloud/server_type.go | 30 +- .../hetznercloud/hcloud-go/hcloud/ssh_key.go | 55 +-- .../hetznercloud/hcloud-go/hcloud/volume.go | 47 +- vendor/modules.txt | 2 +- 22 files changed, 1099 insertions(+), 186 deletions(-) create mode 100644 vendor/github.com/hetznercloud/hcloud-go/hcloud/network.go create mode 100644 vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/network.go diff --git a/go.mod b/go.mod index 108ed5344..4dc5d52da 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/hashicorp/serf v0.8.2 // indirect github.com/hashicorp/vault v1.1.0 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/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 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-homedir v1.0.0 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/mapstructure v0.0.0-20180111000720-b4575eea38cc github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557 diff --git a/go.sum b/go.sum index 0d8e97123..1d48c54fa 100644 --- a/go.sum +++ b/go.sum @@ -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.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= 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/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 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/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= 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.12.0/go.mod h1:g5pff0YNAZywQaivY/CmhUYFVp7oP0nu3MiODC2W4Hw= +github.com/hetznercloud/hcloud-go v1.15.1 h1:G8Q+xyAqQ5IUY7yq4HKZgkabFa0S/VXJXq3TGCeT8JM= +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/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 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/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= 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/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/action.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/action.go index a261891e2..7ac042297 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/action.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/action.go @@ -3,6 +3,7 @@ package hcloud import ( "context" "fmt" + "net/url" "time" "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. type ActionListOpts struct { 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. 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) if err != nil { 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) if err != nil { - errCh <- ctx.Err() + errCh <- err return } diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/client.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/client.go index 9a80d1713..01215cd69 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/client.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/client.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "math" "net/http" + "net/http/httputil" "net/url" "strconv" "strings" @@ -55,6 +56,7 @@ type Client struct { applicationName string applicationVersion string userAgent string + debugWriter io.Writer Action ActionClient Datacenter DatacenterClient @@ -62,6 +64,7 @@ type Client struct { Image ImageClient ISO ISOClient Location LocationClient + Network NetworkClient Pricing PricingClient Server ServerClient 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. func NewClient(options ...ClientOption) *Client { client := &Client{ @@ -132,6 +143,7 @@ func NewClient(options ...ClientOption) *Client { client.Image = ImageClient{client: client} client.ISO = ISOClient{client: client} client.Location = LocationClient{client: client} + client.Network = NetworkClient{client: client} client.Pricing = PricingClient{client: client} client.Server = ServerClient{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 } response := &Response{Response: resp} - body, err := ioutil.ReadAll(resp.Body) if err != nil { resp.Body.Close() @@ -176,6 +187,20 @@ func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) { resp.Body.Close() 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 { 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 } -func valuesForListOpts(opts ListOpts) url.Values { +func (l ListOpts) values() url.Values { vals := url.Values{} - if opts.Page > 0 { - vals.Add("page", strconv.Itoa(opts.Page)) + if l.Page > 0 { + vals.Add("page", strconv.Itoa(l.Page)) } - if opts.PerPage > 0 { - vals.Add("per_page", strconv.Itoa(opts.PerPage)) + if l.PerPage > 0 { + vals.Add("per_page", strconv.Itoa(l.PerPage)) } - if len(opts.LabelSelector) > 0 { - vals.Add("label_selector", opts.LabelSelector) + if len(l.LabelSelector) > 0 { + vals.Add("label_selector", l.LabelSelector) } return vals } diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/datacenter.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/datacenter.go index b0717f942..94676214f 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/datacenter.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/datacenter.go @@ -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. func (c *DatacenterClient) GetByName(ctx context.Context, name string) (*Datacenter, *Response, error) { - path := "/datacenters?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + datacenters, response, err := c.List(ctx, DatacenterListOpts{Name: name}) + if len(datacenters) == 0 { + return nil, 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 + return datacenters[0], response, err } // 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. type DatacenterListOpts struct { 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. 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) if err != nil { return nil, nil, err diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/error.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/error.go index 2e9f3b9a8..9eeab771d 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/error.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/error.go @@ -20,6 +20,8 @@ const ( ErrorCodeUniquenessError ErrorCode = "uniqueness_error" // One or more fields must be unique ErrorCodeProtected ErrorCode = "protected" // The actions you are trying is protected 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 diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/floating_ip.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/floating_ip.go index 7fe2c55d0..afeeca939 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/floating_ip.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/floating_ip.go @@ -7,6 +7,8 @@ import ( "errors" "fmt" "net" + "net/url" + "time" "github.com/hetznercloud/hcloud-go/hcloud/schema" ) @@ -15,6 +17,7 @@ import ( type FloatingIP struct { ID int Description string + Created time.Time IP net.IP Network *net.IPNet Type FloatingIPType @@ -74,9 +77,13 @@ type FloatingIPListOpts struct { ListOpts } +func (l FloatingIPListOpts) values() url.Values { + return l.ListOpts.values() +} + // List returns a list of Floating IPs for a specific page. 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) if err != nil { return nil, nil, err diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/hcloud.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/hcloud.go index 47648590f..55ec98444 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/hcloud.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/hcloud.go @@ -2,4 +2,4 @@ package hcloud // Version is the library's version following Semantic Versioning. -const Version = "1.12.0" +const Version = "1.15.1" diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/image.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/image.go index 4ebe4c3f5..caa533fdd 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/image.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/image.go @@ -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. func (c *ImageClient) GetByName(ctx context.Context, name string) (*Image, *Response, error) { - path := "/images?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + images, response, err := c.List(ctx, ImageListOpts{Name: name}) + if len(images) == 0 { + return nil, 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 + return images[0], response, err } // 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. type ImageListOpts struct { 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. 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) if err != nil { return nil, nil, err @@ -145,7 +159,7 @@ func (c *ImageClient) List(ctx context.Context, opts ImageListOpts) ([]*Image, * // All returns all images. 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. diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/iso.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/iso.go index 852e69841..de360f545 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/iso.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/iso.go @@ -60,22 +60,11 @@ func (c *ISOClient) GetByID(ctx context.Context, id int) (*ISO, *Response, error // GetByName retrieves an ISO by its name. func (c *ISOClient) GetByName(ctx context.Context, name string) (*ISO, *Response, error) { - path := "/isos?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + isos, response, err := c.List(ctx, ISOListOpts{Name: name}) + if len(isos) == 0 { + return nil, 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 + return isos[0], response, err } // 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. type ISOListOpts struct { 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. 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) if err != nil { return nil, nil, err diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/location.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/location.go index 5269b7e76..5efb4a8d1 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/location.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/location.go @@ -18,6 +18,7 @@ type Location struct { City string Latitude float64 Longitude float64 + NetworkZone NetworkZone } // 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. func (c *LocationClient) GetByName(ctx context.Context, name string) (*Location, *Response, error) { - path := "/locations?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + locations, response, err := c.List(ctx, LocationListOpts{Name: name}) + if len(locations) == 0 { + return nil, 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 + return locations[0], response, err } // 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. type LocationListOpts struct { 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. 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) if err != nil { return nil, nil, err diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/network.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/network.go new file mode 100644 index 000000000..1f703d5cb --- /dev/null +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/network.go @@ -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 +} diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema.go index 47a194f3a..2b69605b8 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema.go @@ -50,6 +50,7 @@ func FloatingIPFromSchema(s schema.FloatingIP) *FloatingIP { ID: s.ID, Type: FloatingIPType(s.Type), HomeLocation: LocationFromSchema(s.HomeLocation), + Created: s.Created, Blocked: s.Blocked, Protection: FloatingIPProtection{ Delete: s.Protection.Delete, @@ -98,6 +99,7 @@ func LocationFromSchema(s schema.Location) *Location { City: s.City, Latitude: s.Latitude, Longitude: s.Longitude, + NetworkZone: NetworkZone(s.NetworkZone), } } @@ -162,6 +164,9 @@ func ServerFromSchema(s schema.Server) *Server { for _, id := range s.Volumes { server.Volumes = append(server.Volumes, &Volume{ID: id}) } + for _, privNet := range s.PrivateNet { + server.PrivateNet = append(server.PrivateNet, ServerPrivateNetFromSchema(privNet)) + } return server } @@ -202,6 +207,19 @@ func ServerPublicNetIPv6FromSchema(s schema.ServerPublicNetIPv6) ServerPublicNet 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. func ServerTypeFromSchema(s schema.ServerType) *ServerType { st := &ServerType{ @@ -311,6 +329,56 @@ func VolumeFromSchema(s schema.Volume) *Volume { 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. func PaginationFromSchema(s schema.MetaPagination) Pagination { return Pagination{ diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/floating_ip.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/floating_ip.go index 95ae2054b..4c56d874f 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/floating_ip.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/floating_ip.go @@ -1,9 +1,12 @@ package schema +import "time" + // FloatingIP defines the schema of a Floating IP. type FloatingIP struct { ID int `json:"id"` Description *string `json:"description"` + Created time.Time `json:"created"` IP string `json:"ip"` Type string `json:"type"` Server *int `json:"server"` diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/location.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/location.go index 1f030a9db..3dd58ad5e 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/location.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/location.go @@ -9,6 +9,7 @@ type Location struct { City string `json:"city"` Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` + NetworkZone string `json:"network_zone"` } // LocationGetResponse defines the schema of the response when retrieving a single location. diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/network.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/network.go new file mode 100644 index 000000000..e1c97e426 --- /dev/null +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/network.go @@ -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"` +} diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server.go index e680ae75e..32cd00f90 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server.go @@ -4,24 +4,25 @@ import "time" // Server defines the schema of a server. type Server struct { - ID int `json:"id"` - Name string `json:"name"` - Status string `json:"status"` - Created time.Time `json:"created"` - PublicNet ServerPublicNet `json:"public_net"` - ServerType ServerType `json:"server_type"` - IncludedTraffic uint64 `json:"included_traffic"` - OutgoingTraffic *uint64 `json:"outgoing_traffic"` - IngoingTraffic *uint64 `json:"ingoing_traffic"` - BackupWindow *string `json:"backup_window"` - RescueEnabled bool `json:"rescue_enabled"` - ISO *ISO `json:"iso"` - Locked bool `json:"locked"` - Datacenter Datacenter `json:"datacenter"` - Image *Image `json:"image"` - Protection ServerProtection `json:"protection"` - Labels map[string]string `json:"labels"` - Volumes []int `json:"volumes"` + ID int `json:"id"` + Name string `json:"name"` + Status string `json:"status"` + Created time.Time `json:"created"` + PublicNet ServerPublicNet `json:"public_net"` + PrivateNet []ServerPrivateNet `json:"private_net"` + ServerType ServerType `json:"server_type"` + IncludedTraffic uint64 `json:"included_traffic"` + OutgoingTraffic *uint64 `json:"outgoing_traffic"` + IngoingTraffic *uint64 `json:"ingoing_traffic"` + BackupWindow *string `json:"backup_window"` + RescueEnabled bool `json:"rescue_enabled"` + ISO *ISO `json:"iso"` + Locked bool `json:"locked"` + Datacenter Datacenter `json:"datacenter"` + Image *Image `json:"image"` + Protection ServerProtection `json:"protection"` + Labels map[string]string `json:"labels"` + Volumes []int `json:"volumes"` } // ServerProtection defines the schema of a server's resource protection. @@ -61,6 +62,14 @@ type ServerPublicNetIPv6DNSPtr struct { 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 // retrieving a single server. type ServerGetResponse struct { @@ -87,6 +96,7 @@ type ServerCreateRequest struct { Labels *map[string]string `json:"labels,omitempty"` Automount *bool `json:"automount,omitempty"` Volumes []int `json:"volumes,omitempty"` + Networks []int `json:"networks,omitempty"` } // ServerCreateResponse defines the schema of the response when @@ -291,13 +301,54 @@ type ServerActionChangeDNSPtrResponse struct { 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 { Rebuild *bool `json:"rebuild,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 { 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"` +} diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/server.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/server.go index c301d5baf..9653a2539 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/server.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/server.go @@ -21,6 +21,7 @@ type Server struct { Status ServerStatus Created time.Time PublicNet ServerPublicNet + PrivateNet []ServerPrivateNet ServerType *ServerType Datacenter *Datacenter IncludedTraffic uint64 @@ -53,6 +54,24 @@ const ( // ServerStatusRunning is the status when a server is 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. @@ -77,6 +96,14 @@ type ServerPublicNetIPv6 struct { 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. func (s *ServerPublicNetIPv6) DNSPtrForIP(ip net.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 } -// 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) { - path := "/servers?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + servers, response, err := c.List(ctx, ServerListOpts{Name: name}) + if len(servers) == 0 { + return nil, 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 + return servers[0], response, err } // 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. type ServerListOpts struct { 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. 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) if err != nil { return nil, nil, err @@ -171,7 +200,7 @@ func (c *ServerClient) List(ctx context.Context, opts ServerListOpts) ([]*Server // All returns all servers. 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. @@ -207,6 +236,7 @@ type ServerCreateOpts struct { Labels map[string]string Automount *bool Volumes []*Volume + Networks []*Network } // 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 { 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.ID != 0 { @@ -763,7 +796,7 @@ type ServerChangeProtectionOpts struct { } // 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{ Rebuild: opts.Rebuild, Delete: opts.Delete, @@ -773,7 +806,7 @@ func (c *ServerClient) ChangeProtection(ctx context.Context, image *Server, opts 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)) if err != nil { return nil, nil, err @@ -786,3 +819,102 @@ func (c *ServerClient) ChangeProtection(ctx context.Context, image *Server, opts } 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 +} diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/server_type.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/server_type.go index 6f12bd680..606dd5d7f 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/server_type.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/server_type.go @@ -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. func (c *ServerTypeClient) GetByName(ctx context.Context, name string) (*ServerType, *Response, error) { - path := "/server_types?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + serverTypes, response, err := c.List(ctx, ServerTypeListOpts{Name: name}) + if len(serverTypes) == 0 { + return nil, 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 + return serverTypes[0], response, err } // 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. type ServerTypeListOpts struct { 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. 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) if err != nil { return nil, nil, err diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/ssh_key.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/ssh_key.go index 7db8f0a9e..78e74c48d 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/ssh_key.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/ssh_key.go @@ -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. func (c *SSHKeyClient) GetByName(ctx context.Context, name string) (*SSHKey, *Response, error) { - path := "/ssh_keys?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + sshKeys, response, err := c.List(ctx, SSHKeyListOpts{Name: name}) + if len(sshKeys) == 0 { + return nil, 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 + return sshKeys[0], response, err } // 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) { - path := "/ssh_keys?fingerprint=" + url.QueryEscape(fingerprint) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + sshKeys, response, err := c.List(ctx, SSHKeyListOpts{Fingerprint: fingerprint}) + if len(sshKeys) == 0 { + return nil, 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 + return sshKeys[0], response, err } // 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. type SSHKeyListOpts struct { 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. 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) if err != nil { return nil, nil, err @@ -120,7 +111,7 @@ func (c *SSHKeyClient) List(ctx context.Context, opts SSHKeyListOpts) ([]*SSHKey // All returns all SSH keys. 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. diff --git a/vendor/github.com/hetznercloud/hcloud-go/hcloud/volume.go b/vendor/github.com/hetznercloud/hcloud-go/hcloud/volume.go index a9d2ea3d5..5c837d166 100644 --- a/vendor/github.com/hetznercloud/hcloud-go/hcloud/volume.go +++ b/vendor/github.com/hetznercloud/hcloud-go/hcloud/volume.go @@ -36,6 +36,17 @@ type VolumeClient struct { 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. 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) @@ -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. func (c *VolumeClient) GetByName(ctx context.Context, name string) (*Volume, *Response, error) { - path := "/volumes?name=" + url.QueryEscape(name) - req, err := c.client.NewRequest(ctx, "GET", path, nil) - if err != nil { - return nil, nil, err + volumes, response, err := c.List(ctx, VolumeListOpts{Name: name}) + if len(volumes) == 0 { + return nil, 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 + return volumes[0], response, err } // 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. type VolumeListOpts struct { 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. 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) if err != nil { return nil, nil, err @@ -110,7 +123,7 @@ func (c *VolumeClient) List(ctx context.Context, opts VolumeListOpts) ([]*Volume // All returns all volumes. 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. diff --git a/vendor/modules.txt b/vendor/modules.txt index 05c6b7d77..a15d8dbfa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -295,7 +295,7 @@ github.com/hashicorp/vault/helper/compressutil github.com/hashicorp/vault/helper/strutil # github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d 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/schema # github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775