mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24:05 -08:00
Add hetzner service discovery (#7822)
Signed-off-by: Lukas Kämmerling <lukas.kaemmerling@hetzner-cloud.de>
This commit is contained in:
parent
a1601274ba
commit
b6955bf1ca
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/prometheus/prometheus/discovery/dockerswarm"
|
||||
"github.com/prometheus/prometheus/discovery/ec2"
|
||||
"github.com/prometheus/prometheus/discovery/file"
|
||||
"github.com/prometheus/prometheus/discovery/hetzner"
|
||||
"github.com/prometheus/prometheus/discovery/kubernetes"
|
||||
"github.com/prometheus/prometheus/discovery/marathon"
|
||||
"github.com/prometheus/prometheus/discovery/openstack"
|
||||
|
@ -648,6 +649,34 @@ var expectedConf = &Config{
|
|||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
JobName: "hetzner",
|
||||
HonorTimestamps: true,
|
||||
ScrapeInterval: model.Duration(15 * time.Second),
|
||||
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
|
||||
|
||||
MetricsPath: DefaultScrapeConfig.MetricsPath,
|
||||
Scheme: DefaultScrapeConfig.Scheme,
|
||||
|
||||
ServiceDiscoveryConfigs: discovery.Configs{
|
||||
&hetzner.SDConfig{
|
||||
HTTPClientConfig: config.HTTPClientConfig{
|
||||
BearerToken: "abcdef",
|
||||
},
|
||||
Port: 80,
|
||||
RefreshInterval: model.Duration(60 * time.Second),
|
||||
Role: "hcloud",
|
||||
},
|
||||
&hetzner.SDConfig{
|
||||
HTTPClientConfig: config.HTTPClientConfig{
|
||||
BasicAuth: &config.BasicAuth{Username: "abcdef", Password: "abcdef"},
|
||||
},
|
||||
Port: 80,
|
||||
RefreshInterval: model.Duration(60 * time.Second),
|
||||
Role: "robot",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
AlertingConfig: AlertingConfig{
|
||||
AlertmanagerConfigs: []*AlertmanagerConfig{
|
||||
|
@ -717,7 +746,7 @@ func TestElideSecrets(t *testing.T) {
|
|||
yamlConfig := string(config)
|
||||
|
||||
matches := secretRe.FindAllStringIndex(yamlConfig, -1)
|
||||
testutil.Assert(t, len(matches) == 8, "wrong number of secret matches found")
|
||||
testutil.Assert(t, len(matches) == 10, "wrong number of secret matches found")
|
||||
testutil.Assert(t, !strings.Contains(yamlConfig, "mysecret"),
|
||||
"yaml marshal reveals authentication credentials.")
|
||||
}
|
||||
|
@ -963,6 +992,10 @@ var expectedErrors = []struct {
|
|||
filename: "empty_static_config.bad.yml",
|
||||
errMsg: "empty or null section in static_configs",
|
||||
},
|
||||
{
|
||||
filename: "hetzner_role.bad.yml",
|
||||
errMsg: "unknown role",
|
||||
},
|
||||
}
|
||||
|
||||
func TestBadConfigs(t *testing.T) {
|
||||
|
|
9
config/testdata/conf.good.yml
vendored
9
config/testdata/conf.good.yml
vendored
|
@ -279,6 +279,15 @@ scrape_configs:
|
|||
cert_file: valid_cert_file
|
||||
key_file: valid_key_file
|
||||
|
||||
- job_name: hetzner
|
||||
hetzner_sd_configs:
|
||||
- role: hcloud
|
||||
bearer_token: abcdef
|
||||
- role: robot
|
||||
basic_auth:
|
||||
username: abcdef
|
||||
password: abcdef
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- scheme: https
|
||||
|
|
4
config/testdata/hetzner_role.bad.yml
vendored
Normal file
4
config/testdata/hetzner_role.bad.yml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
scrape_configs:
|
||||
- hetzner_sd_configs:
|
||||
- role: invalid
|
||||
|
132
discovery/hetzner/hcloud.go
Normal file
132
discovery/hetzner/hcloud.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hetzner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
"github.com/prometheus/common/config"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/common/version"
|
||||
"github.com/prometheus/prometheus/discovery/refresh"
|
||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
"github.com/prometheus/prometheus/util/strutil"
|
||||
)
|
||||
|
||||
const (
|
||||
hetznerHcloudLabelPrefix = hetznerLabelPrefix + "hcloud_"
|
||||
hetznerLabelHcloudImageName = hetznerHcloudLabelPrefix + "image_name"
|
||||
hetznerLabelHcloudImageDescription = hetznerHcloudLabelPrefix + "image_description"
|
||||
hetznerLabelHcloudImageOSVersion = hetznerHcloudLabelPrefix + "image_os_version"
|
||||
hetznerLabelHcloudImageOSFlavor = hetznerHcloudLabelPrefix + "image_os_flavor"
|
||||
hetznerLabelHcloudPrivateIPv4 = hetznerHcloudLabelPrefix + "private_ipv4_"
|
||||
hetznerLabelHcloudDatacenterLocation = hetznerHcloudLabelPrefix + "datacenter_location"
|
||||
hetznerLabelHcloudDatacenterLocationNetworkZone = hetznerHcloudLabelPrefix + "datacenter_location_network_zone"
|
||||
hetznerLabelHcloudCPUCores = hetznerHcloudLabelPrefix + "cpu_cores"
|
||||
hetznerLabelHcloudCPUType = hetznerHcloudLabelPrefix + "cpu_type"
|
||||
hetznerLabelHcloudMemoryGB = hetznerHcloudLabelPrefix + "memory_size_gb"
|
||||
hetznerLabelHcloudDiskGB = hetznerHcloudLabelPrefix + "disk_size_gb"
|
||||
hetznerLabelHcloudType = hetznerHcloudLabelPrefix + "server_type"
|
||||
hetznerLabelHcloudLabel = hetznerHcloudLabelPrefix + "label_"
|
||||
)
|
||||
|
||||
// Discovery periodically performs Hetzner Cloud requests. It implements
|
||||
// the Discoverer interface.
|
||||
type hcloudDiscovery struct {
|
||||
*refresh.Discovery
|
||||
client *hcloud.Client
|
||||
port int
|
||||
}
|
||||
|
||||
// newHcloudDiscovery returns a new hcloudDiscovery which periodically refreshes its targets.
|
||||
func newHcloudDiscovery(conf *SDConfig, logger log.Logger) (*hcloudDiscovery, error) {
|
||||
d := &hcloudDiscovery{
|
||||
port: conf.Port,
|
||||
}
|
||||
|
||||
rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "hetzner_sd", false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.client = hcloud.NewClient(
|
||||
hcloud.WithApplication("Prometheus", version.Version),
|
||||
hcloud.WithHTTPClient(&http.Client{
|
||||
Transport: rt,
|
||||
Timeout: time.Duration(conf.RefreshInterval),
|
||||
}),
|
||||
hcloud.WithEndpoint(conf.hcloudEndpoint),
|
||||
)
|
||||
return d, nil
|
||||
}
|
||||
func (d *hcloudDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
|
||||
servers, err := d.client.Server.All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
networks, err := d.client.Network.All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
targets := make([]model.LabelSet, len(servers))
|
||||
for i, server := range servers {
|
||||
labels := model.LabelSet{
|
||||
hetznerLabelRole: model.LabelValue(hetznerRoleHcloud),
|
||||
hetznerLabelServerID: model.LabelValue(fmt.Sprintf("%d", server.ID)),
|
||||
hetznerLabelServerName: model.LabelValue(server.Name),
|
||||
hetznerLabelDatacenter: model.LabelValue(server.Datacenter.Name),
|
||||
hetznerLabelPublicIPv4: model.LabelValue(server.PublicNet.IPv4.IP.String()),
|
||||
hetznerLabelPublicIPv6Network: model.LabelValue(server.PublicNet.IPv6.Network.String()),
|
||||
hetznerLabelServerStatus: model.LabelValue(server.Status),
|
||||
|
||||
hetznerLabelHcloudDatacenterLocation: model.LabelValue(server.Datacenter.Location.Name),
|
||||
hetznerLabelHcloudDatacenterLocationNetworkZone: model.LabelValue(server.Datacenter.Location.NetworkZone),
|
||||
hetznerLabelHcloudType: model.LabelValue(server.ServerType.Name),
|
||||
hetznerLabelHcloudCPUCores: model.LabelValue(fmt.Sprintf("%d", server.ServerType.Cores)),
|
||||
hetznerLabelHcloudCPUType: model.LabelValue(server.ServerType.CPUType),
|
||||
hetznerLabelHcloudMemoryGB: model.LabelValue(fmt.Sprintf("%d", int(server.ServerType.Memory))),
|
||||
hetznerLabelHcloudDiskGB: model.LabelValue(fmt.Sprintf("%d", server.ServerType.Disk)),
|
||||
|
||||
model.AddressLabel: model.LabelValue(net.JoinHostPort(server.PublicNet.IPv4.IP.String(), strconv.FormatUint(uint64(d.port), 10))),
|
||||
}
|
||||
|
||||
if server.Image != nil {
|
||||
labels[hetznerLabelHcloudImageName] = model.LabelValue(server.Image.Name)
|
||||
labels[hetznerLabelHcloudImageDescription] = model.LabelValue(server.Image.Description)
|
||||
labels[hetznerLabelHcloudImageOSVersion] = model.LabelValue(server.Image.OSVersion)
|
||||
labels[hetznerLabelHcloudImageOSFlavor] = model.LabelValue(server.Image.OSFlavor)
|
||||
}
|
||||
|
||||
for _, privateNet := range server.PrivateNet {
|
||||
for _, network := range networks {
|
||||
if privateNet.Network.ID == network.ID {
|
||||
networkLabel := model.LabelName(hetznerLabelHcloudPrivateIPv4 + strutil.SanitizeLabelName(network.Name))
|
||||
labels[networkLabel] = model.LabelValue(privateNet.IP.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
for labelKey, labelValue := range server.Labels {
|
||||
label := model.LabelName(hetznerLabelHcloudLabel + strutil.SanitizeLabelName(labelKey))
|
||||
labels[label] = model.LabelValue(labelValue)
|
||||
}
|
||||
targets[i] = labels
|
||||
}
|
||||
return []*targetgroup.Group{{Source: "hetzner", Targets: targets}}, nil
|
||||
}
|
124
discovery/hetzner/hcloud_test.go
Normal file
124
discovery/hetzner/hcloud_test.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hetzner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type hcloudSDTestSuite struct {
|
||||
Mock *SDMock
|
||||
}
|
||||
|
||||
func (s *hcloudSDTestSuite) SetupTest(t *testing.T) {
|
||||
s.Mock = NewSDMock(t)
|
||||
s.Mock.Setup()
|
||||
|
||||
s.Mock.HandleHcloudServers()
|
||||
s.Mock.HandleHcloudNetworks()
|
||||
}
|
||||
|
||||
func TestHCloudSDRefresh(t *testing.T) {
|
||||
suite := &hcloudSDTestSuite{}
|
||||
suite.SetupTest(t)
|
||||
|
||||
cfg := DefaultSDConfig
|
||||
cfg.HTTPClientConfig.BearerToken = hcloudTestToken
|
||||
cfg.hcloudEndpoint = suite.Mock.Endpoint()
|
||||
|
||||
d, err := newHcloudDiscovery(&cfg, log.NewNopLogger())
|
||||
testutil.Ok(t, err)
|
||||
|
||||
targetGroups, err := d.refresh(context.Background())
|
||||
testutil.Ok(t, err)
|
||||
testutil.Equals(t, 1, len(targetGroups))
|
||||
|
||||
targetGroup := targetGroups[0]
|
||||
testutil.Assert(t, targetGroup != nil, "targetGroup should not be nil")
|
||||
testutil.Assert(t, targetGroup.Targets != nil, "targetGroup.targets should not be nil")
|
||||
testutil.Equals(t, 3, len(targetGroup.Targets))
|
||||
|
||||
for i, labelSet := range []model.LabelSet{
|
||||
{
|
||||
"__address__": model.LabelValue("1.2.3.4:80"),
|
||||
"__meta_hetzner_role": model.LabelValue("hcloud"),
|
||||
"__meta_hetzner_server_id": model.LabelValue("42"),
|
||||
"__meta_hetzner_server_name": model.LabelValue("my-server"),
|
||||
"__meta_hetzner_server_status": model.LabelValue("running"),
|
||||
"__meta_hetzner_public_ipv4": model.LabelValue("1.2.3.4"),
|
||||
"__meta_hetzner_public_ipv6_network": model.LabelValue("2001:db8::/64"),
|
||||
"__meta_hetzner_datacenter": model.LabelValue("fsn1-dc8"),
|
||||
"__meta_hetzner_hcloud_image_name": model.LabelValue("ubuntu-20.04"),
|
||||
"__meta_hetzner_hcloud_image_description": model.LabelValue("Ubuntu 20.04 Standard 64 bit"),
|
||||
"__meta_hetzner_hcloud_image_os_flavor": model.LabelValue("ubuntu"),
|
||||
"__meta_hetzner_hcloud_image_os_version": model.LabelValue("20.04"),
|
||||
"__meta_hetzner_hcloud_datacenter_location": model.LabelValue("fsn1"),
|
||||
"__meta_hetzner_hcloud_datacenter_location_network_zone": model.LabelValue("eu-central"),
|
||||
"__meta_hetzner_hcloud_cpu_cores": model.LabelValue("1"),
|
||||
"__meta_hetzner_hcloud_cpu_type": model.LabelValue("shared"),
|
||||
"__meta_hetzner_hcloud_memory_size_gb": model.LabelValue("1"),
|
||||
"__meta_hetzner_hcloud_disk_size_gb": model.LabelValue("25"),
|
||||
"__meta_hetzner_hcloud_server_type": model.LabelValue("cx11"),
|
||||
"__meta_hetzner_hcloud_private_ipv4_mynet": model.LabelValue("10.0.0.2"),
|
||||
"__meta_hetzner_hcloud_label_my_key": model.LabelValue("my-value"),
|
||||
},
|
||||
{
|
||||
"__address__": model.LabelValue("1.2.3.5:80"),
|
||||
"__meta_hetzner_role": model.LabelValue("hcloud"),
|
||||
"__meta_hetzner_server_id": model.LabelValue("44"),
|
||||
"__meta_hetzner_server_name": model.LabelValue("another-server"),
|
||||
"__meta_hetzner_server_status": model.LabelValue("stopped"),
|
||||
"__meta_hetzner_datacenter": model.LabelValue("fsn1-dc14"),
|
||||
"__meta_hetzner_public_ipv4": model.LabelValue("1.2.3.5"),
|
||||
"__meta_hetzner_public_ipv6_network": model.LabelValue("2001:db9::/64"),
|
||||
"__meta_hetzner_hcloud_image_name": model.LabelValue("ubuntu-20.04"),
|
||||
"__meta_hetzner_hcloud_image_description": model.LabelValue("Ubuntu 20.04 Standard 64 bit"),
|
||||
"__meta_hetzner_hcloud_image_os_flavor": model.LabelValue("ubuntu"),
|
||||
"__meta_hetzner_hcloud_image_os_version": model.LabelValue("20.04"),
|
||||
"__meta_hetzner_hcloud_datacenter_location": model.LabelValue("fsn1"),
|
||||
"__meta_hetzner_hcloud_datacenter_location_network_zone": model.LabelValue("eu-central"),
|
||||
"__meta_hetzner_hcloud_cpu_cores": model.LabelValue("2"),
|
||||
"__meta_hetzner_hcloud_cpu_type": model.LabelValue("shared"),
|
||||
"__meta_hetzner_hcloud_memory_size_gb": model.LabelValue("1"),
|
||||
"__meta_hetzner_hcloud_disk_size_gb": model.LabelValue("50"),
|
||||
"__meta_hetzner_hcloud_server_type": model.LabelValue("cpx11"),
|
||||
},
|
||||
{
|
||||
"__address__": model.LabelValue("1.2.3.6:80"),
|
||||
"__meta_hetzner_role": model.LabelValue("hcloud"),
|
||||
"__meta_hetzner_server_id": model.LabelValue("36"),
|
||||
"__meta_hetzner_server_name": model.LabelValue("deleted-image-server"),
|
||||
"__meta_hetzner_server_status": model.LabelValue("stopped"),
|
||||
"__meta_hetzner_datacenter": model.LabelValue("fsn1-dc14"),
|
||||
"__meta_hetzner_public_ipv4": model.LabelValue("1.2.3.6"),
|
||||
"__meta_hetzner_public_ipv6_network": model.LabelValue("2001:db7::/64"),
|
||||
"__meta_hetzner_hcloud_datacenter_location": model.LabelValue("fsn1"),
|
||||
"__meta_hetzner_hcloud_datacenter_location_network_zone": model.LabelValue("eu-central"),
|
||||
"__meta_hetzner_hcloud_cpu_cores": model.LabelValue("2"),
|
||||
"__meta_hetzner_hcloud_cpu_type": model.LabelValue("shared"),
|
||||
"__meta_hetzner_hcloud_memory_size_gb": model.LabelValue("1"),
|
||||
"__meta_hetzner_hcloud_disk_size_gb": model.LabelValue("50"),
|
||||
"__meta_hetzner_hcloud_server_type": model.LabelValue("cpx11"),
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
|
||||
testutil.Equals(t, labelSet, targetGroup.Targets[i])
|
||||
})
|
||||
}
|
||||
}
|
150
discovery/hetzner/hetzner.go
Normal file
150
discovery/hetzner/hetzner.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hetzner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/pkg/errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
||||
"github.com/prometheus/common/config"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/discovery"
|
||||
"github.com/prometheus/prometheus/discovery/refresh"
|
||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
hetznerLabelPrefix = model.MetaLabelPrefix + "hetzner_"
|
||||
hetznerLabelRole = hetznerLabelPrefix + "role"
|
||||
hetznerLabelServerID = hetznerLabelPrefix + "server_id"
|
||||
hetznerLabelServerName = hetznerLabelPrefix + "server_name"
|
||||
hetznerLabelServerStatus = hetznerLabelPrefix + "server_status"
|
||||
hetznerLabelDatacenter = hetznerLabelPrefix + "datacenter"
|
||||
hetznerLabelPublicIPv4 = hetznerLabelPrefix + "public_ipv4"
|
||||
hetznerLabelPublicIPv6Network = hetznerLabelPrefix + "public_ipv6_network"
|
||||
)
|
||||
|
||||
// DefaultSDConfig is the default Hetzner SD configuration.
|
||||
var DefaultSDConfig = SDConfig{
|
||||
Port: 80,
|
||||
RefreshInterval: model.Duration(60 * time.Second),
|
||||
}
|
||||
|
||||
func init() {
|
||||
discovery.RegisterConfig(&SDConfig{})
|
||||
}
|
||||
|
||||
// SDConfig is the configuration for Hetzner based service discovery.
|
||||
type SDConfig struct {
|
||||
HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`
|
||||
|
||||
RefreshInterval model.Duration `yaml:"refresh_interval"`
|
||||
Port int `yaml:"port"`
|
||||
Role role `yaml:"role"`
|
||||
hcloudEndpoint string // For tests only.
|
||||
robotEndpoint string // For tests only.
|
||||
}
|
||||
|
||||
// Name returns the name of the Config.
|
||||
func (*SDConfig) Name() string { return "hetzner" }
|
||||
|
||||
// NewDiscoverer returns a Discoverer for the Config.
|
||||
func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
|
||||
return NewDiscovery(c, opts.Logger)
|
||||
}
|
||||
|
||||
type refresher interface {
|
||||
refresh(context.Context) ([]*targetgroup.Group, error)
|
||||
}
|
||||
|
||||
// role is the role of the target within the Hetzner Ecosystem.
|
||||
type role string
|
||||
|
||||
// The valid options for role.
|
||||
const (
|
||||
// Hetzner Robot Role (Dedicated Server)
|
||||
// https://robot.hetzner.com
|
||||
hetznerRoleRobot role = "robot"
|
||||
// Hetzner Cloud Role
|
||||
// https://console.hetzner.cloud
|
||||
hetznerRoleHcloud role = "hcloud"
|
||||
)
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *role) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
if err := unmarshal((*string)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
switch *c {
|
||||
case hetznerRoleRobot, hetznerRoleHcloud:
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("unknown role %q", *c)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*c = DefaultSDConfig
|
||||
type plain SDConfig
|
||||
err := unmarshal((*plain)(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Role == "" {
|
||||
return errors.New("role missing (one of: robot, hcloud)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Discovery periodically performs Hetzner requests. It implements
|
||||
// the Discoverer interface.
|
||||
type Discovery struct {
|
||||
*refresh.Discovery
|
||||
}
|
||||
|
||||
// NewDiscovery returns a new Discovery which periodically refreshes its targets.
|
||||
func NewDiscovery(conf *SDConfig, logger log.Logger) (*refresh.Discovery, error) {
|
||||
r, err := newRefresher(conf, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return refresh.NewDiscovery(
|
||||
logger,
|
||||
"hetzner",
|
||||
time.Duration(conf.RefreshInterval),
|
||||
r.refresh,
|
||||
), nil
|
||||
}
|
||||
|
||||
func newRefresher(conf *SDConfig, l log.Logger) (refresher, error) {
|
||||
switch conf.Role {
|
||||
case hetznerRoleHcloud:
|
||||
if conf.hcloudEndpoint == "" {
|
||||
conf.hcloudEndpoint = hcloud.Endpoint
|
||||
}
|
||||
return newHcloudDiscovery(conf, l)
|
||||
case hetznerRoleRobot:
|
||||
if conf.robotEndpoint == "" {
|
||||
conf.robotEndpoint = "https://robot-ws.your-server.de"
|
||||
}
|
||||
return newRobotDiscovery(conf, l)
|
||||
}
|
||||
return nil, errors.New("unknown Hetzner discovery role")
|
||||
}
|
552
discovery/hetzner/mock_test.go
Normal file
552
discovery/hetzner/mock_test.go
Normal file
|
@ -0,0 +1,552 @@
|
|||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hetzner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// SDMock is the interface for the Hetzner Cloud mock
|
||||
type SDMock struct {
|
||||
t *testing.T
|
||||
Server *httptest.Server
|
||||
Mux *http.ServeMux
|
||||
}
|
||||
|
||||
// NewSDMock returns a new SDMock.
|
||||
func NewSDMock(t *testing.T) *SDMock {
|
||||
return &SDMock{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
// Endpoint returns the URI to the mock server
|
||||
func (m *SDMock) Endpoint() string {
|
||||
return m.Server.URL + "/"
|
||||
}
|
||||
|
||||
// Setup creates the mock server
|
||||
func (m *SDMock) Setup() {
|
||||
m.Mux = http.NewServeMux()
|
||||
m.Server = httptest.NewServer(m.Mux)
|
||||
m.t.Cleanup(m.Server.Close)
|
||||
}
|
||||
|
||||
// ShutdownServer creates the mock server
|
||||
func (m *SDMock) ShutdownServer() {
|
||||
m.Server.Close()
|
||||
}
|
||||
|
||||
const hcloudTestToken = "LRK9DAWQ1ZAEFSrCNEEzLCUwhYX1U3g7wMg4dTlkkDC96fyDuyJ39nVbVjCKSDfj"
|
||||
|
||||
// HandleHcloudServers mocks the cloud servers list endpoint.
|
||||
func (m *SDMock) HandleHcloudServers() {
|
||||
m.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", hcloudTestToken) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("content-type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprint(w, `
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"id": 42,
|
||||
"name": "my-server",
|
||||
"status": "running",
|
||||
"created": "2016-01-30T23:50:00+00:00",
|
||||
"public_net": {
|
||||
"ipv4": {
|
||||
"ip": "1.2.3.4",
|
||||
"blocked": false,
|
||||
"dns_ptr": "server01.example.com"
|
||||
},
|
||||
"ipv6": {
|
||||
"ip": "2001:db8::/64",
|
||||
"blocked": false,
|
||||
"dns_ptr": [
|
||||
{
|
||||
"ip": "2001:db8::1",
|
||||
"dns_ptr": "server.example.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
"floating_ips": [
|
||||
478
|
||||
]
|
||||
},
|
||||
"private_net": [
|
||||
{
|
||||
"network": 4711,
|
||||
"ip": "10.0.0.2",
|
||||
"alias_ips": [],
|
||||
"mac_address": "86:00:ff:2a:7d:e1"
|
||||
}
|
||||
],
|
||||
"server_type": {
|
||||
"id": 1,
|
||||
"name": "cx11",
|
||||
"description": "CX11",
|
||||
"cores": 1,
|
||||
"memory": 1,
|
||||
"disk": 25,
|
||||
"deprecated": false,
|
||||
"prices": [
|
||||
{
|
||||
"location": "fsn1",
|
||||
"price_hourly": {
|
||||
"net": "1.0000000000",
|
||||
"gross": "1.1900000000000000"
|
||||
},
|
||||
"price_monthly": {
|
||||
"net": "1.0000000000",
|
||||
"gross": "1.1900000000000000"
|
||||
}
|
||||
}
|
||||
],
|
||||
"storage_type": "local",
|
||||
"cpu_type": "shared"
|
||||
},
|
||||
"datacenter": {
|
||||
"id": 1,
|
||||
"name": "fsn1-dc8",
|
||||
"description": "Falkenstein 1 DC 8",
|
||||
"location": {
|
||||
"id": 1,
|
||||
"name": "fsn1",
|
||||
"description": "Falkenstein DC Park 1",
|
||||
"country": "DE",
|
||||
"city": "Falkenstein",
|
||||
"latitude": 50.47612,
|
||||
"longitude": 12.370071,
|
||||
"network_zone": "eu-central"
|
||||
},
|
||||
"server_types": {
|
||||
"supported": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"available": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"available_for_migration": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"id": 4711,
|
||||
"type": "system",
|
||||
"status": "available",
|
||||
"name": "ubuntu-20.04",
|
||||
"description": "Ubuntu 20.04 Standard 64 bit",
|
||||
"image_size": 2.3,
|
||||
"disk_size": 10,
|
||||
"created": "2016-01-30T23:50:00+00:00",
|
||||
"created_from": {
|
||||
"id": 1,
|
||||
"name": "Server"
|
||||
},
|
||||
"bound_to": null,
|
||||
"os_flavor": "ubuntu",
|
||||
"os_version": "20.04",
|
||||
"rapid_deploy": false,
|
||||
"protection": {
|
||||
"delete": false
|
||||
},
|
||||
"deprecated": "2018-02-28T00:00:00+00:00",
|
||||
"labels": {}
|
||||
},
|
||||
"iso": null,
|
||||
"rescue_enabled": false,
|
||||
"locked": false,
|
||||
"backup_window": "22-02",
|
||||
"outgoing_traffic": 123456,
|
||||
"ingoing_traffic": 123456,
|
||||
"included_traffic": 654321,
|
||||
"protection": {
|
||||
"delete": false,
|
||||
"rebuild": false
|
||||
},
|
||||
"labels": {
|
||||
"my-key": "my-value"
|
||||
},
|
||||
"volumes": [],
|
||||
"load_balancers": []
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"name": "another-server",
|
||||
"status": "stopped",
|
||||
"created": "2016-01-30T23:50:00+00:00",
|
||||
"public_net": {
|
||||
"ipv4": {
|
||||
"ip": "1.2.3.5",
|
||||
"blocked": false,
|
||||
"dns_ptr": "server01.example.org"
|
||||
},
|
||||
"ipv6": {
|
||||
"ip": "2001:db9::/64",
|
||||
"blocked": false,
|
||||
"dns_ptr": [
|
||||
{
|
||||
"ip": "2001:db9::1",
|
||||
"dns_ptr": "server01.example.org"
|
||||
}
|
||||
]
|
||||
},
|
||||
"floating_ips": []
|
||||
},
|
||||
"private_net": [],
|
||||
"server_type": {
|
||||
"id": 2,
|
||||
"name": "cpx11",
|
||||
"description": "CPX11",
|
||||
"cores": 2,
|
||||
"memory": 1,
|
||||
"disk": 50,
|
||||
"deprecated": false,
|
||||
"prices": [
|
||||
{
|
||||
"location": "fsn1",
|
||||
"price_hourly": {
|
||||
"net": "1.0000000000",
|
||||
"gross": "1.1900000000000000"
|
||||
},
|
||||
"price_monthly": {
|
||||
"net": "1.0000000000",
|
||||
"gross": "1.1900000000000000"
|
||||
}
|
||||
}
|
||||
],
|
||||
"storage_type": "local",
|
||||
"cpu_type": "shared"
|
||||
},
|
||||
"datacenter": {
|
||||
"id": 2,
|
||||
"name": "fsn1-dc14",
|
||||
"description": "Falkenstein 1 DC 14",
|
||||
"location": {
|
||||
"id": 1,
|
||||
"name": "fsn1",
|
||||
"description": "Falkenstein DC Park 1",
|
||||
"country": "DE",
|
||||
"city": "Falkenstein",
|
||||
"latitude": 50.47612,
|
||||
"longitude": 12.370071,
|
||||
"network_zone": "eu-central"
|
||||
},
|
||||
"server_types": {
|
||||
"supported": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"available": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"available_for_migration": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"id": 4711,
|
||||
"type": "system",
|
||||
"status": "available",
|
||||
"name": "ubuntu-20.04",
|
||||
"description": "Ubuntu 20.04 Standard 64 bit",
|
||||
"image_size": 2.3,
|
||||
"disk_size": 10,
|
||||
"created": "2016-01-30T23:50:00+00:00",
|
||||
"created_from": {
|
||||
"id": 1,
|
||||
"name": "Server"
|
||||
},
|
||||
"bound_to": null,
|
||||
"os_flavor": "ubuntu",
|
||||
"os_version": "20.04",
|
||||
"rapid_deploy": false,
|
||||
"protection": {
|
||||
"delete": false
|
||||
},
|
||||
"deprecated": "2018-02-28T00:00:00+00:00",
|
||||
"labels": {}
|
||||
},
|
||||
"iso": null,
|
||||
"rescue_enabled": false,
|
||||
"locked": false,
|
||||
"backup_window": "22-02",
|
||||
"outgoing_traffic": 123456,
|
||||
"ingoing_traffic": 123456,
|
||||
"included_traffic": 654321,
|
||||
"protection": {
|
||||
"delete": false,
|
||||
"rebuild": false
|
||||
},
|
||||
"labels": {},
|
||||
"volumes": [],
|
||||
"load_balancers": []
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"name": "deleted-image-server",
|
||||
"status": "stopped",
|
||||
"created": "2016-01-30T23:50:00+00:00",
|
||||
"public_net": {
|
||||
"ipv4": {
|
||||
"ip": "1.2.3.6",
|
||||
"blocked": false,
|
||||
"dns_ptr": "server01.example.org"
|
||||
},
|
||||
"ipv6": {
|
||||
"ip": "2001:db7::/64",
|
||||
"blocked": false,
|
||||
"dns_ptr": [
|
||||
{
|
||||
"ip": "2001:db7::1",
|
||||
"dns_ptr": "server01.example.org"
|
||||
}
|
||||
]
|
||||
},
|
||||
"floating_ips": []
|
||||
},
|
||||
"private_net": [],
|
||||
"server_type": {
|
||||
"id": 2,
|
||||
"name": "cpx11",
|
||||
"description": "CPX11",
|
||||
"cores": 2,
|
||||
"memory": 1,
|
||||
"disk": 50,
|
||||
"deprecated": false,
|
||||
"prices": [
|
||||
{
|
||||
"location": "fsn1",
|
||||
"price_hourly": {
|
||||
"net": "1.0000000000",
|
||||
"gross": "1.1900000000000000"
|
||||
},
|
||||
"price_monthly": {
|
||||
"net": "1.0000000000",
|
||||
"gross": "1.1900000000000000"
|
||||
}
|
||||
}
|
||||
],
|
||||
"storage_type": "local",
|
||||
"cpu_type": "shared"
|
||||
},
|
||||
"datacenter": {
|
||||
"id": 2,
|
||||
"name": "fsn1-dc14",
|
||||
"description": "Falkenstein 1 DC 14",
|
||||
"location": {
|
||||
"id": 1,
|
||||
"name": "fsn1",
|
||||
"description": "Falkenstein DC Park 1",
|
||||
"country": "DE",
|
||||
"city": "Falkenstein",
|
||||
"latitude": 50.47612,
|
||||
"longitude": 12.370071,
|
||||
"network_zone": "eu-central"
|
||||
},
|
||||
"server_types": {
|
||||
"supported": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"available": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"available_for_migration": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
},
|
||||
"image": null,
|
||||
"iso": null,
|
||||
"rescue_enabled": false,
|
||||
"locked": false,
|
||||
"backup_window": "22-02",
|
||||
"outgoing_traffic": 123456,
|
||||
"ingoing_traffic": 123456,
|
||||
"included_traffic": 654321,
|
||||
"protection": {
|
||||
"delete": false,
|
||||
"rebuild": false
|
||||
},
|
||||
"labels": {},
|
||||
"volumes": [],
|
||||
"load_balancers": []
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"per_page": 25,
|
||||
"previous_page": null,
|
||||
"next_page": null,
|
||||
"last_page": 1,
|
||||
"total_entries": 2
|
||||
}
|
||||
}
|
||||
}`,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// HandleHcloudNetworks mocks the cloud networks list endpoint.
|
||||
func (m *SDMock) HandleHcloudNetworks() {
|
||||
m.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", hcloudTestToken) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("content-type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprint(w, `
|
||||
{
|
||||
"networks": [
|
||||
{
|
||||
"id": 4711,
|
||||
"name": "mynet",
|
||||
"ip_range": "10.0.0.0/16",
|
||||
"subnets": [
|
||||
{
|
||||
"type": "cloud",
|
||||
"ip_range": "10.0.1.0/24",
|
||||
"network_zone": "eu-central",
|
||||
"gateway": "10.0.0.1"
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"destination": "10.100.1.0/24",
|
||||
"gateway": "10.0.1.1"
|
||||
}
|
||||
],
|
||||
"servers": [
|
||||
42
|
||||
],
|
||||
"load_balancers": [
|
||||
42
|
||||
],
|
||||
"protection": {
|
||||
"delete": false
|
||||
},
|
||||
"labels": {},
|
||||
"created": "2016-01-30T23:50:00+00:00"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"per_page": 25,
|
||||
"previous_page": null,
|
||||
"next_page": null,
|
||||
"last_page": 1,
|
||||
"total_entries": 1
|
||||
}
|
||||
}
|
||||
}`,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const robotTestUsername = "my-hetzner"
|
||||
const robotTestPassword = "my-password"
|
||||
|
||||
// HandleRobotServers mocks the robot servers list endpoint.
|
||||
func (m *SDMock) HandleRobotServers() {
|
||||
m.Mux.HandleFunc("/server", func(w http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if username != robotTestUsername && password != robotTestPassword && !ok {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("content-type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
fmt.Fprint(w, `
|
||||
[
|
||||
{
|
||||
"server":{
|
||||
"server_ip":"123.123.123.123",
|
||||
"server_number":321,
|
||||
"server_name":"server1",
|
||||
"product":"DS 3000",
|
||||
"dc":"NBG1-DC1",
|
||||
"traffic":"5 TB",
|
||||
"flatrate":true,
|
||||
"status":"ready",
|
||||
"throttled":true,
|
||||
"cancelled":false,
|
||||
"paid_until":"2010-09-02",
|
||||
"ip":[
|
||||
"123.123.123.123"
|
||||
],
|
||||
"subnet":[
|
||||
{
|
||||
"ip":"2a01:4f8:111:4221::",
|
||||
"mask":"64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"server":{
|
||||
"server_ip":"123.123.123.124",
|
||||
"server_number":421,
|
||||
"server_name":"server2",
|
||||
"product":"X5",
|
||||
"dc":"FSN1-DC10",
|
||||
"traffic":"2 TB",
|
||||
"flatrate":true,
|
||||
"status":"in process",
|
||||
"throttled":false,
|
||||
"cancelled":true,
|
||||
"paid_until":"2010-06-11",
|
||||
"ip":[
|
||||
"123.123.123.124"
|
||||
],
|
||||
"subnet":null
|
||||
}
|
||||
}
|
||||
]`,
|
||||
)
|
||||
})
|
||||
}
|
124
discovery/hetzner/robot.go
Normal file
124
discovery/hetzner/robot.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hetzner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
config_util "github.com/prometheus/common/config"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/discovery/refresh"
|
||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
hetznerRobotLabelPrefix = hetznerLabelPrefix + "robot_"
|
||||
hetznerLabelRobotProduct = hetznerRobotLabelPrefix + "product"
|
||||
hetznerLabelRobotCancelled = hetznerRobotLabelPrefix + "cancelled"
|
||||
)
|
||||
|
||||
// Discovery periodically performs Hetzner Robot requests. It implements
|
||||
// the Discoverer interface.
|
||||
type robotDiscovery struct {
|
||||
*refresh.Discovery
|
||||
client *http.Client
|
||||
port int
|
||||
endpoint string
|
||||
}
|
||||
|
||||
// newRobotDiscovery returns a new robotDiscovery which periodically refreshes its targets.
|
||||
func newRobotDiscovery(conf *SDConfig, logger log.Logger) (*robotDiscovery, error) {
|
||||
d := &robotDiscovery{
|
||||
port: conf.Port,
|
||||
endpoint: conf.robotEndpoint,
|
||||
}
|
||||
|
||||
rt, err := config_util.NewRoundTripperFromConfig(conf.HTTPClientConfig, "hetzner_sd", false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.client = &http.Client{
|
||||
Transport: rt,
|
||||
Timeout: time.Duration(conf.RefreshInterval),
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
func (d *robotDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
|
||||
resp, err := d.client.Get(d.endpoint + "/server")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
var servers serversList
|
||||
err = json.NewDecoder(resp.Body).Decode(&servers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targets := make([]model.LabelSet, len(servers))
|
||||
for i, server := range servers {
|
||||
labels := model.LabelSet{
|
||||
hetznerLabelRole: model.LabelValue(hetznerRoleRobot),
|
||||
hetznerLabelServerID: model.LabelValue(strconv.Itoa(server.Server.ServerNumber)),
|
||||
hetznerLabelServerName: model.LabelValue(server.Server.ServerName),
|
||||
hetznerLabelDatacenter: model.LabelValue(strings.ToLower(server.Server.Dc)),
|
||||
hetznerLabelPublicIPv4: model.LabelValue(server.Server.ServerIP),
|
||||
hetznerLabelServerStatus: model.LabelValue(server.Server.Status),
|
||||
hetznerLabelRobotProduct: model.LabelValue(server.Server.Product),
|
||||
hetznerLabelRobotCancelled: model.LabelValue(fmt.Sprintf("%t", server.Server.Canceled)),
|
||||
|
||||
model.AddressLabel: model.LabelValue(net.JoinHostPort(server.Server.ServerIP, strconv.FormatUint(uint64(d.port), 10))),
|
||||
}
|
||||
for _, subnet := range server.Server.Subnet {
|
||||
ip := net.ParseIP(subnet.IP)
|
||||
if ip.To4() == nil {
|
||||
labels[hetznerLabelPublicIPv6Network] = model.LabelValue(fmt.Sprintf("%s/%s", subnet.IP, subnet.Mask))
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
targets[i] = labels
|
||||
}
|
||||
return []*targetgroup.Group{{Source: "hetzner", Targets: targets}}, nil
|
||||
}
|
||||
|
||||
type serversList []struct {
|
||||
Server struct {
|
||||
ServerIP string `json:"server_ip"`
|
||||
ServerNumber int `json:"server_number"`
|
||||
ServerName string `json:"server_name"`
|
||||
Dc string `json:"dc"`
|
||||
Status string `json:"status"`
|
||||
Product string `json:"product"`
|
||||
Canceled bool `json:"cancelled"`
|
||||
Subnet []struct {
|
||||
IP string `json:"ip"`
|
||||
Mask string `json:"mask"`
|
||||
} `json:"subnet"`
|
||||
} `json:"server"`
|
||||
}
|
86
discovery/hetzner/robot_test.go
Normal file
86
discovery/hetzner/robot_test.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hetzner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/prometheus/common/config"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type robotSDTestSuite struct {
|
||||
Mock *SDMock
|
||||
}
|
||||
|
||||
func (s *robotSDTestSuite) SetupTest(t *testing.T) {
|
||||
s.Mock = NewSDMock(t)
|
||||
s.Mock.Setup()
|
||||
|
||||
s.Mock.HandleRobotServers()
|
||||
}
|
||||
|
||||
func TestRobotSDRefresh(t *testing.T) {
|
||||
suite := &robotSDTestSuite{}
|
||||
suite.SetupTest(t)
|
||||
|
||||
cfg := DefaultSDConfig
|
||||
cfg.HTTPClientConfig.BasicAuth = &config.BasicAuth{Username: robotTestUsername, Password: robotTestPassword}
|
||||
cfg.robotEndpoint = suite.Mock.Endpoint()
|
||||
|
||||
d, err := newRobotDiscovery(&cfg, log.NewNopLogger())
|
||||
testutil.Ok(t, err)
|
||||
|
||||
targetGroups, err := d.refresh(context.Background())
|
||||
testutil.Ok(t, err)
|
||||
testutil.Equals(t, 1, len(targetGroups))
|
||||
|
||||
targetGroup := targetGroups[0]
|
||||
testutil.Assert(t, targetGroup != nil, "targetGroup should not be nil")
|
||||
testutil.Assert(t, targetGroup.Targets != nil, "targetGroup.targets should not be nil")
|
||||
testutil.Equals(t, 2, len(targetGroup.Targets))
|
||||
|
||||
for i, labelSet := range []model.LabelSet{
|
||||
{
|
||||
"__address__": model.LabelValue("123.123.123.123:80"),
|
||||
"__meta_hetzner_role": model.LabelValue("robot"),
|
||||
"__meta_hetzner_server_id": model.LabelValue("321"),
|
||||
"__meta_hetzner_server_name": model.LabelValue("server1"),
|
||||
"__meta_hetzner_server_status": model.LabelValue("ready"),
|
||||
"__meta_hetzner_public_ipv4": model.LabelValue("123.123.123.123"),
|
||||
"__meta_hetzner_public_ipv6_network": model.LabelValue("2a01:4f8:111:4221::/64"),
|
||||
"__meta_hetzner_datacenter": model.LabelValue("nbg1-dc1"),
|
||||
"__meta_hetzner_robot_product": model.LabelValue("DS 3000"),
|
||||
"__meta_hetzner_robot_cancelled": model.LabelValue("false"),
|
||||
},
|
||||
{
|
||||
"__address__": model.LabelValue("123.123.123.124:80"),
|
||||
"__meta_hetzner_role": model.LabelValue("robot"),
|
||||
"__meta_hetzner_server_id": model.LabelValue("421"),
|
||||
"__meta_hetzner_server_name": model.LabelValue("server2"),
|
||||
"__meta_hetzner_server_status": model.LabelValue("in process"),
|
||||
"__meta_hetzner_public_ipv4": model.LabelValue("123.123.123.124"),
|
||||
"__meta_hetzner_datacenter": model.LabelValue("fsn1-dc10"),
|
||||
"__meta_hetzner_robot_product": model.LabelValue("X5"),
|
||||
"__meta_hetzner_robot_cancelled": model.LabelValue("true"),
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
|
||||
testutil.Equals(t, labelSet, targetGroup.Targets[i])
|
||||
})
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import (
|
|||
_ "github.com/prometheus/prometheus/discovery/ec2" // register ec2
|
||||
_ "github.com/prometheus/prometheus/discovery/file" // register file
|
||||
_ "github.com/prometheus/prometheus/discovery/gce" // register gce
|
||||
_ "github.com/prometheus/prometheus/discovery/hetzner" // register hetzner
|
||||
_ "github.com/prometheus/prometheus/discovery/kubernetes" // register kubernetes
|
||||
_ "github.com/prometheus/prometheus/discovery/marathon" // register marathon
|
||||
_ "github.com/prometheus/prometheus/discovery/openstack" // register openstack
|
||||
|
|
|
@ -215,6 +215,10 @@ file_sd_configs:
|
|||
gce_sd_configs:
|
||||
[ - <gce_sd_config> ... ]
|
||||
|
||||
# List of Hetzner service discovery configurations.
|
||||
hetzner_sd_configs:
|
||||
[ - <hetzner_sd_config> ... ]
|
||||
|
||||
# List of Kubernetes service discovery configurations.
|
||||
kubernetes_sd_configs:
|
||||
[ - <kubernetes_sd_config> ... ]
|
||||
|
@ -918,6 +922,81 @@ instance it is running on should have at least read-only permissions to the
|
|||
compute resources. If running outside of GCE make sure to create an appropriate
|
||||
service account and place the credential file in one of the expected locations.
|
||||
|
||||
### `<hetzner_sd_config>`
|
||||
|
||||
Hetzner SD configurations allow retrieving scrape targets from [Hetzner Cloud](https://www.hetzner.cloud/)
|
||||
Server API.
|
||||
This service discovery uses the public IPv4 address by default, but that can be
|
||||
changed with relabeling, as demonstrated in [the Prometheus hetzner-sd
|
||||
configuration file](/documentation/examples/prometheus-hetzner.yml).
|
||||
|
||||
The following meta labels are available on all targets during [relabeling](#relabel_config):
|
||||
|
||||
* `__meta_hetzner_server_id`: the id of the server
|
||||
* `__meta_hetzner_server_name`: the name of the server
|
||||
* `__meta_hetzner_server_status`: the status of the server
|
||||
* `__meta_hetzner_public_ipv4`: the public ipv4 of the server
|
||||
* `__meta_hetzner_public_ipv6_network`: the public ipv6 net (/64) of the server
|
||||
* `__meta_hetzner_datacenter`: the name of the datacenter the server is located
|
||||
|
||||
The labels below are only available for targets with `role` set to `hcloud`:
|
||||
* `__meta_hetzner_hcloud_image_name`: the image name of the server
|
||||
* `__meta_hetzner_hcloud_image_description`: the image description of the server
|
||||
* `__meta_hetzner_hcloud_image_os_flavor`: the image os flavor of the server
|
||||
* `__meta_hetzner_hcloud_image_os_version`: the image os version of the server
|
||||
* `__meta_hetzner_hcloud_image_description`: the image name or description of the server
|
||||
* `__meta_hetzner_hcloud_datacenter_location`: the name of the location the server is located
|
||||
* `__meta_hetzner_hcloud_datacenter_location_network_zone`: the name of the network zone the server is located
|
||||
* `__meta_hetzner_hcloud_server_type`: the name of the type of the server
|
||||
* `__meta_hetzner_hcloud_cpu_cores`: the count of CPU cores the server has
|
||||
* `__meta_hetzner_hcloud_cpu_type`: the type of the CPU (shared or dedicated)
|
||||
* `__meta_hetzner_hcloud_memory_size_gb`: the amount of memory the server has (in GB)
|
||||
* `__meta_hetzner_hcloud_disk_size_gb`: the size of disk the server has (in GB)
|
||||
* `__meta_hetzner_hcloud_private_ipv4_<network name>`: the private ipv4 of the server within the network named in the metric if it is attached to a network
|
||||
* `__meta_hetzner_hcloud_label_<labelname>`: the hetzner cloud label of the server with its specific value within the cloud
|
||||
|
||||
The labels below are only available for targets with `role` set to `robot`:
|
||||
* `__meta_hetzner_robot_product`: the name of the product of the server
|
||||
* `__meta_hetzner_robot_cancelled`: the status of the server cancellation
|
||||
|
||||
```yaml
|
||||
# The Hetzner role of entities that should be discovered.
|
||||
# One of robot or hcloud.
|
||||
role: <string>
|
||||
|
||||
# Authentication information used to authenticate to the API server.
|
||||
# Note that `basic_auth`, `bearer_token` and `bearer_token_file` options are
|
||||
# mutually exclusive.
|
||||
# password and password_file are mutually exclusive.
|
||||
|
||||
# Optional HTTP basic authentication information, required when role is robot
|
||||
# Role hcloud does not support basic auth.
|
||||
basic_auth:
|
||||
[ username: <string> ]
|
||||
[ password: <secret> ]
|
||||
[ password_file: <string> ]
|
||||
|
||||
# Optional bearer token authentication information, required when role is hcloud
|
||||
# Role robot does not support bearer token authentication.
|
||||
[ bearer_token: <secret> ]
|
||||
|
||||
# Optional bearer token file authentication information.
|
||||
[ bearer_token_file: <filename> ]
|
||||
|
||||
# Optional proxy URL.
|
||||
[ proxy_url: <string> ]
|
||||
|
||||
# TLS configuration.
|
||||
tls_config:
|
||||
[ <tls_config> ]
|
||||
|
||||
# The port to scrape metrics from.
|
||||
[ port: <int> | default = 80 ]
|
||||
|
||||
# The time after which the servers are refreshed.
|
||||
[ refresh_interval: <duration> | default = 60s ]
|
||||
```
|
||||
|
||||
### `<kubernetes_sd_config>`
|
||||
|
||||
Kubernetes SD configurations allow retrieving scrape targets from
|
||||
|
@ -1494,6 +1573,10 @@ dockerswarm_sd_configs:
|
|||
gce_sd_configs:
|
||||
[ - <gce_sd_config> ... ]
|
||||
|
||||
# List of Hetzner service discovery configurations.
|
||||
hetzner_sd_configs:
|
||||
[ - <hetzner_sd_config> ... ]
|
||||
|
||||
# List of Kubernetes service discovery configurations.
|
||||
kubernetes_sd_configs:
|
||||
[ - <kubernetes_sd_config> ... ]
|
||||
|
|
47
documentation/examples/prometheus-hetzner.yml
Normal file
47
documentation/examples/prometheus-hetzner.yml
Normal file
|
@ -0,0 +1,47 @@
|
|||
# A example scrape configuration for running Prometheus with
|
||||
# Hetzner.
|
||||
|
||||
scrape_configs:
|
||||
|
||||
# Make Prometheus scrape itself for metrics.
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
# Discover Node Exporter instances to scrape.
|
||||
- job_name: 'node'
|
||||
|
||||
hetzner_sd_configs:
|
||||
- bearer_token: "<replace with a Hetzner Cloud API Token>"
|
||||
platform: "hcloud"
|
||||
relabel_configs:
|
||||
# Use the public IPv4 and port 9100 to scrape the target.
|
||||
- source_labels: [__meta_hetzner_public_ipv4]
|
||||
target_label: __address__
|
||||
replacement: '$1:9100'
|
||||
|
||||
# Discover Node Exporter instances to scrape using a Hetzner Cloud Network called mynet.
|
||||
- job_name: 'node_private'
|
||||
|
||||
hetzner_sd_configs:
|
||||
- bearer_token: "<replace with a Hetzner Cloud API Token>"
|
||||
platform: "hcloud"
|
||||
relabel_configs:
|
||||
# Use the private IPv4 within the Hetzner Cloud Network and port 9100 to scrape the target.
|
||||
- source_labels: [__meta_hetzner_hcloud_private_ipv4_mynet]
|
||||
target_label: __address__
|
||||
replacement: '$1:9100'
|
||||
|
||||
# Discover Node Exporter instances to scrape.
|
||||
- job_name: 'node_robot'
|
||||
|
||||
hetzner_sd_configs:
|
||||
- basic_auth:
|
||||
username: "<replace with a Hetzner Robot API username>"
|
||||
password: "<replace with a Hetzner Robot API password>"
|
||||
platform: "robot"
|
||||
relabel_configs:
|
||||
# Use the public IPv4 and port 9100 to scrape the target.
|
||||
- source_labels: [__meta_hetzner_public_ipv4]
|
||||
target_label: __address__
|
||||
replacement: '$1:9100'
|
1
go.mod
1
go.mod
|
@ -35,6 +35,7 @@ require (
|
|||
github.com/hashicorp/go-hclog v0.12.2 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.2.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hetznercloud/hcloud-go v1.21.1
|
||||
github.com/influxdata/influxdb v1.8.1
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -351,6 +351,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
|
@ -440,6 +442,8 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
|
|||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.9.0 h1:+Zd/16AJ9lxk9RzfTDyv/TLhZ8UerqYS0/+JGCIDaa0=
|
||||
github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU=
|
||||
github.com/hetznercloud/hcloud-go v1.21.1 h1:LWNozxiZhKmeMqYbAS7KsAcPcxg47afCnTeLKmN+n7w=
|
||||
github.com/hetznercloud/hcloud-go v1.21.1/go.mod h1:xng8lbDUg+xM1dgc0yGHX5EeqbwIq7UYlMWMTx3SQVg=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
|
|
83
vendor/github.com/google/go-cmp/cmp/compare.go
generated
vendored
83
vendor/github.com/google/go-cmp/cmp/compare.go
generated
vendored
|
@ -6,6 +6,10 @@
|
|||
//
|
||||
// This package is intended to be a more powerful and safer alternative to
|
||||
// reflect.DeepEqual for comparing whether two values are semantically equal.
|
||||
// It is intended to only be used in tests, as performance is not a goal and
|
||||
// it may panic if it cannot compare the values. Its propensity towards
|
||||
// panicking means that its unsuitable for production environments where a
|
||||
// spurious panic may be fatal.
|
||||
//
|
||||
// The primary features of cmp are:
|
||||
//
|
||||
|
@ -86,6 +90,52 @@ import (
|
|||
// If there is a cycle, then the pointed at values are considered equal
|
||||
// only if both addresses were previously visited in the same path step.
|
||||
func Equal(x, y interface{}, opts ...Option) bool {
|
||||
s := newState(opts)
|
||||
s.compareAny(rootStep(x, y))
|
||||
return s.result.Equal()
|
||||
}
|
||||
|
||||
// Diff returns a human-readable report of the differences between two values.
|
||||
// It returns an empty string if and only if Equal returns true for the same
|
||||
// input values and options.
|
||||
//
|
||||
// The output is displayed as a literal in pseudo-Go syntax.
|
||||
// At the start of each line, a "-" prefix indicates an element removed from x,
|
||||
// a "+" prefix to indicates an element added to y, and the lack of a prefix
|
||||
// indicates an element common to both x and y. If possible, the output
|
||||
// uses fmt.Stringer.String or error.Error methods to produce more humanly
|
||||
// readable outputs. In such cases, the string is prefixed with either an
|
||||
// 's' or 'e' character, respectively, to indicate that the method was called.
|
||||
//
|
||||
// Do not depend on this output being stable. If you need the ability to
|
||||
// programmatically interpret the difference, consider using a custom Reporter.
|
||||
func Diff(x, y interface{}, opts ...Option) string {
|
||||
s := newState(opts)
|
||||
|
||||
// Optimization: If there are no other reporters, we can optimize for the
|
||||
// common case where the result is equal (and thus no reported difference).
|
||||
// This avoids the expensive construction of a difference tree.
|
||||
if len(s.reporters) == 0 {
|
||||
s.compareAny(rootStep(x, y))
|
||||
if s.result.Equal() {
|
||||
return ""
|
||||
}
|
||||
s.result = diff.Result{} // Reset results
|
||||
}
|
||||
|
||||
r := new(defaultReporter)
|
||||
s.reporters = append(s.reporters, reporter{r})
|
||||
s.compareAny(rootStep(x, y))
|
||||
d := r.String()
|
||||
if (d == "") != s.result.Equal() {
|
||||
panic("inconsistent difference and equality results")
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// rootStep constructs the first path step. If x and y have differing types,
|
||||
// then they are stored within an empty interface type.
|
||||
func rootStep(x, y interface{}) PathStep {
|
||||
vx := reflect.ValueOf(x)
|
||||
vy := reflect.ValueOf(y)
|
||||
|
||||
|
@ -108,33 +158,7 @@ func Equal(x, y interface{}, opts ...Option) bool {
|
|||
t = vx.Type()
|
||||
}
|
||||
|
||||
s := newState(opts)
|
||||
s.compareAny(&pathStep{t, vx, vy})
|
||||
return s.result.Equal()
|
||||
}
|
||||
|
||||
// Diff returns a human-readable report of the differences between two values.
|
||||
// It returns an empty string if and only if Equal returns true for the same
|
||||
// input values and options.
|
||||
//
|
||||
// The output is displayed as a literal in pseudo-Go syntax.
|
||||
// At the start of each line, a "-" prefix indicates an element removed from x,
|
||||
// a "+" prefix to indicates an element added to y, and the lack of a prefix
|
||||
// indicates an element common to both x and y. If possible, the output
|
||||
// uses fmt.Stringer.String or error.Error methods to produce more humanly
|
||||
// readable outputs. In such cases, the string is prefixed with either an
|
||||
// 's' or 'e' character, respectively, to indicate that the method was called.
|
||||
//
|
||||
// Do not depend on this output being stable. If you need the ability to
|
||||
// programmatically interpret the difference, consider using a custom Reporter.
|
||||
func Diff(x, y interface{}, opts ...Option) string {
|
||||
r := new(defaultReporter)
|
||||
eq := Equal(x, y, Options(opts), Reporter(r))
|
||||
d := r.String()
|
||||
if (d == "") != eq {
|
||||
panic("inconsistent difference and equality results")
|
||||
}
|
||||
return d
|
||||
return &pathStep{t, vx, vy}
|
||||
}
|
||||
|
||||
type state struct {
|
||||
|
@ -352,7 +376,7 @@ func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) {
|
|||
// assuming that T is assignable to R.
|
||||
// Otherwise, it returns the input value as is.
|
||||
func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
|
||||
// TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/22143).
|
||||
// TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/22143).
|
||||
if !flags.AtLeastGo110 {
|
||||
if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
|
||||
return reflect.New(t).Elem()
|
||||
|
@ -362,6 +386,7 @@ func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
|
|||
}
|
||||
|
||||
func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
|
||||
var addr bool
|
||||
var vax, vay reflect.Value // Addressable versions of vx and vy
|
||||
|
||||
var mayForce, mayForceInit bool
|
||||
|
@ -383,6 +408,7 @@ func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
|
|||
// For retrieveUnexportedField to work, the parent struct must
|
||||
// be addressable. Create a new copy of the values if
|
||||
// necessary to make them addressable.
|
||||
addr = vx.CanAddr() || vy.CanAddr()
|
||||
vax = makeAddressable(vx)
|
||||
vay = makeAddressable(vy)
|
||||
}
|
||||
|
@ -393,6 +419,7 @@ func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
|
|||
mayForceInit = true
|
||||
}
|
||||
step.mayForce = mayForce
|
||||
step.paddr = addr
|
||||
step.pvx = vax
|
||||
step.pvy = vay
|
||||
step.field = t.Field(i)
|
||||
|
|
2
vendor/github.com/google/go-cmp/cmp/export_panic.go
generated
vendored
2
vendor/github.com/google/go-cmp/cmp/export_panic.go
generated
vendored
|
@ -10,6 +10,6 @@ import "reflect"
|
|||
|
||||
const supportExporters = false
|
||||
|
||||
func retrieveUnexportedField(reflect.Value, reflect.StructField) reflect.Value {
|
||||
func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value {
|
||||
panic("no support for forcibly accessing unexported fields")
|
||||
}
|
||||
|
|
20
vendor/github.com/google/go-cmp/cmp/export_unsafe.go
generated
vendored
20
vendor/github.com/google/go-cmp/cmp/export_unsafe.go
generated
vendored
|
@ -17,9 +17,19 @@ const supportExporters = true
|
|||
// a struct such that the value has read-write permissions.
|
||||
//
|
||||
// The parent struct, v, must be addressable, while f must be a StructField
|
||||
// describing the field to retrieve.
|
||||
func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value {
|
||||
// See https://github.com/google/go-cmp/issues/167 for discussion of the
|
||||
// following expression.
|
||||
return reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem()
|
||||
// describing the field to retrieve. If addr is false,
|
||||
// then the returned value will be shallowed copied to be non-addressable.
|
||||
func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value {
|
||||
ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem()
|
||||
if !addr {
|
||||
// A field is addressable if and only if the struct is addressable.
|
||||
// If the original parent value was not addressable, shallow copy the
|
||||
// value to make it non-addressable to avoid leaking an implementation
|
||||
// detail of how forcibly exporting a field works.
|
||||
if ve.Kind() == reflect.Interface && ve.IsNil() {
|
||||
return reflect.Zero(f.Type)
|
||||
}
|
||||
return reflect.ValueOf(ve.Interface()).Convert(f.Type)
|
||||
}
|
||||
return ve
|
||||
}
|
||||
|
|
22
vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go
generated
vendored
22
vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go
generated
vendored
|
@ -12,6 +12,13 @@
|
|||
// is more important than obtaining a minimal Levenshtein distance.
|
||||
package diff
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp/internal/flags"
|
||||
)
|
||||
|
||||
// EditType represents a single operation within an edit-script.
|
||||
type EditType uint8
|
||||
|
||||
|
@ -112,6 +119,8 @@ func (r Result) Similar() bool {
|
|||
return r.NumSame+1 >= r.NumDiff
|
||||
}
|
||||
|
||||
var randInt = rand.New(rand.NewSource(time.Now().Unix())).Intn(2)
|
||||
|
||||
// Difference reports whether two lists of lengths nx and ny are equal
|
||||
// given the definition of equality provided as f.
|
||||
//
|
||||
|
@ -159,6 +168,17 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) {
|
|||
// A vertical edge is equivalent to inserting a symbol from list Y.
|
||||
// A diagonal edge is equivalent to a matching symbol between both X and Y.
|
||||
|
||||
// To ensure flexibility in changing the algorithm in the future,
|
||||
// introduce some degree of deliberate instability.
|
||||
// This is achieved by fiddling the zigzag iterator to start searching
|
||||
// the graph starting from the bottom-right versus than the top-left.
|
||||
// The result may differ depending on the starting search location,
|
||||
// but still produces a valid edit script.
|
||||
zigzagInit := randInt // either 0 or 1
|
||||
if flags.Deterministic {
|
||||
zigzagInit = 0
|
||||
}
|
||||
|
||||
// Invariants:
|
||||
// • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx
|
||||
// • 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny
|
||||
|
@ -209,7 +229,7 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) {
|
|||
if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
|
||||
break
|
||||
}
|
||||
for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ {
|
||||
for stop1, stop2, i := false, false, zigzagInit; !(stop1 && stop2) && searchBudget > 0; i++ {
|
||||
// Search in a diagonal pattern for a match.
|
||||
z := zigzag(i)
|
||||
p := point{fwdFrontier.X + z, fwdFrontier.Y - z}
|
||||
|
|
157
vendor/github.com/google/go-cmp/cmp/internal/value/name.go
generated
vendored
Normal file
157
vendor/github.com/google/go-cmp/cmp/internal/value/name.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2020, The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE.md file.
|
||||
|
||||
package value
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TypeString is nearly identical to reflect.Type.String,
|
||||
// but has an additional option to specify that full type names be used.
|
||||
func TypeString(t reflect.Type, qualified bool) string {
|
||||
return string(appendTypeName(nil, t, qualified, false))
|
||||
}
|
||||
|
||||
func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte {
|
||||
// BUG: Go reflection provides no way to disambiguate two named types
|
||||
// of the same name and within the same package,
|
||||
// but declared within the namespace of different functions.
|
||||
|
||||
// Named type.
|
||||
if t.Name() != "" {
|
||||
if qualified && t.PkgPath() != "" {
|
||||
b = append(b, '"')
|
||||
b = append(b, t.PkgPath()...)
|
||||
b = append(b, '"')
|
||||
b = append(b, '.')
|
||||
b = append(b, t.Name()...)
|
||||
} else {
|
||||
b = append(b, t.String()...)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Unnamed type.
|
||||
switch k := t.Kind(); k {
|
||||
case reflect.Bool, reflect.String, reflect.UnsafePointer,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
|
||||
b = append(b, k.String()...)
|
||||
case reflect.Chan:
|
||||
if t.ChanDir() == reflect.RecvDir {
|
||||
b = append(b, "<-"...)
|
||||
}
|
||||
b = append(b, "chan"...)
|
||||
if t.ChanDir() == reflect.SendDir {
|
||||
b = append(b, "<-"...)
|
||||
}
|
||||
b = append(b, ' ')
|
||||
b = appendTypeName(b, t.Elem(), qualified, false)
|
||||
case reflect.Func:
|
||||
if !elideFunc {
|
||||
b = append(b, "func"...)
|
||||
}
|
||||
b = append(b, '(')
|
||||
for i := 0; i < t.NumIn(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, ", "...)
|
||||
}
|
||||
if i == t.NumIn()-1 && t.IsVariadic() {
|
||||
b = append(b, "..."...)
|
||||
b = appendTypeName(b, t.In(i).Elem(), qualified, false)
|
||||
} else {
|
||||
b = appendTypeName(b, t.In(i), qualified, false)
|
||||
}
|
||||
}
|
||||
b = append(b, ')')
|
||||
switch t.NumOut() {
|
||||
case 0:
|
||||
// Do nothing
|
||||
case 1:
|
||||
b = append(b, ' ')
|
||||
b = appendTypeName(b, t.Out(0), qualified, false)
|
||||
default:
|
||||
b = append(b, " ("...)
|
||||
for i := 0; i < t.NumOut(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, ", "...)
|
||||
}
|
||||
b = appendTypeName(b, t.Out(i), qualified, false)
|
||||
}
|
||||
b = append(b, ')')
|
||||
}
|
||||
case reflect.Struct:
|
||||
b = append(b, "struct{ "...)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, "; "...)
|
||||
}
|
||||
sf := t.Field(i)
|
||||
if !sf.Anonymous {
|
||||
if qualified && sf.PkgPath != "" {
|
||||
b = append(b, '"')
|
||||
b = append(b, sf.PkgPath...)
|
||||
b = append(b, '"')
|
||||
b = append(b, '.')
|
||||
}
|
||||
b = append(b, sf.Name...)
|
||||
b = append(b, ' ')
|
||||
}
|
||||
b = appendTypeName(b, sf.Type, qualified, false)
|
||||
if sf.Tag != "" {
|
||||
b = append(b, ' ')
|
||||
b = strconv.AppendQuote(b, string(sf.Tag))
|
||||
}
|
||||
}
|
||||
if b[len(b)-1] == ' ' {
|
||||
b = b[:len(b)-1]
|
||||
} else {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
b = append(b, '}')
|
||||
case reflect.Slice, reflect.Array:
|
||||
b = append(b, '[')
|
||||
if k == reflect.Array {
|
||||
b = strconv.AppendUint(b, uint64(t.Len()), 10)
|
||||
}
|
||||
b = append(b, ']')
|
||||
b = appendTypeName(b, t.Elem(), qualified, false)
|
||||
case reflect.Map:
|
||||
b = append(b, "map["...)
|
||||
b = appendTypeName(b, t.Key(), qualified, false)
|
||||
b = append(b, ']')
|
||||
b = appendTypeName(b, t.Elem(), qualified, false)
|
||||
case reflect.Ptr:
|
||||
b = append(b, '*')
|
||||
b = appendTypeName(b, t.Elem(), qualified, false)
|
||||
case reflect.Interface:
|
||||
b = append(b, "interface{ "...)
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, "; "...)
|
||||
}
|
||||
m := t.Method(i)
|
||||
if qualified && m.PkgPath != "" {
|
||||
b = append(b, '"')
|
||||
b = append(b, m.PkgPath...)
|
||||
b = append(b, '"')
|
||||
b = append(b, '.')
|
||||
}
|
||||
b = append(b, m.Name...)
|
||||
b = appendTypeName(b, m.Type, qualified, true)
|
||||
}
|
||||
if b[len(b)-1] == ' ' {
|
||||
b = b[:len(b)-1]
|
||||
} else {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
b = append(b, '}')
|
||||
default:
|
||||
panic("invalid kind: " + k.String())
|
||||
}
|
||||
return b
|
||||
}
|
10
vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
generated
vendored
10
vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
generated
vendored
|
@ -21,3 +21,13 @@ func PointerOf(v reflect.Value) Pointer {
|
|||
// assumes that the GC implementation does not use a moving collector.
|
||||
return Pointer{v.Pointer(), v.Type()}
|
||||
}
|
||||
|
||||
// IsNil reports whether the pointer is nil.
|
||||
func (p Pointer) IsNil() bool {
|
||||
return p.p == 0
|
||||
}
|
||||
|
||||
// Uintptr returns the pointer as a uintptr.
|
||||
func (p Pointer) Uintptr() uintptr {
|
||||
return p.p
|
||||
}
|
||||
|
|
10
vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go
generated
vendored
10
vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go
generated
vendored
|
@ -24,3 +24,13 @@ func PointerOf(v reflect.Value) Pointer {
|
|||
// which is necessary if the GC ever uses a moving collector.
|
||||
return Pointer{unsafe.Pointer(v.Pointer()), v.Type()}
|
||||
}
|
||||
|
||||
// IsNil reports whether the pointer is nil.
|
||||
func (p Pointer) IsNil() bool {
|
||||
return p.p == nil
|
||||
}
|
||||
|
||||
// Uintptr returns the pointer as a uintptr.
|
||||
func (p Pointer) Uintptr() uintptr {
|
||||
return uintptr(p.p)
|
||||
}
|
||||
|
|
7
vendor/github.com/google/go-cmp/cmp/path.go
generated
vendored
7
vendor/github.com/google/go-cmp/cmp/path.go
generated
vendored
|
@ -177,7 +177,8 @@ type structField struct {
|
|||
// pvx, pvy, and field are only valid if unexported is true.
|
||||
unexported bool
|
||||
mayForce bool // Forcibly allow visibility
|
||||
pvx, pvy reflect.Value // Parent values
|
||||
paddr bool // Was parent addressable?
|
||||
pvx, pvy reflect.Value // Parent values (always addressible)
|
||||
field reflect.StructField // Field information
|
||||
}
|
||||
|
||||
|
@ -189,8 +190,8 @@ func (sf StructField) Values() (vx, vy reflect.Value) {
|
|||
|
||||
// Forcibly obtain read-write access to an unexported struct field.
|
||||
if sf.mayForce {
|
||||
vx = retrieveUnexportedField(sf.pvx, sf.field)
|
||||
vy = retrieveUnexportedField(sf.pvy, sf.field)
|
||||
vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr)
|
||||
vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr)
|
||||
return vx, vy // CanInterface reports true
|
||||
}
|
||||
return sf.vx, sf.vy // CanInterface reports false
|
||||
|
|
5
vendor/github.com/google/go-cmp/cmp/report.go
generated
vendored
5
vendor/github.com/google/go-cmp/cmp/report.go
generated
vendored
|
@ -41,7 +41,10 @@ func (r *defaultReporter) String() string {
|
|||
if r.root.NumDiff == 0 {
|
||||
return ""
|
||||
}
|
||||
return formatOptions{}.FormatDiff(r.root).String()
|
||||
ptrs := new(pointerReferences)
|
||||
text := formatOptions{}.FormatDiff(r.root, ptrs)
|
||||
resolveReferences(text)
|
||||
return text.String()
|
||||
}
|
||||
|
||||
func assert(ok bool) {
|
||||
|
|
200
vendor/github.com/google/go-cmp/cmp/report_compare.go
generated
vendored
200
vendor/github.com/google/go-cmp/cmp/report_compare.go
generated
vendored
|
@ -11,14 +11,6 @@ import (
|
|||
"github.com/google/go-cmp/cmp/internal/value"
|
||||
)
|
||||
|
||||
// TODO: Enforce limits?
|
||||
// * Enforce maximum number of records to print per node?
|
||||
// * Enforce maximum size in bytes allowed?
|
||||
// * As a heuristic, use less verbosity for equal nodes than unequal nodes.
|
||||
// TODO: Enforce unique outputs?
|
||||
// * Avoid Stringer methods if it results in same output?
|
||||
// * Print pointer address if outputs still equal?
|
||||
|
||||
// numContextRecords is the number of surrounding equal records to print.
|
||||
const numContextRecords = 2
|
||||
|
||||
|
@ -71,24 +63,66 @@ func (opts formatOptions) WithTypeMode(t typeMode) formatOptions {
|
|||
opts.TypeMode = t
|
||||
return opts
|
||||
}
|
||||
func (opts formatOptions) WithVerbosity(level int) formatOptions {
|
||||
opts.VerbosityLevel = level
|
||||
opts.LimitVerbosity = true
|
||||
return opts
|
||||
}
|
||||
func (opts formatOptions) verbosity() uint {
|
||||
switch {
|
||||
case opts.VerbosityLevel < 0:
|
||||
return 0
|
||||
case opts.VerbosityLevel > 16:
|
||||
return 16 // some reasonable maximum to avoid shift overflow
|
||||
default:
|
||||
return uint(opts.VerbosityLevel)
|
||||
}
|
||||
}
|
||||
|
||||
const maxVerbosityPreset = 3
|
||||
|
||||
// verbosityPreset modifies the verbosity settings given an index
|
||||
// between 0 and maxVerbosityPreset, inclusive.
|
||||
func verbosityPreset(opts formatOptions, i int) formatOptions {
|
||||
opts.VerbosityLevel = int(opts.verbosity()) + 2*i
|
||||
if i > 0 {
|
||||
opts.AvoidStringer = true
|
||||
}
|
||||
if i >= maxVerbosityPreset {
|
||||
opts.PrintAddresses = true
|
||||
opts.QualifiedNames = true
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// FormatDiff converts a valueNode tree into a textNode tree, where the later
|
||||
// is a textual representation of the differences detected in the former.
|
||||
func (opts formatOptions) FormatDiff(v *valueNode) textNode {
|
||||
func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) {
|
||||
if opts.DiffMode == diffIdentical {
|
||||
opts = opts.WithVerbosity(1)
|
||||
} else {
|
||||
opts = opts.WithVerbosity(3)
|
||||
}
|
||||
|
||||
// Check whether we have specialized formatting for this node.
|
||||
// This is not necessary, but helpful for producing more readable outputs.
|
||||
if opts.CanFormatDiffSlice(v) {
|
||||
return opts.FormatDiffSlice(v)
|
||||
}
|
||||
|
||||
var parentKind reflect.Kind
|
||||
if v.parent != nil && v.parent.TransformerName == "" {
|
||||
parentKind = v.parent.Type.Kind()
|
||||
}
|
||||
|
||||
// For leaf nodes, format the value based on the reflect.Values alone.
|
||||
if v.MaxDepth == 0 {
|
||||
switch opts.DiffMode {
|
||||
case diffUnknown, diffIdentical:
|
||||
// Format Equal.
|
||||
if v.NumDiff == 0 {
|
||||
outx := opts.FormatValue(v.ValueX, visitedPointers{})
|
||||
outy := opts.FormatValue(v.ValueY, visitedPointers{})
|
||||
outx := opts.FormatValue(v.ValueX, parentKind, ptrs)
|
||||
outy := opts.FormatValue(v.ValueY, parentKind, ptrs)
|
||||
if v.NumIgnored > 0 && v.NumSame == 0 {
|
||||
return textEllipsis
|
||||
} else if outx.Len() < outy.Len() {
|
||||
|
@ -101,8 +135,13 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode {
|
|||
// Format unequal.
|
||||
assert(opts.DiffMode == diffUnknown)
|
||||
var list textList
|
||||
outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, visitedPointers{})
|
||||
outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, visitedPointers{})
|
||||
outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs)
|
||||
outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs)
|
||||
for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ {
|
||||
opts2 := verbosityPreset(opts, i).WithTypeMode(elideType)
|
||||
outx = opts2.FormatValue(v.ValueX, parentKind, ptrs)
|
||||
outy = opts2.FormatValue(v.ValueY, parentKind, ptrs)
|
||||
}
|
||||
if outx != nil {
|
||||
list = append(list, textRecord{Diff: '-', Value: outx})
|
||||
}
|
||||
|
@ -111,34 +150,57 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode {
|
|||
}
|
||||
return opts.WithTypeMode(emitType).FormatType(v.Type, list)
|
||||
case diffRemoved:
|
||||
return opts.FormatValue(v.ValueX, visitedPointers{})
|
||||
return opts.FormatValue(v.ValueX, parentKind, ptrs)
|
||||
case diffInserted:
|
||||
return opts.FormatValue(v.ValueY, visitedPointers{})
|
||||
return opts.FormatValue(v.ValueY, parentKind, ptrs)
|
||||
default:
|
||||
panic("invalid diff mode")
|
||||
}
|
||||
}
|
||||
|
||||
// Register slice element to support cycle detection.
|
||||
if parentKind == reflect.Slice {
|
||||
ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true)
|
||||
defer ptrs.Pop()
|
||||
defer func() { out = wrapTrunkReferences(ptrRefs, out) }()
|
||||
}
|
||||
|
||||
// Descend into the child value node.
|
||||
if v.TransformerName != "" {
|
||||
out := opts.WithTypeMode(emitType).FormatDiff(v.Value)
|
||||
out = textWrap{"Inverse(" + v.TransformerName + ", ", out, ")"}
|
||||
out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
|
||||
out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"}
|
||||
return opts.FormatType(v.Type, out)
|
||||
} else {
|
||||
switch k := v.Type.Kind(); k {
|
||||
case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map:
|
||||
return opts.FormatType(v.Type, opts.formatDiffList(v.Records, k))
|
||||
case reflect.Struct, reflect.Array, reflect.Slice:
|
||||
out = opts.formatDiffList(v.Records, k, ptrs)
|
||||
out = opts.FormatType(v.Type, out)
|
||||
case reflect.Map:
|
||||
// Register map to support cycle detection.
|
||||
ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false)
|
||||
defer ptrs.Pop()
|
||||
|
||||
out = opts.formatDiffList(v.Records, k, ptrs)
|
||||
out = wrapTrunkReferences(ptrRefs, out)
|
||||
out = opts.FormatType(v.Type, out)
|
||||
case reflect.Ptr:
|
||||
return textWrap{"&", opts.FormatDiff(v.Value), ""}
|
||||
// Register pointer to support cycle detection.
|
||||
ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false)
|
||||
defer ptrs.Pop()
|
||||
|
||||
out = opts.FormatDiff(v.Value, ptrs)
|
||||
out = wrapTrunkReferences(ptrRefs, out)
|
||||
out = &textWrap{Prefix: "&", Value: out}
|
||||
case reflect.Interface:
|
||||
return opts.WithTypeMode(emitType).FormatDiff(v.Value)
|
||||
out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
|
||||
default:
|
||||
panic(fmt.Sprintf("%v cannot have children", k))
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) textNode {
|
||||
func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode {
|
||||
// Derive record name based on the data structure kind.
|
||||
var name string
|
||||
var formatKey func(reflect.Value) string
|
||||
|
@ -154,7 +216,17 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
|||
case reflect.Map:
|
||||
name = "entry"
|
||||
opts = opts.WithTypeMode(elideType)
|
||||
formatKey = formatMapKey
|
||||
formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) }
|
||||
}
|
||||
|
||||
maxLen := -1
|
||||
if opts.LimitVerbosity {
|
||||
if opts.DiffMode == diffIdentical {
|
||||
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
|
||||
} else {
|
||||
maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc...
|
||||
}
|
||||
opts.VerbosityLevel--
|
||||
}
|
||||
|
||||
// Handle unification.
|
||||
|
@ -163,6 +235,11 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
|||
var list textList
|
||||
var deferredEllipsis bool // Add final "..." to indicate records were dropped
|
||||
for _, r := range recs {
|
||||
if len(list) == maxLen {
|
||||
deferredEllipsis = true
|
||||
break
|
||||
}
|
||||
|
||||
// Elide struct fields that are zero value.
|
||||
if k == reflect.Struct {
|
||||
var isZero bool
|
||||
|
@ -186,23 +263,31 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
|||
}
|
||||
continue
|
||||
}
|
||||
if out := opts.FormatDiff(r.Value); out != nil {
|
||||
if out := opts.FormatDiff(r.Value, ptrs); out != nil {
|
||||
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||
}
|
||||
}
|
||||
if deferredEllipsis {
|
||||
list.AppendEllipsis(diffStats{})
|
||||
}
|
||||
return textWrap{"{", list, "}"}
|
||||
return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||
case diffUnknown:
|
||||
default:
|
||||
panic("invalid diff mode")
|
||||
}
|
||||
|
||||
// Handle differencing.
|
||||
var numDiffs int
|
||||
var list textList
|
||||
var keys []reflect.Value // invariant: len(list) == len(keys)
|
||||
groups := coalesceAdjacentRecords(name, recs)
|
||||
maxGroup := diffStats{Name: name}
|
||||
for i, ds := range groups {
|
||||
if maxLen >= 0 && numDiffs >= maxLen {
|
||||
maxGroup = maxGroup.Append(ds)
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle equal records.
|
||||
if ds.NumDiff() == 0 {
|
||||
// Compute the number of leading and trailing records to print.
|
||||
|
@ -226,16 +311,21 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
|||
|
||||
// Format the equal values.
|
||||
for _, r := range recs[:numLo] {
|
||||
out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
|
||||
out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs)
|
||||
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||
keys = append(keys, r.Key)
|
||||
}
|
||||
if numEqual > numLo+numHi {
|
||||
ds.NumIdentical -= numLo + numHi
|
||||
list.AppendEllipsis(ds)
|
||||
for len(keys) < len(list) {
|
||||
keys = append(keys, reflect.Value{})
|
||||
}
|
||||
}
|
||||
for _, r := range recs[numEqual-numHi : numEqual] {
|
||||
out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
|
||||
out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs)
|
||||
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||
keys = append(keys, r.Key)
|
||||
}
|
||||
recs = recs[numEqual:]
|
||||
continue
|
||||
|
@ -247,24 +337,70 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
|||
case opts.CanFormatDiffSlice(r.Value):
|
||||
out := opts.FormatDiffSlice(r.Value)
|
||||
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||
keys = append(keys, r.Key)
|
||||
case r.Value.NumChildren == r.Value.MaxDepth:
|
||||
outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value)
|
||||
outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value)
|
||||
outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs)
|
||||
outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs)
|
||||
for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ {
|
||||
opts2 := verbosityPreset(opts, i)
|
||||
outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs)
|
||||
outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs)
|
||||
}
|
||||
if outx != nil {
|
||||
list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx})
|
||||
keys = append(keys, r.Key)
|
||||
}
|
||||
if outy != nil {
|
||||
list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy})
|
||||
keys = append(keys, r.Key)
|
||||
}
|
||||
default:
|
||||
out := opts.FormatDiff(r.Value)
|
||||
out := opts.FormatDiff(r.Value, ptrs)
|
||||
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||
keys = append(keys, r.Key)
|
||||
}
|
||||
}
|
||||
recs = recs[ds.NumDiff():]
|
||||
numDiffs += ds.NumDiff()
|
||||
}
|
||||
assert(len(recs) == 0)
|
||||
return textWrap{"{", list, "}"}
|
||||
if maxGroup.IsZero() {
|
||||
assert(len(recs) == 0)
|
||||
} else {
|
||||
list.AppendEllipsis(maxGroup)
|
||||
for len(keys) < len(list) {
|
||||
keys = append(keys, reflect.Value{})
|
||||
}
|
||||
}
|
||||
assert(len(list) == len(keys))
|
||||
|
||||
// For maps, the default formatting logic uses fmt.Stringer which may
|
||||
// produce ambiguous output. Avoid calling String to disambiguate.
|
||||
if k == reflect.Map {
|
||||
var ambiguous bool
|
||||
seenKeys := map[string]reflect.Value{}
|
||||
for i, currKey := range keys {
|
||||
if currKey.IsValid() {
|
||||
strKey := list[i].Key
|
||||
prevKey, seen := seenKeys[strKey]
|
||||
if seen && prevKey.CanInterface() && currKey.CanInterface() {
|
||||
ambiguous = prevKey.Interface() != currKey.Interface()
|
||||
if ambiguous {
|
||||
break
|
||||
}
|
||||
}
|
||||
seenKeys[strKey] = currKey
|
||||
}
|
||||
}
|
||||
if ambiguous {
|
||||
for i, k := range keys {
|
||||
if k.IsValid() {
|
||||
list[i].Key = formatMapKey(k, true, ptrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||
}
|
||||
|
||||
// coalesceAdjacentRecords coalesces the list of records into groups of
|
||||
|
|
264
vendor/github.com/google/go-cmp/cmp/report_references.go
generated
vendored
Normal file
264
vendor/github.com/google/go-cmp/cmp/report_references.go
generated
vendored
Normal file
|
@ -0,0 +1,264 @@
|
|||
// Copyright 2020, The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE.md file.
|
||||
|
||||
package cmp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-cmp/cmp/internal/flags"
|
||||
"github.com/google/go-cmp/cmp/internal/value"
|
||||
)
|
||||
|
||||
const (
|
||||
pointerDelimPrefix = "⟪"
|
||||
pointerDelimSuffix = "⟫"
|
||||
)
|
||||
|
||||
// formatPointer prints the address of the pointer.
|
||||
func formatPointer(p value.Pointer, withDelims bool) string {
|
||||
v := p.Uintptr()
|
||||
if flags.Deterministic {
|
||||
v = 0xdeadf00f // Only used for stable testing purposes
|
||||
}
|
||||
if withDelims {
|
||||
return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix
|
||||
}
|
||||
return formatHex(uint64(v))
|
||||
}
|
||||
|
||||
// pointerReferences is a stack of pointers visited so far.
|
||||
type pointerReferences [][2]value.Pointer
|
||||
|
||||
func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) {
|
||||
if deref && vx.IsValid() {
|
||||
vx = vx.Addr()
|
||||
}
|
||||
if deref && vy.IsValid() {
|
||||
vy = vy.Addr()
|
||||
}
|
||||
switch d {
|
||||
case diffUnknown, diffIdentical:
|
||||
pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)}
|
||||
case diffRemoved:
|
||||
pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}}
|
||||
case diffInserted:
|
||||
pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)}
|
||||
}
|
||||
*ps = append(*ps, pp)
|
||||
return pp
|
||||
}
|
||||
|
||||
func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) {
|
||||
p = value.PointerOf(v)
|
||||
for _, pp := range *ps {
|
||||
if p == pp[0] || p == pp[1] {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
*ps = append(*ps, [2]value.Pointer{p, p})
|
||||
return p, false
|
||||
}
|
||||
|
||||
func (ps *pointerReferences) Pop() {
|
||||
*ps = (*ps)[:len(*ps)-1]
|
||||
}
|
||||
|
||||
// trunkReferences is metadata for a textNode indicating that the sub-tree
|
||||
// represents the value for either pointer in a pair of references.
|
||||
type trunkReferences struct{ pp [2]value.Pointer }
|
||||
|
||||
// trunkReference is metadata for a textNode indicating that the sub-tree
|
||||
// represents the value for the given pointer reference.
|
||||
type trunkReference struct{ p value.Pointer }
|
||||
|
||||
// leafReference is metadata for a textNode indicating that the value is
|
||||
// truncated as it refers to another part of the tree (i.e., a trunk).
|
||||
type leafReference struct{ p value.Pointer }
|
||||
|
||||
func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode {
|
||||
switch {
|
||||
case pp[0].IsNil():
|
||||
return &textWrap{Value: s, Metadata: trunkReference{pp[1]}}
|
||||
case pp[1].IsNil():
|
||||
return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
|
||||
case pp[0] == pp[1]:
|
||||
return &textWrap{Value: s, Metadata: trunkReference{pp[0]}}
|
||||
default:
|
||||
return &textWrap{Value: s, Metadata: trunkReferences{pp}}
|
||||
}
|
||||
}
|
||||
func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode {
|
||||
var prefix string
|
||||
if printAddress {
|
||||
prefix = formatPointer(p, true)
|
||||
}
|
||||
return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}}
|
||||
}
|
||||
func makeLeafReference(p value.Pointer, printAddress bool) textNode {
|
||||
out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"}
|
||||
var prefix string
|
||||
if printAddress {
|
||||
prefix = formatPointer(p, true)
|
||||
}
|
||||
return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}}
|
||||
}
|
||||
|
||||
// resolveReferences walks the textNode tree searching for any leaf reference
|
||||
// metadata and resolves each against the corresponding trunk references.
|
||||
// Since pointer addresses in memory are not particularly readable to the user,
|
||||
// it replaces each pointer value with an arbitrary and unique reference ID.
|
||||
func resolveReferences(s textNode) {
|
||||
var walkNodes func(textNode, func(textNode))
|
||||
walkNodes = func(s textNode, f func(textNode)) {
|
||||
f(s)
|
||||
switch s := s.(type) {
|
||||
case *textWrap:
|
||||
walkNodes(s.Value, f)
|
||||
case textList:
|
||||
for _, r := range s {
|
||||
walkNodes(r.Value, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all trunks and leaves with reference metadata.
|
||||
var trunks, leaves []*textWrap
|
||||
walkNodes(s, func(s textNode) {
|
||||
if s, ok := s.(*textWrap); ok {
|
||||
switch s.Metadata.(type) {
|
||||
case leafReference:
|
||||
leaves = append(leaves, s)
|
||||
case trunkReference, trunkReferences:
|
||||
trunks = append(trunks, s)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// No leaf references to resolve.
|
||||
if len(leaves) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Collect the set of all leaf references to resolve.
|
||||
leafPtrs := make(map[value.Pointer]bool)
|
||||
for _, leaf := range leaves {
|
||||
leafPtrs[leaf.Metadata.(leafReference).p] = true
|
||||
}
|
||||
|
||||
// Collect the set of trunk pointers that are always paired together.
|
||||
// This allows us to assign a single ID to both pointers for brevity.
|
||||
// If a pointer in a pair ever occurs by itself or as a different pair,
|
||||
// then the pair is broken.
|
||||
pairedTrunkPtrs := make(map[value.Pointer]value.Pointer)
|
||||
unpair := func(p value.Pointer) {
|
||||
if !pairedTrunkPtrs[p].IsNil() {
|
||||
pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half
|
||||
}
|
||||
pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half
|
||||
}
|
||||
for _, trunk := range trunks {
|
||||
switch p := trunk.Metadata.(type) {
|
||||
case trunkReference:
|
||||
unpair(p.p) // standalone pointer cannot be part of a pair
|
||||
case trunkReferences:
|
||||
p0, ok0 := pairedTrunkPtrs[p.pp[0]]
|
||||
p1, ok1 := pairedTrunkPtrs[p.pp[1]]
|
||||
switch {
|
||||
case !ok0 && !ok1:
|
||||
// Register the newly seen pair.
|
||||
pairedTrunkPtrs[p.pp[0]] = p.pp[1]
|
||||
pairedTrunkPtrs[p.pp[1]] = p.pp[0]
|
||||
case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]:
|
||||
// Exact pair already seen; do nothing.
|
||||
default:
|
||||
// Pair conflicts with some other pair; break all pairs.
|
||||
unpair(p.pp[0])
|
||||
unpair(p.pp[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Correlate each pointer referenced by leaves to a unique identifier,
|
||||
// and print the IDs for each trunk that matches those pointers.
|
||||
var nextID uint
|
||||
ptrIDs := make(map[value.Pointer]uint)
|
||||
newID := func() uint {
|
||||
id := nextID
|
||||
nextID++
|
||||
return id
|
||||
}
|
||||
for _, trunk := range trunks {
|
||||
switch p := trunk.Metadata.(type) {
|
||||
case trunkReference:
|
||||
if print := leafPtrs[p.p]; print {
|
||||
id, ok := ptrIDs[p.p]
|
||||
if !ok {
|
||||
id = newID()
|
||||
ptrIDs[p.p] = id
|
||||
}
|
||||
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
|
||||
}
|
||||
case trunkReferences:
|
||||
print0 := leafPtrs[p.pp[0]]
|
||||
print1 := leafPtrs[p.pp[1]]
|
||||
if print0 || print1 {
|
||||
id0, ok0 := ptrIDs[p.pp[0]]
|
||||
id1, ok1 := ptrIDs[p.pp[1]]
|
||||
isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0]
|
||||
if isPair {
|
||||
var id uint
|
||||
assert(ok0 == ok1) // must be seen together or not at all
|
||||
if ok0 {
|
||||
assert(id0 == id1) // must have the same ID
|
||||
id = id0
|
||||
} else {
|
||||
id = newID()
|
||||
ptrIDs[p.pp[0]] = id
|
||||
ptrIDs[p.pp[1]] = id
|
||||
}
|
||||
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id))
|
||||
} else {
|
||||
if print0 && !ok0 {
|
||||
id0 = newID()
|
||||
ptrIDs[p.pp[0]] = id0
|
||||
}
|
||||
if print1 && !ok1 {
|
||||
id1 = newID()
|
||||
ptrIDs[p.pp[1]] = id1
|
||||
}
|
||||
switch {
|
||||
case print0 && print1:
|
||||
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1))
|
||||
case print0:
|
||||
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0))
|
||||
case print1:
|
||||
trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update all leaf references with the unique identifier.
|
||||
for _, leaf := range leaves {
|
||||
if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok {
|
||||
leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatReference(id uint) string {
|
||||
return fmt.Sprintf("ref#%d", id)
|
||||
}
|
||||
|
||||
func updateReferencePrefix(prefix, ref string) string {
|
||||
if prefix == "" {
|
||||
return pointerDelimPrefix + ref + pointerDelimSuffix
|
||||
}
|
||||
suffix := strings.TrimPrefix(prefix, pointerDelimPrefix)
|
||||
return pointerDelimPrefix + ref + ": " + suffix
|
||||
}
|
241
vendor/github.com/google/go-cmp/cmp/report_reflect.go
generated
vendored
241
vendor/github.com/google/go-cmp/cmp/report_reflect.go
generated
vendored
|
@ -10,8 +10,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/google/go-cmp/cmp/internal/flags"
|
||||
"github.com/google/go-cmp/cmp/internal/value"
|
||||
)
|
||||
|
||||
|
@ -20,14 +20,22 @@ type formatValueOptions struct {
|
|||
// methods like error.Error or fmt.Stringer.String.
|
||||
AvoidStringer bool
|
||||
|
||||
// ShallowPointers controls whether to avoid descending into pointers.
|
||||
// Useful when printing map keys, where pointer comparison is performed
|
||||
// on the pointer address rather than the pointed-at value.
|
||||
ShallowPointers bool
|
||||
|
||||
// PrintAddresses controls whether to print the address of all pointers,
|
||||
// slice elements, and maps.
|
||||
PrintAddresses bool
|
||||
|
||||
// QualifiedNames controls whether FormatType uses the fully qualified name
|
||||
// (including the full package path as opposed to just the package name).
|
||||
QualifiedNames bool
|
||||
|
||||
// VerbosityLevel controls the amount of output to produce.
|
||||
// A higher value produces more output. A value of zero or lower produces
|
||||
// no output (represented using an ellipsis).
|
||||
// If LimitVerbosity is false, then the level is treated as infinite.
|
||||
VerbosityLevel int
|
||||
|
||||
// LimitVerbosity specifies that formatting should respect VerbosityLevel.
|
||||
LimitVerbosity bool
|
||||
}
|
||||
|
||||
// FormatType prints the type as if it were wrapping s.
|
||||
|
@ -44,12 +52,15 @@ func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
|
|||
default:
|
||||
return s
|
||||
}
|
||||
if opts.DiffMode == diffIdentical {
|
||||
return s // elide type for identical nodes
|
||||
}
|
||||
case elideType:
|
||||
return s
|
||||
}
|
||||
|
||||
// Determine the type label, applying special handling for unnamed types.
|
||||
typeName := t.String()
|
||||
typeName := value.TypeString(t, opts.QualifiedNames)
|
||||
if t.Name() == "" {
|
||||
// According to Go grammar, certain type literals contain symbols that
|
||||
// do not strongly bind to the next lexicographical token (e.g., *T).
|
||||
|
@ -57,39 +68,78 @@ func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
|
|||
case reflect.Chan, reflect.Func, reflect.Ptr:
|
||||
typeName = "(" + typeName + ")"
|
||||
}
|
||||
typeName = strings.Replace(typeName, "struct {", "struct{", -1)
|
||||
typeName = strings.Replace(typeName, "interface {", "interface{", -1)
|
||||
}
|
||||
return &textWrap{Prefix: typeName, Value: wrapParens(s)}
|
||||
}
|
||||
|
||||
// Avoid wrap the value in parenthesis if unnecessary.
|
||||
if s, ok := s.(textWrap); ok {
|
||||
hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")")
|
||||
hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}")
|
||||
// wrapParens wraps s with a set of parenthesis, but avoids it if the
|
||||
// wrapped node itself is already surrounded by a pair of parenthesis or braces.
|
||||
// It handles unwrapping one level of pointer-reference nodes.
|
||||
func wrapParens(s textNode) textNode {
|
||||
var refNode *textWrap
|
||||
if s2, ok := s.(*textWrap); ok {
|
||||
// Unwrap a single pointer reference node.
|
||||
switch s2.Metadata.(type) {
|
||||
case leafReference, trunkReference, trunkReferences:
|
||||
refNode = s2
|
||||
if s3, ok := refNode.Value.(*textWrap); ok {
|
||||
s2 = s3
|
||||
}
|
||||
}
|
||||
|
||||
// Already has delimiters that make parenthesis unnecessary.
|
||||
hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")")
|
||||
hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}")
|
||||
if hasParens || hasBraces {
|
||||
return textWrap{typeName, s, ""}
|
||||
return s
|
||||
}
|
||||
}
|
||||
return textWrap{typeName + "(", s, ")"}
|
||||
if refNode != nil {
|
||||
refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"}
|
||||
return s
|
||||
}
|
||||
return &textWrap{Prefix: "(", Value: s, Suffix: ")"}
|
||||
}
|
||||
|
||||
// FormatValue prints the reflect.Value, taking extra care to avoid descending
|
||||
// into pointers already in m. As pointers are visited, m is also updated.
|
||||
func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
|
||||
// into pointers already in ptrs. As pointers are visited, ptrs is also updated.
|
||||
func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) {
|
||||
if !v.IsValid() {
|
||||
return nil
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
// Check slice element for cycles.
|
||||
if parentKind == reflect.Slice {
|
||||
ptrRef, visited := ptrs.Push(v.Addr())
|
||||
if visited {
|
||||
return makeLeafReference(ptrRef, false)
|
||||
}
|
||||
defer ptrs.Pop()
|
||||
defer func() { out = wrapTrunkReference(ptrRef, false, out) }()
|
||||
}
|
||||
|
||||
// Check whether there is an Error or String method to call.
|
||||
if !opts.AvoidStringer && v.CanInterface() {
|
||||
// Avoid calling Error or String methods on nil receivers since many
|
||||
// implementations crash when doing so.
|
||||
if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
|
||||
var prefix, strVal string
|
||||
switch v := v.Interface().(type) {
|
||||
case error:
|
||||
return textLine("e" + formatString(v.Error()))
|
||||
prefix, strVal = "e", v.Error()
|
||||
case fmt.Stringer:
|
||||
return textLine("s" + formatString(v.String()))
|
||||
prefix, strVal = "s", v.String()
|
||||
}
|
||||
if prefix != "" {
|
||||
maxLen := len(strVal)
|
||||
if opts.LimitVerbosity {
|
||||
maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc...
|
||||
}
|
||||
if len(strVal) > maxLen+len(textEllipsis) {
|
||||
return textLine(prefix + formatString(strVal[:maxLen]) + string(textEllipsis))
|
||||
}
|
||||
return textLine(prefix + formatString(strVal))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,94 +152,136 @@ func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out t
|
|||
}
|
||||
}()
|
||||
|
||||
var ptr string
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return textLine(fmt.Sprint(v.Bool()))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return textLine(fmt.Sprint(v.Int()))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
// Unnamed uints are usually bytes or words, so use hexadecimal.
|
||||
if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
|
||||
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return textLine(fmt.Sprint(v.Uint()))
|
||||
case reflect.Uint8:
|
||||
if parentKind == reflect.Slice || parentKind == reflect.Array {
|
||||
return textLine(formatHex(v.Uint()))
|
||||
}
|
||||
return textLine(fmt.Sprint(v.Uint()))
|
||||
case reflect.Uintptr:
|
||||
return textLine(formatHex(v.Uint()))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return textLine(fmt.Sprint(v.Float()))
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return textLine(fmt.Sprint(v.Complex()))
|
||||
case reflect.String:
|
||||
maxLen := v.Len()
|
||||
if opts.LimitVerbosity {
|
||||
maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc...
|
||||
}
|
||||
if v.Len() > maxLen+len(textEllipsis) {
|
||||
return textLine(formatString(v.String()[:maxLen]) + string(textEllipsis))
|
||||
}
|
||||
return textLine(formatString(v.String()))
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
return textLine(formatPointer(v))
|
||||
return textLine(formatPointer(value.PointerOf(v), true))
|
||||
case reflect.Struct:
|
||||
var list textList
|
||||
v := makeAddressable(v) // needed for retrieveUnexportedField
|
||||
maxLen := v.NumField()
|
||||
if opts.LimitVerbosity {
|
||||
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
|
||||
opts.VerbosityLevel--
|
||||
}
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
vv := v.Field(i)
|
||||
if value.IsZero(vv) {
|
||||
continue // Elide fields with zero values
|
||||
}
|
||||
s := opts.WithTypeMode(autoType).FormatValue(vv, m)
|
||||
list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
|
||||
if len(list) == maxLen {
|
||||
list.AppendEllipsis(diffStats{})
|
||||
break
|
||||
}
|
||||
sf := t.Field(i)
|
||||
if supportExporters && !isExported(sf.Name) {
|
||||
vv = retrieveUnexportedField(v, sf, true)
|
||||
}
|
||||
s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs)
|
||||
list = append(list, textRecord{Key: sf.Name, Value: s})
|
||||
}
|
||||
return textWrap{"{", list, "}"}
|
||||
return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
return textNil
|
||||
}
|
||||
if opts.PrintAddresses {
|
||||
ptr = formatPointer(v)
|
||||
}
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
maxLen := v.Len()
|
||||
if opts.LimitVerbosity {
|
||||
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
|
||||
opts.VerbosityLevel--
|
||||
}
|
||||
var list textList
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
vi := v.Index(i)
|
||||
if vi.CanAddr() { // Check for cyclic elements
|
||||
p := vi.Addr()
|
||||
if m.Visit(p) {
|
||||
var out textNode
|
||||
out = textLine(formatPointer(p))
|
||||
out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
|
||||
out = textWrap{"*", out, ""}
|
||||
list = append(list, textRecord{Value: out})
|
||||
continue
|
||||
}
|
||||
if len(list) == maxLen {
|
||||
list.AppendEllipsis(diffStats{})
|
||||
break
|
||||
}
|
||||
s := opts.WithTypeMode(elideType).FormatValue(vi, m)
|
||||
s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs)
|
||||
list = append(list, textRecord{Value: s})
|
||||
}
|
||||
return textWrap{ptr + "{", list, "}"}
|
||||
|
||||
out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||
if t.Kind() == reflect.Slice && opts.PrintAddresses {
|
||||
header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap())
|
||||
out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out}
|
||||
}
|
||||
return out
|
||||
case reflect.Map:
|
||||
if v.IsNil() {
|
||||
return textNil
|
||||
}
|
||||
if m.Visit(v) {
|
||||
return textLine(formatPointer(v))
|
||||
}
|
||||
|
||||
// Check pointer for cycles.
|
||||
ptrRef, visited := ptrs.Push(v)
|
||||
if visited {
|
||||
return makeLeafReference(ptrRef, opts.PrintAddresses)
|
||||
}
|
||||
defer ptrs.Pop()
|
||||
|
||||
maxLen := v.Len()
|
||||
if opts.LimitVerbosity {
|
||||
maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
|
||||
opts.VerbosityLevel--
|
||||
}
|
||||
var list textList
|
||||
for _, k := range value.SortKeys(v.MapKeys()) {
|
||||
sk := formatMapKey(k)
|
||||
sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
|
||||
if len(list) == maxLen {
|
||||
list.AppendEllipsis(diffStats{})
|
||||
break
|
||||
}
|
||||
sk := formatMapKey(k, false, ptrs)
|
||||
sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs)
|
||||
list = append(list, textRecord{Key: sk, Value: sv})
|
||||
}
|
||||
if opts.PrintAddresses {
|
||||
ptr = formatPointer(v)
|
||||
}
|
||||
return textWrap{ptr + "{", list, "}"}
|
||||
|
||||
out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||
out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
|
||||
return out
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
return textNil
|
||||
}
|
||||
if m.Visit(v) || opts.ShallowPointers {
|
||||
return textLine(formatPointer(v))
|
||||
}
|
||||
if opts.PrintAddresses {
|
||||
ptr = formatPointer(v)
|
||||
|
||||
// Check pointer for cycles.
|
||||
ptrRef, visited := ptrs.Push(v)
|
||||
if visited {
|
||||
out = makeLeafReference(ptrRef, opts.PrintAddresses)
|
||||
return &textWrap{Prefix: "&", Value: out}
|
||||
}
|
||||
defer ptrs.Pop()
|
||||
|
||||
skipType = true // Let the underlying value print the type instead
|
||||
return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
|
||||
out = opts.FormatValue(v.Elem(), t.Kind(), ptrs)
|
||||
out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
|
||||
out = &textWrap{Prefix: "&", Value: out}
|
||||
return out
|
||||
case reflect.Interface:
|
||||
if v.IsNil() {
|
||||
return textNil
|
||||
|
@ -197,7 +289,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out t
|
|||
// Interfaces accept different concrete types,
|
||||
// so configure the underlying value to explicitly print the type.
|
||||
skipType = true // Print the concrete type instead
|
||||
return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m)
|
||||
return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs)
|
||||
default:
|
||||
panic(fmt.Sprintf("%v kind not handled", v.Kind()))
|
||||
}
|
||||
|
@ -205,11 +297,14 @@ func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out t
|
|||
|
||||
// formatMapKey formats v as if it were a map key.
|
||||
// The result is guaranteed to be a single line.
|
||||
func formatMapKey(v reflect.Value) string {
|
||||
func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string {
|
||||
var opts formatOptions
|
||||
opts.DiffMode = diffIdentical
|
||||
opts.TypeMode = elideType
|
||||
opts.ShallowPointers = true
|
||||
s := opts.FormatValue(v, visitedPointers{}).String()
|
||||
opts.PrintAddresses = disambiguate
|
||||
opts.AvoidStringer = disambiguate
|
||||
opts.QualifiedNames = disambiguate
|
||||
s := opts.FormatValue(v, reflect.Map, ptrs).String()
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
|
@ -227,7 +322,7 @@ func formatString(s string) string {
|
|||
rawInvalid := func(r rune) bool {
|
||||
return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
|
||||
}
|
||||
if strings.IndexFunc(s, rawInvalid) < 0 {
|
||||
if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 {
|
||||
return "`" + s + "`"
|
||||
}
|
||||
return qs
|
||||
|
@ -256,23 +351,3 @@ func formatHex(u uint64) string {
|
|||
}
|
||||
return fmt.Sprintf(f, u)
|
||||
}
|
||||
|
||||
// formatPointer prints the address of the pointer.
|
||||
func formatPointer(v reflect.Value) string {
|
||||
p := v.Pointer()
|
||||
if flags.Deterministic {
|
||||
p = 0xdeadf00f // Only used for stable testing purposes
|
||||
}
|
||||
return fmt.Sprintf("⟪0x%x⟫", p)
|
||||
}
|
||||
|
||||
type visitedPointers map[value.Pointer]struct{}
|
||||
|
||||
// Visit inserts pointer v into the visited map and reports whether it had
|
||||
// already been visited before.
|
||||
func (m visitedPointers) Visit(v reflect.Value) bool {
|
||||
p := value.PointerOf(v)
|
||||
_, visited := m[p]
|
||||
m[p] = struct{}{}
|
||||
return visited
|
||||
}
|
||||
|
|
135
vendor/github.com/google/go-cmp/cmp/report_slices.go
generated
vendored
135
vendor/github.com/google/go-cmp/cmp/report_slices.go
generated
vendored
|
@ -8,6 +8,7 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
@ -23,11 +24,25 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
|
|||
return false // Must be formatting in diff mode
|
||||
case v.NumDiff == 0:
|
||||
return false // No differences detected
|
||||
case v.NumIgnored+v.NumCompared+v.NumTransformed > 0:
|
||||
// TODO: Handle the case where someone uses bytes.Equal on a large slice.
|
||||
return false // Some custom option was used to determined equality
|
||||
case !v.ValueX.IsValid() || !v.ValueY.IsValid():
|
||||
return false // Both values must be valid
|
||||
case v.Type.Kind() == reflect.Slice && (v.ValueX.Len() == 0 || v.ValueY.Len() == 0):
|
||||
return false // Both slice values have to be non-empty
|
||||
case v.NumIgnored > 0:
|
||||
return false // Some ignore option was used
|
||||
case v.NumTransformed > 0:
|
||||
return false // Some transform option was used
|
||||
case v.NumCompared > 1:
|
||||
return false // More than one comparison was used
|
||||
case v.NumCompared == 1 && v.Type.Name() != "":
|
||||
// The need for cmp to check applicability of options on every element
|
||||
// in a slice is a significant performance detriment for large []byte.
|
||||
// The workaround is to specify Comparer(bytes.Equal),
|
||||
// which enables cmp to compare []byte more efficiently.
|
||||
// If they differ, we still want to provide batched diffing.
|
||||
// The logic disallows named types since they tend to have their own
|
||||
// String method, with nicer formatting than what this provides.
|
||||
return false
|
||||
}
|
||||
|
||||
switch t := v.Type; t.Kind() {
|
||||
|
@ -82,7 +97,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
}
|
||||
if isText || isBinary {
|
||||
var numLines, lastLineIdx, maxLineLen int
|
||||
isBinary = false
|
||||
isBinary = !utf8.ValidString(sx) || !utf8.ValidString(sy)
|
||||
for i, r := range sx + sy {
|
||||
if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError {
|
||||
isBinary = true
|
||||
|
@ -97,7 +112,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
}
|
||||
}
|
||||
isText = !isBinary
|
||||
isLinedText = isText && numLines >= 4 && maxLineLen <= 256
|
||||
isLinedText = isText && numLines >= 4 && maxLineLen <= 1024
|
||||
}
|
||||
|
||||
// Format the string into printable records.
|
||||
|
@ -117,6 +132,83 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
},
|
||||
)
|
||||
delim = "\n"
|
||||
|
||||
// If possible, use a custom triple-quote (""") syntax for printing
|
||||
// differences in a string literal. This format is more readable,
|
||||
// but has edge-cases where differences are visually indistinguishable.
|
||||
// This format is avoided under the following conditions:
|
||||
// • A line starts with `"""`
|
||||
// • A line starts with "..."
|
||||
// • A line contains non-printable characters
|
||||
// • Adjacent different lines differ only by whitespace
|
||||
//
|
||||
// For example:
|
||||
// """
|
||||
// ... // 3 identical lines
|
||||
// foo
|
||||
// bar
|
||||
// - baz
|
||||
// + BAZ
|
||||
// """
|
||||
isTripleQuoted := true
|
||||
prevRemoveLines := map[string]bool{}
|
||||
prevInsertLines := map[string]bool{}
|
||||
var list2 textList
|
||||
list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true})
|
||||
for _, r := range list {
|
||||
if !r.Value.Equal(textEllipsis) {
|
||||
line, _ := strconv.Unquote(string(r.Value.(textLine)))
|
||||
line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
|
||||
normLine := strings.Map(func(r rune) rune {
|
||||
if unicode.IsSpace(r) {
|
||||
return -1 // drop whitespace to avoid visually indistinguishable output
|
||||
}
|
||||
return r
|
||||
}, line)
|
||||
isPrintable := func(r rune) bool {
|
||||
return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable
|
||||
}
|
||||
isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == ""
|
||||
switch r.Diff {
|
||||
case diffRemoved:
|
||||
isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine]
|
||||
prevRemoveLines[normLine] = true
|
||||
case diffInserted:
|
||||
isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine]
|
||||
prevInsertLines[normLine] = true
|
||||
}
|
||||
if !isTripleQuoted {
|
||||
break
|
||||
}
|
||||
r.Value = textLine(line)
|
||||
r.ElideComma = true
|
||||
}
|
||||
if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group
|
||||
prevRemoveLines = map[string]bool{}
|
||||
prevInsertLines = map[string]bool{}
|
||||
}
|
||||
list2 = append(list2, r)
|
||||
}
|
||||
if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 {
|
||||
list2 = list2[:len(list2)-1] // elide single empty line at the end
|
||||
}
|
||||
list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true})
|
||||
if isTripleQuoted {
|
||||
var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"}
|
||||
switch t.Kind() {
|
||||
case reflect.String:
|
||||
if t != reflect.TypeOf(string("")) {
|
||||
out = opts.FormatType(t, out)
|
||||
}
|
||||
case reflect.Slice:
|
||||
// Always emit type for slices since the triple-quote syntax
|
||||
// looks like a string (not a slice).
|
||||
opts = opts.WithTypeMode(emitType)
|
||||
out = opts.FormatType(t, out)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// If the text appears to be single-lined text,
|
||||
// then perform differencing in approximately fixed-sized chunks.
|
||||
// The output is printed as quoted strings.
|
||||
|
@ -129,6 +221,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
},
|
||||
)
|
||||
delim = ""
|
||||
|
||||
// If the text appears to be binary data,
|
||||
// then perform differencing in approximately fixed-sized chunks.
|
||||
// The output is inspired by hexdump.
|
||||
|
@ -145,6 +238,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
return textRecord{Diff: d, Value: textLine(s), Comment: comment}
|
||||
},
|
||||
)
|
||||
|
||||
// For all other slices of primitive types,
|
||||
// then perform differencing in approximately fixed-sized chunks.
|
||||
// The size of each chunk depends on the width of the element kind.
|
||||
|
@ -172,7 +266,9 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
switch t.Elem().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
ss = append(ss, fmt.Sprint(v.Index(i).Int()))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
ss = append(ss, fmt.Sprint(v.Index(i).Uint()))
|
||||
case reflect.Uint8, reflect.Uintptr:
|
||||
ss = append(ss, formatHex(v.Index(i).Uint()))
|
||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
|
||||
ss = append(ss, fmt.Sprint(v.Index(i).Interface()))
|
||||
|
@ -185,7 +281,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
}
|
||||
|
||||
// Wrap the output with appropriate type information.
|
||||
var out textNode = textWrap{"{", list, "}"}
|
||||
var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||
if !isText {
|
||||
// The "{...}" byte-sequence literal is not valid Go syntax for strings.
|
||||
// Emit the type for extra clarity (e.g. "string{...}").
|
||||
|
@ -196,12 +292,12 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
|||
}
|
||||
switch t.Kind() {
|
||||
case reflect.String:
|
||||
out = textWrap{"strings.Join(", out, fmt.Sprintf(", %q)", delim)}
|
||||
out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)}
|
||||
if t != reflect.TypeOf(string("")) {
|
||||
out = opts.FormatType(t, out)
|
||||
}
|
||||
case reflect.Slice:
|
||||
out = textWrap{"bytes.Join(", out, fmt.Sprintf(", %q)", delim)}
|
||||
out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)}
|
||||
if t != reflect.TypeOf([]byte(nil)) {
|
||||
out = opts.FormatType(t, out)
|
||||
}
|
||||
|
@ -242,9 +338,22 @@ func (opts formatOptions) formatDiffSlice(
|
|||
return n0 - v.Len()
|
||||
}
|
||||
|
||||
var numDiffs int
|
||||
maxLen := -1
|
||||
if opts.LimitVerbosity {
|
||||
maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc...
|
||||
opts.VerbosityLevel--
|
||||
}
|
||||
|
||||
groups := coalesceAdjacentEdits(name, es)
|
||||
groups = coalesceInterveningIdentical(groups, chunkSize/4)
|
||||
maxGroup := diffStats{Name: name}
|
||||
for i, ds := range groups {
|
||||
if maxLen >= 0 && numDiffs >= maxLen {
|
||||
maxGroup = maxGroup.Append(ds)
|
||||
continue
|
||||
}
|
||||
|
||||
// Print equal.
|
||||
if ds.NumDiff() == 0 {
|
||||
// Compute the number of leading and trailing equal bytes to print.
|
||||
|
@ -273,12 +382,18 @@ func (opts formatOptions) formatDiffSlice(
|
|||
}
|
||||
|
||||
// Print unequal.
|
||||
len0 := len(list)
|
||||
nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved)
|
||||
vx = vx.Slice(nx, vx.Len())
|
||||
ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted)
|
||||
vy = vy.Slice(ny, vy.Len())
|
||||
numDiffs += len(list) - len0
|
||||
}
|
||||
if maxGroup.IsZero() {
|
||||
assert(vx.Len() == 0 && vy.Len() == 0)
|
||||
} else {
|
||||
list.AppendEllipsis(maxGroup)
|
||||
}
|
||||
assert(vx.Len() == 0 && vy.Len() == 0)
|
||||
return list
|
||||
}
|
||||
|
||||
|
|
86
vendor/github.com/google/go-cmp/cmp/report_text.go
generated
vendored
86
vendor/github.com/google/go-cmp/cmp/report_text.go
generated
vendored
|
@ -10,12 +10,15 @@ import (
|
|||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/google/go-cmp/cmp/internal/flags"
|
||||
)
|
||||
|
||||
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
|
||||
|
||||
const maxColumnLength = 80
|
||||
|
||||
type indentMode int
|
||||
|
||||
func (n indentMode) appendIndent(b []byte, d diffMode) []byte {
|
||||
|
@ -91,21 +94,22 @@ type textNode interface {
|
|||
// textWrap is a wrapper that concatenates a prefix and/or a suffix
|
||||
// to the underlying node.
|
||||
type textWrap struct {
|
||||
Prefix string // e.g., "bytes.Buffer{"
|
||||
Value textNode // textWrap | textList | textLine
|
||||
Suffix string // e.g., "}"
|
||||
Prefix string // e.g., "bytes.Buffer{"
|
||||
Value textNode // textWrap | textList | textLine
|
||||
Suffix string // e.g., "}"
|
||||
Metadata interface{} // arbitrary metadata; has no effect on formatting
|
||||
}
|
||||
|
||||
func (s textWrap) Len() int {
|
||||
func (s *textWrap) Len() int {
|
||||
return len(s.Prefix) + s.Value.Len() + len(s.Suffix)
|
||||
}
|
||||
func (s1 textWrap) Equal(s2 textNode) bool {
|
||||
if s2, ok := s2.(textWrap); ok {
|
||||
func (s1 *textWrap) Equal(s2 textNode) bool {
|
||||
if s2, ok := s2.(*textWrap); ok {
|
||||
return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (s textWrap) String() string {
|
||||
func (s *textWrap) String() string {
|
||||
var d diffMode
|
||||
var n indentMode
|
||||
_, s2 := s.formatCompactTo(nil, d)
|
||||
|
@ -114,7 +118,7 @@ func (s textWrap) String() string {
|
|||
b = append(b, '\n') // Trailing newline
|
||||
return string(b)
|
||||
}
|
||||
func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
|
||||
func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
|
||||
n0 := len(b) // Original buffer length
|
||||
b = append(b, s.Prefix...)
|
||||
b, s.Value = s.Value.formatCompactTo(b, d)
|
||||
|
@ -124,7 +128,7 @@ func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
|
|||
}
|
||||
return b, s
|
||||
}
|
||||
func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
|
||||
func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
|
||||
b = append(b, s.Prefix...)
|
||||
b = s.Value.formatExpandedTo(b, d, n)
|
||||
b = append(b, s.Suffix...)
|
||||
|
@ -136,22 +140,23 @@ func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
|
|||
// of the textList.formatCompactTo method.
|
||||
type textList []textRecord
|
||||
type textRecord struct {
|
||||
Diff diffMode // e.g., 0 or '-' or '+'
|
||||
Key string // e.g., "MyField"
|
||||
Value textNode // textWrap | textLine
|
||||
Comment fmt.Stringer // e.g., "6 identical fields"
|
||||
Diff diffMode // e.g., 0 or '-' or '+'
|
||||
Key string // e.g., "MyField"
|
||||
Value textNode // textWrap | textLine
|
||||
ElideComma bool // avoid trailing comma
|
||||
Comment fmt.Stringer // e.g., "6 identical fields"
|
||||
}
|
||||
|
||||
// AppendEllipsis appends a new ellipsis node to the list if none already
|
||||
// exists at the end. If cs is non-zero it coalesces the statistics with the
|
||||
// previous diffStats.
|
||||
func (s *textList) AppendEllipsis(ds diffStats) {
|
||||
hasStats := ds != diffStats{}
|
||||
hasStats := !ds.IsZero()
|
||||
if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) {
|
||||
if hasStats {
|
||||
*s = append(*s, textRecord{Value: textEllipsis, Comment: ds})
|
||||
*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds})
|
||||
} else {
|
||||
*s = append(*s, textRecord{Value: textEllipsis})
|
||||
*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -191,7 +196,7 @@ func (s1 textList) Equal(s2 textNode) bool {
|
|||
}
|
||||
|
||||
func (s textList) String() string {
|
||||
return textWrap{"{", s, "}"}.String()
|
||||
return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String()
|
||||
}
|
||||
|
||||
func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
|
||||
|
@ -221,7 +226,7 @@ func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
|
|||
}
|
||||
// Force multi-lined output when printing a removed/inserted node that
|
||||
// is sufficiently long.
|
||||
if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > 80 {
|
||||
if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength {
|
||||
multiLine = true
|
||||
}
|
||||
if !multiLine {
|
||||
|
@ -236,16 +241,50 @@ func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
|
|||
_, isLine := r.Value.(textLine)
|
||||
return r.Key == "" || !isLine
|
||||
},
|
||||
func(r textRecord) int { return len(r.Key) },
|
||||
func(r textRecord) int { return utf8.RuneCountInString(r.Key) },
|
||||
)
|
||||
alignValueLens := s.alignLens(
|
||||
func(r textRecord) bool {
|
||||
_, isLine := r.Value.(textLine)
|
||||
return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil
|
||||
},
|
||||
func(r textRecord) int { return len(r.Value.(textLine)) },
|
||||
func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) },
|
||||
)
|
||||
|
||||
// Format lists of simple lists in a batched form.
|
||||
// If the list is sequence of only textLine values,
|
||||
// then batch multiple values on a single line.
|
||||
var isSimple bool
|
||||
for _, r := range s {
|
||||
_, isLine := r.Value.(textLine)
|
||||
isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil
|
||||
if !isSimple {
|
||||
break
|
||||
}
|
||||
}
|
||||
if isSimple {
|
||||
n++
|
||||
var batch []byte
|
||||
emitBatch := func() {
|
||||
if len(batch) > 0 {
|
||||
b = n.appendIndent(append(b, '\n'), d)
|
||||
b = append(b, bytes.TrimRight(batch, " ")...)
|
||||
batch = batch[:0]
|
||||
}
|
||||
}
|
||||
for _, r := range s {
|
||||
line := r.Value.(textLine)
|
||||
if len(batch)+len(line)+len(", ") > maxColumnLength {
|
||||
emitBatch()
|
||||
}
|
||||
batch = append(batch, line...)
|
||||
batch = append(batch, ", "...)
|
||||
}
|
||||
emitBatch()
|
||||
n--
|
||||
return n.appendIndent(append(b, '\n'), d)
|
||||
}
|
||||
|
||||
// Format the list as a multi-lined output.
|
||||
n++
|
||||
for i, r := range s {
|
||||
|
@ -256,7 +295,7 @@ func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
|
|||
b = alignKeyLens[i].appendChar(b, ' ')
|
||||
|
||||
b = r.Value.formatExpandedTo(b, d|r.Diff, n)
|
||||
if !r.Value.Equal(textEllipsis) {
|
||||
if !r.ElideComma {
|
||||
b = append(b, ',')
|
||||
}
|
||||
b = alignValueLens[i].appendChar(b, ' ')
|
||||
|
@ -332,6 +371,11 @@ type diffStats struct {
|
|||
NumModified int
|
||||
}
|
||||
|
||||
func (s diffStats) IsZero() bool {
|
||||
s.Name = ""
|
||||
return s == diffStats{}
|
||||
}
|
||||
|
||||
func (s diffStats) NumDiff() int {
|
||||
return s.NumRemoved + s.NumInserted + s.NumModified
|
||||
}
|
||||
|
|
21
vendor/github.com/hetznercloud/hcloud-go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/hetznercloud/hcloud-go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018-2020 Hetzner Cloud GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
210
vendor/github.com/hetznercloud/hcloud-go/hcloud/action.go
generated
vendored
Normal file
210
vendor/github.com/hetznercloud/hcloud-go/hcloud/action.go
generated
vendored
Normal file
|
@ -0,0 +1,210 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Action represents an action in the Hetzner Cloud.
|
||||
type Action struct {
|
||||
ID int
|
||||
Status ActionStatus
|
||||
Command string
|
||||
Progress int
|
||||
Started time.Time
|
||||
Finished time.Time
|
||||
ErrorCode string
|
||||
ErrorMessage string
|
||||
Resources []*ActionResource
|
||||
}
|
||||
|
||||
// ActionStatus represents an action's status.
|
||||
type ActionStatus string
|
||||
|
||||
// List of action statuses.
|
||||
const (
|
||||
ActionStatusRunning ActionStatus = "running"
|
||||
ActionStatusSuccess ActionStatus = "success"
|
||||
ActionStatusError ActionStatus = "error"
|
||||
)
|
||||
|
||||
// ActionResource references other resources from an action.
|
||||
type ActionResource struct {
|
||||
ID int
|
||||
Type ActionResourceType
|
||||
}
|
||||
|
||||
// ActionResourceType represents an action's resource reference type.
|
||||
type ActionResourceType string
|
||||
|
||||
// List of action resource reference types.
|
||||
const (
|
||||
ActionResourceTypeServer ActionResourceType = "server"
|
||||
ActionResourceTypeImage ActionResourceType = "image"
|
||||
ActionResourceTypeISO ActionResourceType = "iso"
|
||||
ActionResourceTypeFloatingIP ActionResourceType = "floating_ip"
|
||||
ActionResourceTypeVolume ActionResourceType = "volume"
|
||||
)
|
||||
|
||||
// ActionError is the error of an action.
|
||||
type ActionError struct {
|
||||
Code string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e ActionError) Error() string {
|
||||
return fmt.Sprintf("%s (%s)", e.Message, e.Code)
|
||||
}
|
||||
|
||||
func (a *Action) Error() error {
|
||||
if a.ErrorCode != "" && a.ErrorMessage != "" {
|
||||
return ActionError{
|
||||
Code: a.ErrorCode,
|
||||
Message: a.ErrorMessage,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ActionClient is a client for the actions API.
|
||||
type ActionClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves an action by its ID. If the action does not exist, nil is returned.
|
||||
func (c *ActionClient) GetByID(ctx context.Context, id int) (*Action, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/actions/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ActionGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return ActionFromSchema(body.Action), resp, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *ActionClient) List(ctx context.Context, opts ActionListOpts) ([]*Action, *Response, error) {
|
||||
path := "/actions?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ActionListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
actions := make([]*Action, 0, len(body.Actions))
|
||||
for _, i := range body.Actions {
|
||||
actions = append(actions, ActionFromSchema(i))
|
||||
}
|
||||
return actions, resp, nil
|
||||
}
|
||||
|
||||
// All returns all actions.
|
||||
func (c *ActionClient) All(ctx context.Context) ([]*Action, error) {
|
||||
allActions := []*Action{}
|
||||
|
||||
opts := ActionListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
actions, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allActions = append(allActions, actions...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allActions, nil
|
||||
}
|
||||
|
||||
// WatchProgress watches the action's progress until it completes with success or error.
|
||||
func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-chan int, <-chan error) {
|
||||
errCh := make(chan error, 1)
|
||||
progressCh := make(chan int)
|
||||
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
defer close(progressCh)
|
||||
|
||||
ticker := time.NewTicker(c.client.pollInterval)
|
||||
sendProgress := func(p int) {
|
||||
select {
|
||||
case progressCh <- p:
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errCh <- ctx.Err()
|
||||
return
|
||||
case <-ticker.C:
|
||||
break
|
||||
}
|
||||
|
||||
a, _, err := c.GetByID(ctx, action.ID)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
switch a.Status {
|
||||
case ActionStatusRunning:
|
||||
sendProgress(a.Progress)
|
||||
break
|
||||
case ActionStatusSuccess:
|
||||
sendProgress(100)
|
||||
errCh <- nil
|
||||
return
|
||||
case ActionStatusError:
|
||||
errCh <- a.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return progressCh, errCh
|
||||
}
|
246
vendor/github.com/hetznercloud/hcloud-go/hcloud/certificate.go
generated
vendored
Normal file
246
vendor/github.com/hetznercloud/hcloud-go/hcloud/certificate.go
generated
vendored
Normal file
|
@ -0,0 +1,246 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Certificate represents an certificate in the Hetzner Cloud.
|
||||
type Certificate struct {
|
||||
ID int
|
||||
Name string
|
||||
Labels map[string]string
|
||||
Certificate string
|
||||
Created time.Time
|
||||
NotValidBefore time.Time
|
||||
NotValidAfter time.Time
|
||||
DomainNames []string
|
||||
Fingerprint string
|
||||
}
|
||||
|
||||
// CertificateClient is a client for the Certificates API.
|
||||
type CertificateClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a Certificate by its ID. If the Certificate does not exist, nil is returned.
|
||||
func (c *CertificateClient) GetByID(ctx context.Context, id int) (*Certificate, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/certificates/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.CertificateGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return CertificateFromSchema(body.Certificate), resp, nil
|
||||
}
|
||||
|
||||
// GetByName retrieves a Certificate by its name. If the Certificate does not exist, nil is returned.
|
||||
func (c *CertificateClient) GetByName(ctx context.Context, name string) (*Certificate, *Response, error) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
Certificate, response, err := c.List(ctx, CertificateListOpts{Name: name})
|
||||
if len(Certificate) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return Certificate[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a Certificate by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a Certificate by its name. If the Certificate does not exist, nil is returned.
|
||||
func (c *CertificateClient) Get(ctx context.Context, idOrName string) (*Certificate, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// CertificateListOpts specifies options for listing Certificates.
|
||||
type CertificateListOpts struct {
|
||||
ListOpts
|
||||
Name string
|
||||
}
|
||||
|
||||
func (l CertificateListOpts) values() url.Values {
|
||||
vals := l.ListOpts.values()
|
||||
if l.Name != "" {
|
||||
vals.Add("name", l.Name)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// List returns a list of Certificates for a specific page.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *CertificateClient) List(ctx context.Context, opts CertificateListOpts) ([]*Certificate, *Response, error) {
|
||||
path := "/certificates?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.CertificateListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
Certificates := make([]*Certificate, 0, len(body.Certificates))
|
||||
for _, s := range body.Certificates {
|
||||
Certificates = append(Certificates, CertificateFromSchema(s))
|
||||
}
|
||||
return Certificates, resp, nil
|
||||
}
|
||||
|
||||
// All returns all Certificates.
|
||||
func (c *CertificateClient) All(ctx context.Context) ([]*Certificate, error) {
|
||||
allCertificates := []*Certificate{}
|
||||
|
||||
opts := CertificateListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
Certificate, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allCertificates = append(allCertificates, Certificate...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allCertificates, nil
|
||||
}
|
||||
|
||||
// AllWithOpts returns all Certificates for the given options.
|
||||
func (c *CertificateClient) AllWithOpts(ctx context.Context, opts CertificateListOpts) ([]*Certificate, error) {
|
||||
var allCertificates []*Certificate
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
Certificates, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allCertificates = append(allCertificates, Certificates...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allCertificates, nil
|
||||
}
|
||||
|
||||
// CertificateCreateOpts specifies options for creating a new Certificate.
|
||||
type CertificateCreateOpts struct {
|
||||
Name string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Validate checks if options are valid.
|
||||
func (o CertificateCreateOpts) Validate() error {
|
||||
if o.Name == "" {
|
||||
return errors.New("missing name")
|
||||
}
|
||||
if o.Certificate == "" {
|
||||
return errors.New("missing certificate")
|
||||
}
|
||||
if o.PrivateKey == "" {
|
||||
return errors.New("missing private key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create creates a new certificate.
|
||||
func (c *CertificateClient) Create(ctx context.Context, opts CertificateCreateOpts) (*Certificate, *Response, error) {
|
||||
if err := opts.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
reqBody := schema.CertificateCreateRequest{
|
||||
Name: opts.Name,
|
||||
Certificate: opts.Certificate,
|
||||
PrivateKey: opts.PrivateKey,
|
||||
}
|
||||
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", "/certificates", bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.CertificateCreateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return CertificateFromSchema(respBody.Certificate), resp, nil
|
||||
}
|
||||
|
||||
// CertificateUpdateOpts specifies options for updating a Certificate.
|
||||
type CertificateUpdateOpts struct {
|
||||
Name string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Update updates a Certificate.
|
||||
func (c *CertificateClient) Update(ctx context.Context, certificate *Certificate, opts CertificateUpdateOpts) (*Certificate, *Response, error) {
|
||||
reqBody := schema.CertificateUpdateRequest{}
|
||||
if opts.Name != "" {
|
||||
reqBody.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("/certificates/%d", certificate.ID)
|
||||
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.CertificateUpdateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return CertificateFromSchema(respBody.Certificate), resp, nil
|
||||
}
|
||||
|
||||
// Delete deletes a certificate.
|
||||
func (c *CertificateClient) Delete(ctx context.Context, certificate *Certificate) (*Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/certificates/%d", certificate.ID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Do(req, nil)
|
||||
}
|
394
vendor/github.com/hetznercloud/hcloud-go/hcloud/client.go
generated
vendored
Normal file
394
vendor/github.com/hetznercloud/hcloud-go/hcloud/client.go
generated
vendored
Normal file
|
@ -0,0 +1,394 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Endpoint is the base URL of the API.
|
||||
const Endpoint = "https://api.hetzner.cloud/v1"
|
||||
|
||||
// UserAgent is the value for the library part of the User-Agent header
|
||||
// that is sent with each request.
|
||||
const UserAgent = "hcloud-go/" + Version
|
||||
|
||||
// A BackoffFunc returns the duration to wait before performing the
|
||||
// next retry. The retries argument specifies how many retries have
|
||||
// already been performed. When called for the first time, retries is 0.
|
||||
type BackoffFunc func(retries int) time.Duration
|
||||
|
||||
// ConstantBackoff returns a BackoffFunc which backs off for
|
||||
// constant duration d.
|
||||
func ConstantBackoff(d time.Duration) BackoffFunc {
|
||||
return func(_ int) time.Duration {
|
||||
return d
|
||||
}
|
||||
}
|
||||
|
||||
// ExponentialBackoff returns a BackoffFunc which implements an exponential
|
||||
// backoff using the formula: b^retries * d
|
||||
func ExponentialBackoff(b float64, d time.Duration) BackoffFunc {
|
||||
return func(retries int) time.Duration {
|
||||
return time.Duration(math.Pow(b, float64(retries))) * d
|
||||
}
|
||||
}
|
||||
|
||||
// Client is a client for the Hetzner Cloud API.
|
||||
type Client struct {
|
||||
endpoint string
|
||||
token string
|
||||
pollInterval time.Duration
|
||||
backoffFunc BackoffFunc
|
||||
httpClient *http.Client
|
||||
applicationName string
|
||||
applicationVersion string
|
||||
userAgent string
|
||||
debugWriter io.Writer
|
||||
|
||||
Action ActionClient
|
||||
Certificate CertificateClient
|
||||
Datacenter DatacenterClient
|
||||
FloatingIP FloatingIPClient
|
||||
Image ImageClient
|
||||
ISO ISOClient
|
||||
LoadBalancer LoadBalancerClient
|
||||
LoadBalancerType LoadBalancerTypeClient
|
||||
Location LocationClient
|
||||
Network NetworkClient
|
||||
Pricing PricingClient
|
||||
Server ServerClient
|
||||
ServerType ServerTypeClient
|
||||
SSHKey SSHKeyClient
|
||||
Volume VolumeClient
|
||||
}
|
||||
|
||||
// A ClientOption is used to configure a Client.
|
||||
type ClientOption func(*Client)
|
||||
|
||||
// WithEndpoint configures a Client to use the specified API endpoint.
|
||||
func WithEndpoint(endpoint string) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.endpoint = strings.TrimRight(endpoint, "/")
|
||||
}
|
||||
}
|
||||
|
||||
// WithToken configures a Client to use the specified token for authentication.
|
||||
func WithToken(token string) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.token = token
|
||||
}
|
||||
}
|
||||
|
||||
// WithPollInterval configures a Client to use the specified interval when polling
|
||||
// from the API.
|
||||
func WithPollInterval(pollInterval time.Duration) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.pollInterval = pollInterval
|
||||
}
|
||||
}
|
||||
|
||||
// WithBackoffFunc configures a Client to use the specified backoff function.
|
||||
func WithBackoffFunc(f BackoffFunc) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.backoffFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithApplication configures a Client with the given application name and
|
||||
// application version. The version may be blank. Programs are encouraged
|
||||
// to at least set an application name.
|
||||
func WithApplication(name, version string) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.applicationName = name
|
||||
client.applicationVersion = version
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// WithHTTPClient configures a Client to perform HTTP requests with httpClient.
|
||||
func WithHTTPClient(httpClient *http.Client) ClientOption {
|
||||
return func(client *Client) {
|
||||
client.httpClient = httpClient
|
||||
}
|
||||
}
|
||||
|
||||
// NewClient creates a new client.
|
||||
func NewClient(options ...ClientOption) *Client {
|
||||
client := &Client{
|
||||
endpoint: Endpoint,
|
||||
httpClient: &http.Client{},
|
||||
backoffFunc: ExponentialBackoff(2, 500*time.Millisecond),
|
||||
pollInterval: 500 * time.Millisecond,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(client)
|
||||
}
|
||||
|
||||
client.buildUserAgent()
|
||||
|
||||
client.Action = ActionClient{client: client}
|
||||
client.Datacenter = DatacenterClient{client: client}
|
||||
client.FloatingIP = FloatingIPClient{client: 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}
|
||||
client.SSHKey = SSHKeyClient{client: client}
|
||||
client.Volume = VolumeClient{client: client}
|
||||
client.LoadBalancer = LoadBalancerClient{client: client}
|
||||
client.LoadBalancerType = LoadBalancerTypeClient{client: client}
|
||||
client.Certificate = CertificateClient{client: client}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// NewRequest creates an HTTP request against the API. The returned request
|
||||
// is assigned with ctx and has all necessary headers set (auth, user agent, etc.).
|
||||
func (c *Client) NewRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) {
|
||||
url := c.endpoint + path
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("User-Agent", c.userAgent)
|
||||
if c.token != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token))
|
||||
}
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// Do performs an HTTP request against the API.
|
||||
func (c *Client) Do(r *http.Request, v interface{}) (*Response, error) {
|
||||
var retries int
|
||||
var body []byte
|
||||
var err error
|
||||
if r.ContentLength > 0 {
|
||||
body, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
r.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
r.Body.Close()
|
||||
}
|
||||
for {
|
||||
if r.ContentLength > 0 {
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
}
|
||||
|
||||
if c.debugWriter != nil {
|
||||
// To get the response body we need to read it before the request was actually send. https://github.com/golang/go/issues/29792
|
||||
dumpReq, err := httputil.DumpRequestOut(r, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Fprintf(c.debugWriter, "--- Request:\n%s\n\n", dumpReq)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response := &Response{Response: resp}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
return response, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
|
||||
if c.debugWriter != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 && resp.StatusCode <= 599 {
|
||||
err = errorFromResponse(resp, body)
|
||||
if err == nil {
|
||||
err = fmt.Errorf("hcloud: server responded with status code %d", resp.StatusCode)
|
||||
} else {
|
||||
if isRetryable(err) {
|
||||
c.backoff(retries)
|
||||
retries++
|
||||
continue
|
||||
}
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
if v != nil {
|
||||
if w, ok := v.(io.Writer); ok {
|
||||
_, err = io.Copy(w, bytes.NewReader(body))
|
||||
} else {
|
||||
err = json.Unmarshal(body, v)
|
||||
}
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
}
|
||||
|
||||
func isRetryable(error error) bool {
|
||||
err, ok := error.(Error)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return err.Code == ErrorCodeRateLimitExceeded || err.Code == ErrorCodeConflict
|
||||
}
|
||||
|
||||
func (c *Client) backoff(retries int) {
|
||||
time.Sleep(c.backoffFunc(retries))
|
||||
}
|
||||
|
||||
func (c *Client) all(f func(int) (*Response, error)) (*Response, error) {
|
||||
var (
|
||||
page = 1
|
||||
)
|
||||
for {
|
||||
resp, err := f(page)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Meta.Pagination == nil || resp.Meta.Pagination.NextPage == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
page = resp.Meta.Pagination.NextPage
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) buildUserAgent() {
|
||||
switch {
|
||||
case c.applicationName != "" && c.applicationVersion != "":
|
||||
c.userAgent = c.applicationName + "/" + c.applicationVersion + " " + UserAgent
|
||||
case c.applicationName != "" && c.applicationVersion == "":
|
||||
c.userAgent = c.applicationName + " " + UserAgent
|
||||
default:
|
||||
c.userAgent = UserAgent
|
||||
}
|
||||
}
|
||||
|
||||
func errorFromResponse(resp *http.Response, body []byte) error {
|
||||
if !strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
|
||||
return nil
|
||||
}
|
||||
|
||||
var respBody schema.ErrorResponse
|
||||
if err := json.Unmarshal(body, &respBody); err != nil {
|
||||
return nil
|
||||
}
|
||||
if respBody.Error.Code == "" && respBody.Error.Message == "" {
|
||||
return nil
|
||||
}
|
||||
return ErrorFromSchema(respBody.Error)
|
||||
}
|
||||
|
||||
// Response represents a response from the API. It embeds http.Response.
|
||||
type Response struct {
|
||||
*http.Response
|
||||
Meta Meta
|
||||
}
|
||||
|
||||
func (r *Response) readMeta(body []byte) error {
|
||||
if h := r.Header.Get("RateLimit-Limit"); h != "" {
|
||||
r.Meta.Ratelimit.Limit, _ = strconv.Atoi(h)
|
||||
}
|
||||
if h := r.Header.Get("RateLimit-Remaining"); h != "" {
|
||||
r.Meta.Ratelimit.Remaining, _ = strconv.Atoi(h)
|
||||
}
|
||||
if h := r.Header.Get("RateLimit-Reset"); h != "" {
|
||||
if ts, err := strconv.ParseInt(h, 10, 64); err == nil {
|
||||
r.Meta.Ratelimit.Reset = time.Unix(ts, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.Header.Get("Content-Type"), "application/json") {
|
||||
var s schema.MetaResponse
|
||||
if err := json.Unmarshal(body, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.Meta.Pagination != nil {
|
||||
p := PaginationFromSchema(*s.Meta.Pagination)
|
||||
r.Meta.Pagination = &p
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Meta represents meta information included in an API response.
|
||||
type Meta struct {
|
||||
Pagination *Pagination
|
||||
Ratelimit Ratelimit
|
||||
}
|
||||
|
||||
// Pagination represents pagination meta information.
|
||||
type Pagination struct {
|
||||
Page int
|
||||
PerPage int
|
||||
PreviousPage int
|
||||
NextPage int
|
||||
LastPage int
|
||||
TotalEntries int
|
||||
}
|
||||
|
||||
// Ratelimit represents ratelimit information.
|
||||
type Ratelimit struct {
|
||||
Limit int
|
||||
Remaining int
|
||||
Reset time.Time
|
||||
}
|
||||
|
||||
// ListOpts specifies options for listing resources.
|
||||
type ListOpts struct {
|
||||
Page int // Page (starting at 1)
|
||||
PerPage int // Items per page (0 means default)
|
||||
LabelSelector string // Label selector for filtering by labels
|
||||
}
|
||||
|
||||
func (l ListOpts) values() url.Values {
|
||||
vals := url.Values{}
|
||||
if l.Page > 0 {
|
||||
vals.Add("page", strconv.Itoa(l.Page))
|
||||
}
|
||||
if l.PerPage > 0 {
|
||||
vals.Add("per_page", strconv.Itoa(l.PerPage))
|
||||
}
|
||||
if len(l.LabelSelector) > 0 {
|
||||
vals.Add("label_selector", l.LabelSelector)
|
||||
}
|
||||
return vals
|
||||
}
|
129
vendor/github.com/hetznercloud/hcloud-go/hcloud/datacenter.go
generated
vendored
Normal file
129
vendor/github.com/hetznercloud/hcloud-go/hcloud/datacenter.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Datacenter represents a datacenter in the Hetzner Cloud.
|
||||
type Datacenter struct {
|
||||
ID int
|
||||
Name string
|
||||
Description string
|
||||
Location *Location
|
||||
ServerTypes DatacenterServerTypes
|
||||
}
|
||||
|
||||
// DatacenterServerTypes represents the server types available and supported in a datacenter.
|
||||
type DatacenterServerTypes struct {
|
||||
Supported []*ServerType
|
||||
Available []*ServerType
|
||||
}
|
||||
|
||||
// DatacenterClient is a client for the datacenter API.
|
||||
type DatacenterClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a datacenter by its ID. If the datacenter does not exist, nil is returned.
|
||||
func (c *DatacenterClient) GetByID(ctx context.Context, id int) (*Datacenter, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/datacenters/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.DatacenterGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, resp, err
|
||||
}
|
||||
return DatacenterFromSchema(body.Datacenter), resp, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
datacenters, response, err := c.List(ctx, DatacenterListOpts{Name: name})
|
||||
if len(datacenters) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return datacenters[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a datacenter by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a datacenter by its name. If the datacenter does not exist, nil is returned.
|
||||
func (c *DatacenterClient) Get(ctx context.Context, idOrName string) (*Datacenter, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *DatacenterClient) List(ctx context.Context, opts DatacenterListOpts) ([]*Datacenter, *Response, error) {
|
||||
path := "/datacenters?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.DatacenterListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
datacenters := make([]*Datacenter, 0, len(body.Datacenters))
|
||||
for _, i := range body.Datacenters {
|
||||
datacenters = append(datacenters, DatacenterFromSchema(i))
|
||||
}
|
||||
return datacenters, resp, nil
|
||||
}
|
||||
|
||||
// All returns all datacenters.
|
||||
func (c *DatacenterClient) All(ctx context.Context) ([]*Datacenter, error) {
|
||||
allDatacenters := []*Datacenter{}
|
||||
|
||||
opts := DatacenterListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
datacenters, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allDatacenters = append(allDatacenters, datacenters...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allDatacenters, nil
|
||||
}
|
62
vendor/github.com/hetznercloud/hcloud-go/hcloud/error.go
generated
vendored
Normal file
62
vendor/github.com/hetznercloud/hcloud-go/hcloud/error.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
package hcloud
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ErrorCode represents an error code returned from the API.
|
||||
type ErrorCode string
|
||||
|
||||
// Error codes returned from the API.
|
||||
const (
|
||||
ErrorCodeServiceError ErrorCode = "service_error" // Generic service error
|
||||
ErrorCodeRateLimitExceeded ErrorCode = "rate_limit_exceeded" // Rate limit exceeded
|
||||
ErrorCodeUnknownError ErrorCode = "unknown_error" // Unknown error
|
||||
ErrorCodeNotFound ErrorCode = "not_found" // Resource not found
|
||||
ErrorCodeInvalidInput ErrorCode = "invalid_input" // Validation error
|
||||
ErrorCodeForbidden ErrorCode = "forbidden" // Insufficient permissions
|
||||
ErrorCodeJSONError ErrorCode = "json_error" // Invalid JSON in request
|
||||
ErrorCodeLocked ErrorCode = "locked" // Item is locked (Another action is running)
|
||||
ErrorCodeResourceLimitExceeded ErrorCode = "resource_limit_exceeded" // Resource limit exceeded
|
||||
ErrorCodeResourceUnavailable ErrorCode = "resource_unavailable" // Resource currently unavailable
|
||||
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
|
||||
|
||||
// The actual value of this error code is limit_reached. The new error code
|
||||
// rate_limit_exceeded for ratelimiting was introduced before Hetzner Cloud
|
||||
// launched into the public. To make clients using the old error code still
|
||||
// work as expected, we set the value of the old error code to that of the
|
||||
// new error code.
|
||||
ErrorCodeLimitReached = ErrorCodeRateLimitExceeded
|
||||
)
|
||||
|
||||
// Error is an error returned from the API.
|
||||
type Error struct {
|
||||
Code ErrorCode
|
||||
Message string
|
||||
Details interface{}
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("%s (%s)", e.Message, e.Code)
|
||||
}
|
||||
|
||||
// ErrorDetailsInvalidInput contains the details of an 'invalid_input' error.
|
||||
type ErrorDetailsInvalidInput struct {
|
||||
Fields []ErrorDetailsInvalidInputField
|
||||
}
|
||||
|
||||
// ErrorDetailsInvalidInputField contains the validation errors reported on a field.
|
||||
type ErrorDetailsInvalidInputField struct {
|
||||
Name string
|
||||
Messages []string
|
||||
}
|
||||
|
||||
// IsError returns whether err is an API error with the given error code.
|
||||
func IsError(err error, code ErrorCode) bool {
|
||||
apiErr, ok := err.(Error)
|
||||
return ok && apiErr.Code == code
|
||||
}
|
378
vendor/github.com/hetznercloud/hcloud-go/hcloud/floating_ip.go
generated
vendored
Normal file
378
vendor/github.com/hetznercloud/hcloud-go/hcloud/floating_ip.go
generated
vendored
Normal file
|
@ -0,0 +1,378 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// FloatingIP represents a Floating IP in the Hetzner Cloud.
|
||||
type FloatingIP struct {
|
||||
ID int
|
||||
Description string
|
||||
Created time.Time
|
||||
IP net.IP
|
||||
Network *net.IPNet
|
||||
Type FloatingIPType
|
||||
Server *Server
|
||||
DNSPtr map[string]string
|
||||
HomeLocation *Location
|
||||
Blocked bool
|
||||
Protection FloatingIPProtection
|
||||
Labels map[string]string
|
||||
Name string
|
||||
}
|
||||
|
||||
// DNSPtrForIP returns the reverse DNS pointer of the IP address.
|
||||
func (f *FloatingIP) DNSPtrForIP(ip net.IP) string {
|
||||
return f.DNSPtr[ip.String()]
|
||||
}
|
||||
|
||||
// FloatingIPProtection represents the protection level of a Floating IP.
|
||||
type FloatingIPProtection struct {
|
||||
Delete bool
|
||||
}
|
||||
|
||||
// FloatingIPType represents the type of a Floating IP.
|
||||
type FloatingIPType string
|
||||
|
||||
// Floating IP types.
|
||||
const (
|
||||
FloatingIPTypeIPv4 FloatingIPType = "ipv4"
|
||||
FloatingIPTypeIPv6 FloatingIPType = "ipv6"
|
||||
)
|
||||
|
||||
// FloatingIPClient is a client for the Floating IP API.
|
||||
type FloatingIPClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a Floating IP by its ID. If the Floating IP does not exist,
|
||||
// nil is returned.
|
||||
func (c *FloatingIPClient) GetByID(ctx context.Context, id int) (*FloatingIP, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/floating_ips/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.FloatingIPGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, resp, err
|
||||
}
|
||||
return FloatingIPFromSchema(body.FloatingIP), resp, nil
|
||||
}
|
||||
|
||||
// GetByName retrieves a Floating IP by its name. If the Floating IP does not exist, nil is returned.
|
||||
func (c *FloatingIPClient) GetByName(ctx context.Context, name string) (*FloatingIP, *Response, error) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
floatingIPs, response, err := c.List(ctx, FloatingIPListOpts{Name: name})
|
||||
if len(floatingIPs) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return floatingIPs[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a Floating IP by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a Floating IP by its name. If the Floating IP does not exist, nil is returned.
|
||||
func (c *FloatingIPClient) Get(ctx context.Context, idOrName string) (*FloatingIP, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// FloatingIPListOpts specifies options for listing Floating IPs.
|
||||
type FloatingIPListOpts struct {
|
||||
ListOpts
|
||||
Name string
|
||||
}
|
||||
|
||||
func (l FloatingIPListOpts) values() url.Values {
|
||||
vals := l.ListOpts.values()
|
||||
if l.Name != "" {
|
||||
vals.Add("name", l.Name)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// List returns a list of Floating IPs for a specific page.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *FloatingIPClient) List(ctx context.Context, opts FloatingIPListOpts) ([]*FloatingIP, *Response, error) {
|
||||
path := "/floating_ips?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.FloatingIPListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
floatingIPs := make([]*FloatingIP, 0, len(body.FloatingIPs))
|
||||
for _, s := range body.FloatingIPs {
|
||||
floatingIPs = append(floatingIPs, FloatingIPFromSchema(s))
|
||||
}
|
||||
return floatingIPs, resp, nil
|
||||
}
|
||||
|
||||
// All returns all Floating IPs.
|
||||
func (c *FloatingIPClient) All(ctx context.Context) ([]*FloatingIP, error) {
|
||||
return c.AllWithOpts(ctx, FloatingIPListOpts{ListOpts: ListOpts{PerPage: 50}})
|
||||
}
|
||||
|
||||
// AllWithOpts returns all Floating IPs for the given options.
|
||||
func (c *FloatingIPClient) AllWithOpts(ctx context.Context, opts FloatingIPListOpts) ([]*FloatingIP, error) {
|
||||
allFloatingIPs := []*FloatingIP{}
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
floatingIPs, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allFloatingIPs = append(allFloatingIPs, floatingIPs...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allFloatingIPs, nil
|
||||
}
|
||||
|
||||
// FloatingIPCreateOpts specifies options for creating a Floating IP.
|
||||
type FloatingIPCreateOpts struct {
|
||||
Type FloatingIPType
|
||||
HomeLocation *Location
|
||||
Server *Server
|
||||
Description *string
|
||||
Name *string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Validate checks if options are valid.
|
||||
func (o FloatingIPCreateOpts) Validate() error {
|
||||
switch o.Type {
|
||||
case FloatingIPTypeIPv4, FloatingIPTypeIPv6:
|
||||
break
|
||||
default:
|
||||
return errors.New("missing or invalid type")
|
||||
}
|
||||
if o.HomeLocation == nil && o.Server == nil {
|
||||
return errors.New("one of home location or server is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FloatingIPCreateResult is the result of creating a Floating IP.
|
||||
type FloatingIPCreateResult struct {
|
||||
FloatingIP *FloatingIP
|
||||
Action *Action
|
||||
}
|
||||
|
||||
// Create creates a Floating IP.
|
||||
func (c *FloatingIPClient) Create(ctx context.Context, opts FloatingIPCreateOpts) (FloatingIPCreateResult, *Response, error) {
|
||||
if err := opts.Validate(); err != nil {
|
||||
return FloatingIPCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
reqBody := schema.FloatingIPCreateRequest{
|
||||
Type: string(opts.Type),
|
||||
Description: opts.Description,
|
||||
Name: opts.Name,
|
||||
}
|
||||
if opts.HomeLocation != nil {
|
||||
reqBody.HomeLocation = String(opts.HomeLocation.Name)
|
||||
}
|
||||
if opts.Server != nil {
|
||||
reqBody.Server = Int(opts.Server.ID)
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
reqBody.Labels = &opts.Labels
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return FloatingIPCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "POST", "/floating_ips", bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return FloatingIPCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.FloatingIPCreateResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return FloatingIPCreateResult{}, resp, err
|
||||
}
|
||||
var action *Action
|
||||
if respBody.Action != nil {
|
||||
action = ActionFromSchema(*respBody.Action)
|
||||
}
|
||||
return FloatingIPCreateResult{
|
||||
FloatingIP: FloatingIPFromSchema(respBody.FloatingIP),
|
||||
Action: action,
|
||||
}, resp, nil
|
||||
}
|
||||
|
||||
// Delete deletes a Floating IP.
|
||||
func (c *FloatingIPClient) Delete(ctx context.Context, floatingIP *FloatingIP) (*Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/floating_ips/%d", floatingIP.ID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// FloatingIPUpdateOpts specifies options for updating a Floating IP.
|
||||
type FloatingIPUpdateOpts struct {
|
||||
Description string
|
||||
Labels map[string]string
|
||||
Name string
|
||||
}
|
||||
|
||||
// Update updates a Floating IP.
|
||||
func (c *FloatingIPClient) Update(ctx context.Context, floatingIP *FloatingIP, opts FloatingIPUpdateOpts) (*FloatingIP, *Response, error) {
|
||||
reqBody := schema.FloatingIPUpdateRequest{
|
||||
Description: opts.Description,
|
||||
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("/floating_ips/%d", floatingIP.ID)
|
||||
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.FloatingIPUpdateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return FloatingIPFromSchema(respBody.FloatingIP), resp, nil
|
||||
}
|
||||
|
||||
// Assign assigns a Floating IP to a server.
|
||||
func (c *FloatingIPClient) Assign(ctx context.Context, floatingIP *FloatingIP, server *Server) (*Action, *Response, error) {
|
||||
reqBody := schema.FloatingIPActionAssignRequest{
|
||||
Server: server.ID,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/floating_ips/%d/actions/assign", floatingIP.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.FloatingIPActionAssignResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// Unassign unassigns a Floating IP from the currently assigned server.
|
||||
func (c *FloatingIPClient) Unassign(ctx context.Context, floatingIP *FloatingIP) (*Action, *Response, error) {
|
||||
var reqBody schema.FloatingIPActionUnassignRequest
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/floating_ips/%d/actions/unassign", floatingIP.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.FloatingIPActionUnassignResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// ChangeDNSPtr changes or resets the reverse DNS pointer for a Floating IP address.
|
||||
// Pass a nil ptr to reset the reverse DNS pointer to its default value.
|
||||
func (c *FloatingIPClient) ChangeDNSPtr(ctx context.Context, floatingIP *FloatingIP, ip string, ptr *string) (*Action, *Response, error) {
|
||||
reqBody := schema.FloatingIPActionChangeDNSPtrRequest{
|
||||
IP: ip,
|
||||
DNSPtr: ptr,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/floating_ips/%d/actions/change_dns_ptr", floatingIP.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.FloatingIPActionChangeDNSPtrResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// FloatingIPChangeProtectionOpts specifies options for changing the resource protection level of a Floating IP.
|
||||
type FloatingIPChangeProtectionOpts struct {
|
||||
Delete *bool
|
||||
}
|
||||
|
||||
// ChangeProtection changes the resource protection level of a Floating IP.
|
||||
func (c *FloatingIPClient) ChangeProtection(ctx context.Context, floatingIP *FloatingIP, opts FloatingIPChangeProtectionOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.FloatingIPActionChangeProtectionRequest{
|
||||
Delete: opts.Delete,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/floating_ips/%d/actions/change_protection", floatingIP.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.FloatingIPActionChangeProtectionResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
5
vendor/github.com/hetznercloud/hcloud-go/hcloud/hcloud.go
generated
vendored
Normal file
5
vendor/github.com/hetznercloud/hcloud-go/hcloud/hcloud.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Package hcloud is a library for the Hetzner Cloud API.
|
||||
package hcloud
|
||||
|
||||
// Version is the library's version following Semantic Versioning.
|
||||
const Version = "1.21.1"
|
18
vendor/github.com/hetznercloud/hcloud-go/hcloud/helper.go
generated
vendored
Normal file
18
vendor/github.com/hetznercloud/hcloud-go/hcloud/helper.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package hcloud
|
||||
|
||||
import "time"
|
||||
|
||||
// String returns a pointer to the passed string s.
|
||||
func String(s string) *string { return &s }
|
||||
|
||||
// Int returns a pointer to the passed integer i.
|
||||
func Int(i int) *int { return &i }
|
||||
|
||||
// Bool returns a pointer to the passed bool b.
|
||||
func Bool(b bool) *bool { return &b }
|
||||
|
||||
// Duration returns a pointer to the passed time.Duration d.
|
||||
func Duration(d time.Duration) *time.Duration { return &d }
|
||||
|
||||
func intSlice(is []int) *[]int { return &is }
|
||||
func stringSlice(ss []string) *[]string { return &ss }
|
268
vendor/github.com/hetznercloud/hcloud-go/hcloud/image.go
generated
vendored
Normal file
268
vendor/github.com/hetznercloud/hcloud-go/hcloud/image.go
generated
vendored
Normal file
|
@ -0,0 +1,268 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Image represents an Image in the Hetzner Cloud.
|
||||
type Image struct {
|
||||
ID int
|
||||
Name string
|
||||
Type ImageType
|
||||
Status ImageStatus
|
||||
Description string
|
||||
ImageSize float32
|
||||
DiskSize float32
|
||||
Created time.Time
|
||||
CreatedFrom *Server
|
||||
BoundTo *Server
|
||||
RapidDeploy bool
|
||||
|
||||
OSFlavor string
|
||||
OSVersion string
|
||||
|
||||
Protection ImageProtection
|
||||
Deprecated time.Time // The zero value denotes the image is not deprecated.
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// IsDeprecated returns whether the image is deprecated.
|
||||
func (image *Image) IsDeprecated() bool {
|
||||
return !image.Deprecated.IsZero()
|
||||
}
|
||||
|
||||
// ImageProtection represents the protection level of an image.
|
||||
type ImageProtection struct {
|
||||
Delete bool
|
||||
}
|
||||
|
||||
// ImageType specifies the type of an image.
|
||||
type ImageType string
|
||||
|
||||
const (
|
||||
// ImageTypeSnapshot represents a snapshot image.
|
||||
ImageTypeSnapshot ImageType = "snapshot"
|
||||
// ImageTypeBackup represents a backup image.
|
||||
ImageTypeBackup ImageType = "backup"
|
||||
// ImageTypeSystem represents a system image.
|
||||
ImageTypeSystem ImageType = "system"
|
||||
)
|
||||
|
||||
// ImageStatus specifies the status of an image.
|
||||
type ImageStatus string
|
||||
|
||||
const (
|
||||
// ImageStatusCreating is the status when an image is being created.
|
||||
ImageStatusCreating ImageStatus = "creating"
|
||||
// ImageStatusAvailable is the status when an image is available.
|
||||
ImageStatusAvailable ImageStatus = "available"
|
||||
)
|
||||
|
||||
// ImageClient is a client for the image API.
|
||||
type ImageClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves an image by its ID. If the image does not exist, nil is returned.
|
||||
func (c *ImageClient) GetByID(ctx context.Context, id int) (*Image, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/images/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ImageGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return ImageFromSchema(body.Image), resp, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
images, response, err := c.List(ctx, ImageListOpts{Name: name})
|
||||
if len(images) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return images[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves an image by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves an image by its name. If the image does not exist, nil is returned.
|
||||
func (c *ImageClient) Get(ctx context.Context, idOrName string) (*Image, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// ImageListOpts specifies options for listing images.
|
||||
type ImageListOpts struct {
|
||||
ListOpts
|
||||
Type []ImageType
|
||||
BoundTo *Server
|
||||
Name string
|
||||
Sort []string
|
||||
Status []ImageStatus
|
||||
IncludeDeprecated bool
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
if l.IncludeDeprecated {
|
||||
vals.Add("include_deprecated", strconv.FormatBool(l.IncludeDeprecated))
|
||||
}
|
||||
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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *ImageClient) List(ctx context.Context, opts ImageListOpts) ([]*Image, *Response, error) {
|
||||
path := "/images?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ImageListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
images := make([]*Image, 0, len(body.Images))
|
||||
for _, i := range body.Images {
|
||||
images = append(images, ImageFromSchema(i))
|
||||
}
|
||||
return images, resp, nil
|
||||
}
|
||||
|
||||
// All returns all images.
|
||||
func (c *ImageClient) All(ctx context.Context) ([]*Image, error) {
|
||||
return c.AllWithOpts(ctx, ImageListOpts{ListOpts: ListOpts{PerPage: 50}})
|
||||
}
|
||||
|
||||
// AllWithOpts returns all images for the given options.
|
||||
func (c *ImageClient) AllWithOpts(ctx context.Context, opts ImageListOpts) ([]*Image, error) {
|
||||
allImages := []*Image{}
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
images, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allImages = append(allImages, images...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allImages, nil
|
||||
}
|
||||
|
||||
// Delete deletes an image.
|
||||
func (c *ImageClient) Delete(ctx context.Context, image *Image) (*Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/images/%d", image.ID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// ImageUpdateOpts specifies options for updating an image.
|
||||
type ImageUpdateOpts struct {
|
||||
Description *string
|
||||
Type ImageType
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Update updates an image.
|
||||
func (c *ImageClient) Update(ctx context.Context, image *Image, opts ImageUpdateOpts) (*Image, *Response, error) {
|
||||
reqBody := schema.ImageUpdateRequest{
|
||||
Description: opts.Description,
|
||||
}
|
||||
if opts.Type != "" {
|
||||
reqBody.Type = String(string(opts.Type))
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
reqBody.Labels = &opts.Labels
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/images/%d", image.ID)
|
||||
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ImageUpdateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ImageFromSchema(respBody.Image), resp, nil
|
||||
}
|
||||
|
||||
// ImageChangeProtectionOpts specifies options for changing the resource protection level of an image.
|
||||
type ImageChangeProtectionOpts struct {
|
||||
Delete *bool
|
||||
}
|
||||
|
||||
// ChangeProtection changes the resource protection level of an image.
|
||||
func (c *ImageClient) ChangeProtection(ctx context.Context, image *Image, opts ImageChangeProtectionOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.ImageActionChangeProtectionRequest{
|
||||
Delete: opts.Delete,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/images/%d/actions/change_protection", image.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ImageActionChangeProtectionResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
139
vendor/github.com/hetznercloud/hcloud-go/hcloud/iso.go
generated
vendored
Normal file
139
vendor/github.com/hetznercloud/hcloud-go/hcloud/iso.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// ISO represents an ISO image in the Hetzner Cloud.
|
||||
type ISO struct {
|
||||
ID int
|
||||
Name string
|
||||
Description string
|
||||
Type ISOType
|
||||
Deprecated time.Time
|
||||
}
|
||||
|
||||
// IsDeprecated returns true if the ISO is deprecated
|
||||
func (iso *ISO) IsDeprecated() bool {
|
||||
return !iso.Deprecated.IsZero()
|
||||
}
|
||||
|
||||
// ISOType specifies the type of an ISO image.
|
||||
type ISOType string
|
||||
|
||||
const (
|
||||
// ISOTypePublic is the type of a public ISO image.
|
||||
ISOTypePublic ISOType = "public"
|
||||
|
||||
// ISOTypePrivate is the type of a private ISO image.
|
||||
ISOTypePrivate ISOType = "private"
|
||||
)
|
||||
|
||||
// ISOClient is a client for the ISO API.
|
||||
type ISOClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves an ISO by its ID.
|
||||
func (c *ISOClient) GetByID(ctx context.Context, id int) (*ISO, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/isos/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ISOGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, resp, err
|
||||
}
|
||||
return ISOFromSchema(body.ISO), resp, nil
|
||||
}
|
||||
|
||||
// GetByName retrieves an ISO by its name.
|
||||
func (c *ISOClient) GetByName(ctx context.Context, name string) (*ISO, *Response, error) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
isos, response, err := c.List(ctx, ISOListOpts{Name: name})
|
||||
if len(isos) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
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.
|
||||
func (c *ISOClient) Get(ctx context.Context, idOrName string) (*ISO, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *ISOClient) List(ctx context.Context, opts ISOListOpts) ([]*ISO, *Response, error) {
|
||||
path := "/isos?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ISOListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
isos := make([]*ISO, 0, len(body.ISOs))
|
||||
for _, i := range body.ISOs {
|
||||
isos = append(isos, ISOFromSchema(i))
|
||||
}
|
||||
return isos, resp, nil
|
||||
}
|
||||
|
||||
// All returns all ISOs.
|
||||
func (c *ISOClient) All(ctx context.Context) ([]*ISO, error) {
|
||||
allISOs := []*ISO{}
|
||||
|
||||
opts := ISOListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
isos, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allISOs = append(allISOs, isos...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allISOs, nil
|
||||
}
|
933
vendor/github.com/hetznercloud/hcloud-go/hcloud/load_balancer.go
generated
vendored
Normal file
933
vendor/github.com/hetznercloud/hcloud-go/hcloud/load_balancer.go
generated
vendored
Normal file
|
@ -0,0 +1,933 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// LoadBalancer represents a Load Balancer in the Hetzner Cloud.
|
||||
type LoadBalancer struct {
|
||||
ID int
|
||||
Name string
|
||||
PublicNet LoadBalancerPublicNet
|
||||
PrivateNet []LoadBalancerPrivateNet
|
||||
Location *Location
|
||||
LoadBalancerType *LoadBalancerType
|
||||
Algorithm LoadBalancerAlgorithm
|
||||
Services []LoadBalancerService
|
||||
Targets []LoadBalancerTarget
|
||||
Protection LoadBalancerProtection
|
||||
Labels map[string]string
|
||||
Created time.Time
|
||||
IncludedTraffic uint64
|
||||
OutgoingTraffic uint64
|
||||
IngoingTraffic uint64
|
||||
}
|
||||
|
||||
// LoadBalancerPublicNet represents a Load Balancer's public network.
|
||||
type LoadBalancerPublicNet struct {
|
||||
Enabled bool
|
||||
IPv4 LoadBalancerPublicNetIPv4
|
||||
IPv6 LoadBalancerPublicNetIPv6
|
||||
}
|
||||
|
||||
// LoadBalancerPublicNetIPv4 represents a Load Balancer's public IPv4 address.
|
||||
type LoadBalancerPublicNetIPv4 struct {
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
// LoadBalancerPublicNetIPv6 represents a Load Balancer's public IPv6 address.
|
||||
type LoadBalancerPublicNetIPv6 struct {
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
// LoadBalancerPrivateNet represents a Load Balancer's private network.
|
||||
type LoadBalancerPrivateNet struct {
|
||||
Network *Network
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
// LoadBalancerService represents a Load Balancer service.
|
||||
type LoadBalancerService struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
ListenPort int
|
||||
DestinationPort int
|
||||
Proxyprotocol bool
|
||||
HTTP LoadBalancerServiceHTTP
|
||||
HealthCheck LoadBalancerServiceHealthCheck
|
||||
}
|
||||
|
||||
// LoadBalancerServiceHTTP stores configuration for a service using the HTTP protocol.
|
||||
type LoadBalancerServiceHTTP struct {
|
||||
CookieName string
|
||||
CookieLifetime time.Duration
|
||||
Certificates []*Certificate
|
||||
RedirectHTTP bool
|
||||
StickySessions bool
|
||||
}
|
||||
|
||||
// LoadBalancerServiceHealthCheck stores configuration for a service health check.
|
||||
type LoadBalancerServiceHealthCheck struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
Port int
|
||||
Interval time.Duration
|
||||
Timeout time.Duration
|
||||
Retries int
|
||||
HTTP *LoadBalancerServiceHealthCheckHTTP
|
||||
}
|
||||
|
||||
// LoadBalancerServiceHealthCheckHTTP stores configuration for a service health check
|
||||
// using the HTTP protocol.
|
||||
type LoadBalancerServiceHealthCheckHTTP struct {
|
||||
Domain string
|
||||
Path string
|
||||
Response string
|
||||
StatusCodes []string
|
||||
TLS bool
|
||||
}
|
||||
|
||||
// LoadBalancerAlgorithmType specifies the algorithm type a Load Balancer
|
||||
// uses for distributing requests.
|
||||
type LoadBalancerAlgorithmType string
|
||||
|
||||
const (
|
||||
// LoadBalancerAlgorithmTypeRoundRobin is an algorithm which distributes
|
||||
// requests to targets in a round robin fashion.
|
||||
LoadBalancerAlgorithmTypeRoundRobin LoadBalancerAlgorithmType = "round_robin"
|
||||
// LoadBalancerAlgorithmTypeLeastConnections is an algorithm which distributes
|
||||
// requests to targets with the least number of connections.
|
||||
LoadBalancerAlgorithmTypeLeastConnections LoadBalancerAlgorithmType = "least_connections"
|
||||
)
|
||||
|
||||
// LoadBalancerAlgorithm configures the algorithm a Load Balancer uses
|
||||
// for distributing requests.
|
||||
type LoadBalancerAlgorithm struct {
|
||||
Type LoadBalancerAlgorithmType
|
||||
}
|
||||
|
||||
// LoadBalancerTargetType specifies the type of a Load Balancer target.
|
||||
type LoadBalancerTargetType string
|
||||
|
||||
const (
|
||||
// LoadBalancerTargetTypeServer is a target type which points to a specific
|
||||
// server.
|
||||
LoadBalancerTargetTypeServer LoadBalancerTargetType = "server"
|
||||
|
||||
// LoadBalancerTargetTypeLabelSelector is a target type which selects the
|
||||
// servers a Load Balancer points to using labels assigned to the servers.
|
||||
LoadBalancerTargetTypeLabelSelector LoadBalancerTargetType = "label_selector"
|
||||
|
||||
// LoadBalancerTargetTypeIP is a target type which points to an IP.
|
||||
LoadBalancerTargetTypeIP LoadBalancerTargetType = "ip"
|
||||
)
|
||||
|
||||
// LoadBalancerServiceProtocol specifies the protocol of a Load Balancer service.
|
||||
type LoadBalancerServiceProtocol string
|
||||
|
||||
const (
|
||||
// LoadBalancerServiceProtocolTCP specifies a TCP service.
|
||||
LoadBalancerServiceProtocolTCP LoadBalancerServiceProtocol = "tcp"
|
||||
// LoadBalancerServiceProtocolHTTP specifies an HTTP service.
|
||||
LoadBalancerServiceProtocolHTTP LoadBalancerServiceProtocol = "http"
|
||||
// LoadBalancerServiceProtocolHTTPS specifies an HTTPS service.
|
||||
LoadBalancerServiceProtocolHTTPS LoadBalancerServiceProtocol = "https"
|
||||
)
|
||||
|
||||
// LoadBalancerTarget represents a Load Balancer target.
|
||||
type LoadBalancerTarget struct {
|
||||
Type LoadBalancerTargetType
|
||||
Server *LoadBalancerTargetServer
|
||||
LabelSelector *LoadBalancerTargetLabelSelector
|
||||
IP *LoadBalancerTargetIP
|
||||
HealthStatus []LoadBalancerTargetHealthStatus
|
||||
Targets []LoadBalancerTarget
|
||||
UsePrivateIP bool
|
||||
}
|
||||
|
||||
// LoadBalancerTargetServer configures a Load Balancer target
|
||||
// pointing at a specific server.
|
||||
type LoadBalancerTargetServer struct {
|
||||
Server *Server
|
||||
}
|
||||
|
||||
// LoadBalancerTargetLabelSelector configures a Load Balancer target pointing
|
||||
// at the servers matching the selector. This includes the target pointing at
|
||||
// nothing, if no servers match the Selector.
|
||||
type LoadBalancerTargetLabelSelector struct {
|
||||
Selector string
|
||||
}
|
||||
|
||||
// LoadBalancerTargetIP configures a Load Balancer target pointing to a Hetzner
|
||||
// Online IP address.
|
||||
type LoadBalancerTargetIP struct {
|
||||
IP string
|
||||
}
|
||||
|
||||
// LoadBalancerTargetHealthStatusStatus describes a target's health status.
|
||||
type LoadBalancerTargetHealthStatusStatus string
|
||||
|
||||
const (
|
||||
// LoadBalancerTargetHealthStatusStatusUnknown denotes that the health status is unknown.
|
||||
LoadBalancerTargetHealthStatusStatusUnknown LoadBalancerTargetHealthStatusStatus = "unknown"
|
||||
// LoadBalancerTargetHealthStatusStatusHealthy denotes a healthy target.
|
||||
LoadBalancerTargetHealthStatusStatusHealthy LoadBalancerTargetHealthStatusStatus = "healthy"
|
||||
// LoadBalancerTargetHealthStatusStatusUnhealthy denotes an unhealthy target.
|
||||
LoadBalancerTargetHealthStatusStatusUnhealthy LoadBalancerTargetHealthStatusStatus = "unhealthy"
|
||||
)
|
||||
|
||||
// LoadBalancerTargetHealthStatus describes a target's health for a specific service.
|
||||
type LoadBalancerTargetHealthStatus struct {
|
||||
ListenPort int
|
||||
Status LoadBalancerTargetHealthStatusStatus
|
||||
}
|
||||
|
||||
// LoadBalancerProtection represents the protection level of a Load Balancer.
|
||||
type LoadBalancerProtection struct {
|
||||
Delete bool
|
||||
}
|
||||
|
||||
// LoadBalancerClient is a client for the Load Balancers API.
|
||||
type LoadBalancerClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a Load Balancer by its ID. If the Load Balancer does not exist, nil is returned.
|
||||
func (c *LoadBalancerClient) GetByID(ctx context.Context, id int) (*LoadBalancer, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/load_balancers/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.LoadBalancerGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return LoadBalancerFromSchema(body.LoadBalancer), resp, nil
|
||||
}
|
||||
|
||||
// GetByName retrieves a Load Balancer by its name. If the Load Balancer does not exist, nil is returned.
|
||||
func (c *LoadBalancerClient) GetByName(ctx context.Context, name string) (*LoadBalancer, *Response, error) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
LoadBalancer, response, err := c.List(ctx, LoadBalancerListOpts{Name: name})
|
||||
if len(LoadBalancer) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return LoadBalancer[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a Load Balancer by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a Load Balancer by its name. If the Load Balancer does not exist, nil is returned.
|
||||
func (c *LoadBalancerClient) Get(ctx context.Context, idOrName string) (*LoadBalancer, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// LoadBalancerListOpts specifies options for listing Load Balancers.
|
||||
type LoadBalancerListOpts struct {
|
||||
ListOpts
|
||||
Name string
|
||||
}
|
||||
|
||||
func (l LoadBalancerListOpts) values() url.Values {
|
||||
vals := l.ListOpts.values()
|
||||
if l.Name != "" {
|
||||
vals.Add("name", l.Name)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// List returns a list of Load Balancers for a specific page.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *LoadBalancerClient) List(ctx context.Context, opts LoadBalancerListOpts) ([]*LoadBalancer, *Response, error) {
|
||||
path := "/load_balancers?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.LoadBalancerListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
LoadBalancers := make([]*LoadBalancer, 0, len(body.LoadBalancers))
|
||||
for _, s := range body.LoadBalancers {
|
||||
LoadBalancers = append(LoadBalancers, LoadBalancerFromSchema(s))
|
||||
}
|
||||
return LoadBalancers, resp, nil
|
||||
}
|
||||
|
||||
// All returns all Load Balancers.
|
||||
func (c *LoadBalancerClient) All(ctx context.Context) ([]*LoadBalancer, error) {
|
||||
allLoadBalancer := []*LoadBalancer{}
|
||||
|
||||
opts := LoadBalancerListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
LoadBalancer, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allLoadBalancer = append(allLoadBalancer, LoadBalancer...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allLoadBalancer, nil
|
||||
}
|
||||
|
||||
// AllWithOpts returns all Load Balancers for the given options.
|
||||
func (c *LoadBalancerClient) AllWithOpts(ctx context.Context, opts LoadBalancerListOpts) ([]*LoadBalancer, error) {
|
||||
var allLoadBalancers []*LoadBalancer
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
LoadBalancers, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allLoadBalancers = append(allLoadBalancers, LoadBalancers...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allLoadBalancers, nil
|
||||
}
|
||||
|
||||
// LoadBalancerUpdateOpts specifies options for updating a Load Balancer.
|
||||
type LoadBalancerUpdateOpts struct {
|
||||
Name string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Update updates a Load Balancer.
|
||||
func (c *LoadBalancerClient) Update(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerUpdateOpts) (*LoadBalancer, *Response, error) {
|
||||
reqBody := schema.LoadBalancerUpdateRequest{}
|
||||
if opts.Name != "" {
|
||||
reqBody.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("/load_balancers/%d", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.LoadBalancerUpdateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return LoadBalancerFromSchema(respBody.LoadBalancer), resp, nil
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOpts specifies options for creating a new Load Balancer.
|
||||
type LoadBalancerCreateOpts struct {
|
||||
Name string
|
||||
LoadBalancerType *LoadBalancerType
|
||||
Algorithm *LoadBalancerAlgorithm
|
||||
Location *Location
|
||||
NetworkZone NetworkZone
|
||||
Labels map[string]string
|
||||
Targets []LoadBalancerCreateOptsTarget
|
||||
Services []LoadBalancerCreateOptsService
|
||||
PublicInterface *bool
|
||||
Network *Network
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsTarget holds options for specifying a target
|
||||
// when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsTarget struct {
|
||||
Type LoadBalancerTargetType
|
||||
Server LoadBalancerCreateOptsTargetServer
|
||||
LabelSelector LoadBalancerCreateOptsTargetLabelSelector
|
||||
IP LoadBalancerCreateOptsTargetIP
|
||||
UsePrivateIP *bool
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsTargetServer holds options for specifying a server target
|
||||
// when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsTargetServer struct {
|
||||
Server *Server
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsTargetLabelSelector holds options for specifying a label selector target
|
||||
// when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsTargetLabelSelector struct {
|
||||
Selector string
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsTargetIP holds options for specifying an IP target
|
||||
// when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsTargetIP struct {
|
||||
IP string
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsService holds options for specifying a service
|
||||
// when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsService struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
ListenPort *int
|
||||
DestinationPort *int
|
||||
Proxyprotocol *bool
|
||||
HTTP *LoadBalancerCreateOptsServiceHTTP
|
||||
HealthCheck *LoadBalancerCreateOptsServiceHealthCheck
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsServiceHTTP holds options for specifying an HTTP service
|
||||
// when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsServiceHTTP struct {
|
||||
CookieName *string
|
||||
CookieLifetime *time.Duration
|
||||
Certificates []*Certificate
|
||||
RedirectHTTP *bool
|
||||
StickySessions *bool
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsServiceHealthCheck holds options for specifying a service
|
||||
// health check when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsServiceHealthCheck struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
Port *int
|
||||
Interval *time.Duration
|
||||
Timeout *time.Duration
|
||||
Retries *int
|
||||
HTTP *LoadBalancerCreateOptsServiceHealthCheckHTTP
|
||||
}
|
||||
|
||||
// LoadBalancerCreateOptsServiceHealthCheckHTTP holds options for specifying a service
|
||||
// HTTP health check when creating a new Load Balancer.
|
||||
type LoadBalancerCreateOptsServiceHealthCheckHTTP struct {
|
||||
Domain *string
|
||||
Path *string
|
||||
Response *string
|
||||
StatusCodes []string
|
||||
TLS *bool
|
||||
}
|
||||
|
||||
// LoadBalancerCreateResult is the result of a create Load Balancer call.
|
||||
type LoadBalancerCreateResult struct {
|
||||
LoadBalancer *LoadBalancer
|
||||
Action *Action
|
||||
}
|
||||
|
||||
// Create creates a new Load Balancer.
|
||||
func (c *LoadBalancerClient) Create(ctx context.Context, opts LoadBalancerCreateOpts) (LoadBalancerCreateResult, *Response, error) {
|
||||
reqBody := loadBalancerCreateOptsToSchema(opts)
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
|
||||
if err != nil {
|
||||
return LoadBalancerCreateResult{}, nil, err
|
||||
}
|
||||
req, err := c.client.NewRequest(ctx, "POST", "/load_balancers", bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return LoadBalancerCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.LoadBalancerCreateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return LoadBalancerCreateResult{}, resp, err
|
||||
}
|
||||
return LoadBalancerCreateResult{
|
||||
LoadBalancer: LoadBalancerFromSchema(respBody.LoadBalancer),
|
||||
Action: ActionFromSchema(respBody.Action),
|
||||
}, resp, nil
|
||||
}
|
||||
|
||||
// Delete deletes a Load Balancer.
|
||||
func (c *LoadBalancerClient) Delete(ctx context.Context, loadBalancer *LoadBalancer) (*Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/load_balancers/%d", loadBalancer.ID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Do(req, nil)
|
||||
}
|
||||
|
||||
func (c *LoadBalancerClient) addTarget(ctx context.Context, loadBalancer *LoadBalancer, reqBody schema.LoadBalancerActionAddTargetRequest) (*Action, *Response, error) {
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/add_target", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.LoadBalancerActionAddTargetResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
func (c *LoadBalancerClient) removeTarget(ctx context.Context, loadBalancer *LoadBalancer, reqBody schema.LoadBalancerActionRemoveTargetRequest) (*Action, *Response, error) {
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/remove_target", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.LoadBalancerActionRemoveTargetResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// LoadBalancerAddServerTargetOpts specifies options for adding a server target
|
||||
// to a Load Balancer.
|
||||
type LoadBalancerAddServerTargetOpts struct {
|
||||
Server *Server
|
||||
UsePrivateIP *bool
|
||||
}
|
||||
|
||||
// AddServerTarget adds a server target to a Load Balancer.
|
||||
func (c *LoadBalancerClient) AddServerTarget(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerAddServerTargetOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionAddTargetRequest{
|
||||
Type: string(LoadBalancerTargetTypeServer),
|
||||
Server: &schema.LoadBalancerActionAddTargetRequestServer{
|
||||
ID: opts.Server.ID,
|
||||
},
|
||||
UsePrivateIP: opts.UsePrivateIP,
|
||||
}
|
||||
return c.addTarget(ctx, loadBalancer, reqBody)
|
||||
}
|
||||
|
||||
// RemoveServerTarget removes a server target from a Load Balancer.
|
||||
func (c *LoadBalancerClient) RemoveServerTarget(ctx context.Context, loadBalancer *LoadBalancer, server *Server) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionRemoveTargetRequest{
|
||||
Type: string(LoadBalancerTargetTypeServer),
|
||||
Server: &schema.LoadBalancerActionRemoveTargetRequestServer{
|
||||
ID: server.ID,
|
||||
},
|
||||
}
|
||||
return c.removeTarget(ctx, loadBalancer, reqBody)
|
||||
}
|
||||
|
||||
// LoadBalancerAddLabelSelectorTargetOpts specifies options for adding a label selector target
|
||||
// to a Load Balancer.
|
||||
type LoadBalancerAddLabelSelectorTargetOpts struct {
|
||||
Selector string
|
||||
UsePrivateIP *bool
|
||||
}
|
||||
|
||||
// AddLabelSelectorTarget adds a label selector target to a Load Balancer.
|
||||
func (c *LoadBalancerClient) AddLabelSelectorTarget(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerAddLabelSelectorTargetOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionAddTargetRequest{
|
||||
Type: string(LoadBalancerTargetTypeLabelSelector),
|
||||
LabelSelector: &schema.LoadBalancerActionAddTargetRequestLabelSelector{
|
||||
Selector: opts.Selector,
|
||||
},
|
||||
UsePrivateIP: opts.UsePrivateIP,
|
||||
}
|
||||
return c.addTarget(ctx, loadBalancer, reqBody)
|
||||
}
|
||||
|
||||
// RemoveLabelSelectorTarget removes a label selector target from a Load Balancer.
|
||||
func (c *LoadBalancerClient) RemoveLabelSelectorTarget(ctx context.Context, loadBalancer *LoadBalancer, labelSelector string) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionRemoveTargetRequest{
|
||||
Type: string(LoadBalancerTargetTypeLabelSelector),
|
||||
LabelSelector: &schema.LoadBalancerActionRemoveTargetRequestLabelSelector{
|
||||
Selector: labelSelector,
|
||||
},
|
||||
}
|
||||
return c.removeTarget(ctx, loadBalancer, reqBody)
|
||||
}
|
||||
|
||||
// LoadBalancerAddIPTargetOpts specifies options for adding an IP target to a
|
||||
// Load Balancer.
|
||||
type LoadBalancerAddIPTargetOpts struct {
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
// AddIPTarget adds an IP target to a Load Balancer.
|
||||
func (c *LoadBalancerClient) AddIPTarget(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerAddIPTargetOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionAddTargetRequest{
|
||||
Type: string(LoadBalancerTargetTypeIP),
|
||||
IP: &schema.LoadBalancerActionAddTargetRequestIP{IP: opts.IP.String()},
|
||||
}
|
||||
return c.addTarget(ctx, loadBalancer, reqBody)
|
||||
}
|
||||
|
||||
// RemoveIPTarget removes an IP target from a Load Balancer.
|
||||
func (c *LoadBalancerClient) RemoveIPTarget(ctx context.Context, loadBalancer *LoadBalancer, ip net.IP) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionRemoveTargetRequest{
|
||||
Type: string(LoadBalancerTargetTypeIP),
|
||||
IP: &schema.LoadBalancerActionRemoveTargetRequestIP{
|
||||
IP: ip.String(),
|
||||
},
|
||||
}
|
||||
return c.removeTarget(ctx, loadBalancer, reqBody)
|
||||
}
|
||||
|
||||
// LoadBalancerAddServiceOpts specifies options for adding a service to a Load Balancer.
|
||||
type LoadBalancerAddServiceOpts struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
ListenPort *int
|
||||
DestinationPort *int
|
||||
Proxyprotocol *bool
|
||||
HTTP *LoadBalancerAddServiceOptsHTTP
|
||||
HealthCheck *LoadBalancerAddServiceOptsHealthCheck
|
||||
}
|
||||
|
||||
// LoadBalancerAddServiceOptsHTTP holds options for specifying an HTTP service
|
||||
// when adding a service to a Load Balancer.
|
||||
type LoadBalancerAddServiceOptsHTTP struct {
|
||||
CookieName *string
|
||||
CookieLifetime *time.Duration
|
||||
Certificates []*Certificate
|
||||
RedirectHTTP *bool
|
||||
StickySessions *bool
|
||||
}
|
||||
|
||||
// LoadBalancerAddServiceOptsHealthCheck holds options for specifying an health check
|
||||
// when adding a service to a Load Balancer.
|
||||
type LoadBalancerAddServiceOptsHealthCheck struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
Port *int
|
||||
Interval *time.Duration
|
||||
Timeout *time.Duration
|
||||
Retries *int
|
||||
HTTP *LoadBalancerAddServiceOptsHealthCheckHTTP
|
||||
}
|
||||
|
||||
// LoadBalancerAddServiceOptsHealthCheckHTTP holds options for specifying an
|
||||
// HTTP health check when adding a service to a Load Balancer.
|
||||
type LoadBalancerAddServiceOptsHealthCheckHTTP struct {
|
||||
Domain *string
|
||||
Path *string
|
||||
Response *string
|
||||
StatusCodes []string
|
||||
TLS *bool
|
||||
}
|
||||
|
||||
// AddService adds a service to a Load Balancer.
|
||||
func (c *LoadBalancerClient) AddService(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerAddServiceOpts) (*Action, *Response, error) {
|
||||
reqBody := loadBalancerAddServiceOptsToSchema(opts)
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/add_service", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.LoadBalancerActionAddServiceResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// LoadBalancerUpdateServiceOpts specifies options for updating a service.
|
||||
type LoadBalancerUpdateServiceOpts struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
DestinationPort *int
|
||||
Proxyprotocol *bool
|
||||
HTTP *LoadBalancerUpdateServiceOptsHTTP
|
||||
HealthCheck *LoadBalancerUpdateServiceOptsHealthCheck
|
||||
}
|
||||
|
||||
// LoadBalancerUpdateServiceOptsHTTP specifies options for updating an HTTP(S) service.
|
||||
type LoadBalancerUpdateServiceOptsHTTP struct {
|
||||
CookieName *string
|
||||
CookieLifetime *time.Duration
|
||||
Certificates []*Certificate
|
||||
RedirectHTTP *bool
|
||||
StickySessions *bool
|
||||
}
|
||||
|
||||
// LoadBalancerUpdateServiceOptsHealthCheck specifies options for updating
|
||||
// a service's health check.
|
||||
type LoadBalancerUpdateServiceOptsHealthCheck struct {
|
||||
Protocol LoadBalancerServiceProtocol
|
||||
Port *int
|
||||
Interval *time.Duration
|
||||
Timeout *time.Duration
|
||||
Retries *int
|
||||
HTTP *LoadBalancerUpdateServiceOptsHealthCheckHTTP
|
||||
}
|
||||
|
||||
// LoadBalancerUpdateServiceOptsHealthCheckHTTP specifies options for updating
|
||||
// the HTTP-specific settings of a service's health check.
|
||||
type LoadBalancerUpdateServiceOptsHealthCheckHTTP struct {
|
||||
Domain *string
|
||||
Path *string
|
||||
Response *string
|
||||
StatusCodes []string
|
||||
TLS *bool
|
||||
}
|
||||
|
||||
// UpdateService updates a Load Balancer service.
|
||||
func (c *LoadBalancerClient) UpdateService(ctx context.Context, loadBalancer *LoadBalancer, listenPort int, opts LoadBalancerUpdateServiceOpts) (*Action, *Response, error) {
|
||||
reqBody := loadBalancerUpdateServiceOptsToSchema(opts)
|
||||
reqBody.ListenPort = listenPort
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/update_service", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.LoadBalancerActionUpdateServiceResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// DeleteService deletes a Load Balancer service.
|
||||
func (c *LoadBalancerClient) DeleteService(ctx context.Context, loadBalancer *LoadBalancer, listenPort int) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerDeleteServiceRequest{
|
||||
ListenPort: listenPort,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/delete_service", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.LoadBalancerDeleteServiceResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// LoadBalancerChangeProtectionOpts specifies options for changing the resource protection level of a Load Balancer.
|
||||
type LoadBalancerChangeProtectionOpts struct {
|
||||
Delete *bool
|
||||
}
|
||||
|
||||
// ChangeProtection changes the resource protection level of a Load Balancer.
|
||||
func (c *LoadBalancerClient) ChangeProtection(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerChangeProtectionOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionChangeProtectionRequest{
|
||||
Delete: opts.Delete,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/change_protection", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.LoadBalancerActionChangeProtectionResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// LoadBalancerChangeAlgorithmOpts specifies options for changing the algorithm of a Load Balancer.
|
||||
type LoadBalancerChangeAlgorithmOpts struct {
|
||||
Type LoadBalancerAlgorithmType
|
||||
}
|
||||
|
||||
// ChangeAlgorithm changes the algorithm of a Load Balancer.
|
||||
func (c *LoadBalancerClient) ChangeAlgorithm(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerChangeAlgorithmOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionChangeAlgorithmRequest{
|
||||
Type: string(opts.Type),
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/change_algorithm", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.LoadBalancerActionChangeAlgorithmResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// LoadBalancerAttachToNetworkOpts specifies options for attaching a Load Balancer to a network.
|
||||
type LoadBalancerAttachToNetworkOpts struct {
|
||||
Network *Network
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
// AttachToNetwork attaches a Load Balancer to a network.
|
||||
func (c *LoadBalancerClient) AttachToNetwork(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerAttachToNetworkOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionAttachToNetworkRequest{
|
||||
Network: opts.Network.ID,
|
||||
}
|
||||
if opts.IP != nil {
|
||||
reqBody.IP = String(opts.IP.String())
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/attach_to_network", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.LoadBalancerActionAttachToNetworkResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// LoadBalancerDetachFromNetworkOpts specifies options for detaching a Load Balancer from a network.
|
||||
type LoadBalancerDetachFromNetworkOpts struct {
|
||||
Network *Network
|
||||
}
|
||||
|
||||
// DetachFromNetwork detaches a Load Balancer from a network.
|
||||
func (c *LoadBalancerClient) DetachFromNetwork(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerDetachFromNetworkOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionDetachFromNetworkRequest{
|
||||
Network: opts.Network.ID,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/detach_from_network", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.LoadBalancerActionDetachFromNetworkResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// EnablePublicInterface enables the Load Balancer's public network interface.
|
||||
func (c *LoadBalancerClient) EnablePublicInterface(ctx context.Context, loadBalancer *LoadBalancer) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/enable_public_interface", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
respBody := schema.LoadBalancerActionEnablePublicInterfaceResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// DisablePublicInterface disables the Load Balancer's public network interface.
|
||||
func (c *LoadBalancerClient) DisablePublicInterface(ctx context.Context, loadBalancer *LoadBalancer) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/disable_public_interface", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
respBody := schema.LoadBalancerActionDisablePublicInterfaceResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// LoadBalancerChangeTypeOpts specifies options for changing a Load Balancer's type.
|
||||
type LoadBalancerChangeTypeOpts struct {
|
||||
LoadBalancerType *LoadBalancerType // new Load Balancer type
|
||||
}
|
||||
|
||||
// ChangeType changes a Load Balancer's type.
|
||||
func (c *LoadBalancerClient) ChangeType(ctx context.Context, loadBalancer *LoadBalancer, opts LoadBalancerChangeTypeOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.LoadBalancerActionChangeTypeRequest{}
|
||||
if opts.LoadBalancerType.ID != 0 {
|
||||
reqBody.LoadBalancerType = opts.LoadBalancerType.ID
|
||||
} else {
|
||||
reqBody.LoadBalancerType = opts.LoadBalancerType.Name
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/load_balancers/%d/actions/change_type", loadBalancer.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.LoadBalancerActionChangeTypeResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
126
vendor/github.com/hetznercloud/hcloud-go/hcloud/load_balancer_type.go
generated
vendored
Normal file
126
vendor/github.com/hetznercloud/hcloud-go/hcloud/load_balancer_type.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// LoadBalancerType represents a LoadBalancer type in the Hetzner Cloud.
|
||||
type LoadBalancerType struct {
|
||||
ID int
|
||||
Name string
|
||||
Description string
|
||||
MaxConnections int
|
||||
MaxServices int
|
||||
MaxTargets int
|
||||
MaxAssignedCertificates int
|
||||
Pricings []LoadBalancerTypeLocationPricing
|
||||
}
|
||||
|
||||
// LoadBalancerTypeClient is a client for the Load Balancer types API.
|
||||
type LoadBalancerTypeClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a Load Balancer type by its ID. If the Load Balancer type does not exist, nil is returned.
|
||||
func (c *LoadBalancerTypeClient) GetByID(ctx context.Context, id int) (*LoadBalancerType, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/load_balancer_types/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.LoadBalancerTypeGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return LoadBalancerTypeFromSchema(body.LoadBalancerType), resp, nil
|
||||
}
|
||||
|
||||
// GetByName retrieves a Load Balancer type by its name. If the Load Balancer type does not exist, nil is returned.
|
||||
func (c *LoadBalancerTypeClient) GetByName(ctx context.Context, name string) (*LoadBalancerType, *Response, error) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
LoadBalancerTypes, response, err := c.List(ctx, LoadBalancerTypeListOpts{Name: name})
|
||||
if len(LoadBalancerTypes) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return LoadBalancerTypes[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a Load Balancer type by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a Load Balancer type by its name. If the Load Balancer type does not exist, nil is returned.
|
||||
func (c *LoadBalancerTypeClient) Get(ctx context.Context, idOrName string) (*LoadBalancerType, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// LoadBalancerTypeListOpts specifies options for listing Load Balancer types.
|
||||
type LoadBalancerTypeListOpts struct {
|
||||
ListOpts
|
||||
Name string
|
||||
}
|
||||
|
||||
func (l LoadBalancerTypeListOpts) values() url.Values {
|
||||
vals := l.ListOpts.values()
|
||||
if l.Name != "" {
|
||||
vals.Add("name", l.Name)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// List returns a list of Load Balancer types for a specific page.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *LoadBalancerTypeClient) List(ctx context.Context, opts LoadBalancerTypeListOpts) ([]*LoadBalancerType, *Response, error) {
|
||||
path := "/load_balancer_types?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.LoadBalancerTypeListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
LoadBalancerTypes := make([]*LoadBalancerType, 0, len(body.LoadBalancerTypes))
|
||||
for _, s := range body.LoadBalancerTypes {
|
||||
LoadBalancerTypes = append(LoadBalancerTypes, LoadBalancerTypeFromSchema(s))
|
||||
}
|
||||
return LoadBalancerTypes, resp, nil
|
||||
}
|
||||
|
||||
// All returns all Load Balancer types.
|
||||
func (c *LoadBalancerTypeClient) All(ctx context.Context) ([]*LoadBalancerType, error) {
|
||||
allLoadBalancerTypes := []*LoadBalancerType{}
|
||||
|
||||
opts := LoadBalancerTypeListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
LoadBalancerTypes, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allLoadBalancerTypes = append(allLoadBalancerTypes, LoadBalancerTypes...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allLoadBalancerTypes, nil
|
||||
}
|
126
vendor/github.com/hetznercloud/hcloud-go/hcloud/location.go
generated
vendored
Normal file
126
vendor/github.com/hetznercloud/hcloud-go/hcloud/location.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Location represents a location in the Hetzner Cloud.
|
||||
type Location struct {
|
||||
ID int
|
||||
Name string
|
||||
Description string
|
||||
Country string
|
||||
City string
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
NetworkZone NetworkZone
|
||||
}
|
||||
|
||||
// LocationClient is a client for the location API.
|
||||
type LocationClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a location by its ID. If the location does not exist, nil is returned.
|
||||
func (c *LocationClient) GetByID(ctx context.Context, id int) (*Location, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/locations/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.LocationGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, resp, err
|
||||
}
|
||||
return LocationFromSchema(body.Location), resp, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
locations, response, err := c.List(ctx, LocationListOpts{Name: name})
|
||||
if len(locations) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return locations[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a location by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a location by its name. If the location does not exist, nil is returned.
|
||||
func (c *LocationClient) Get(ctx context.Context, idOrName string) (*Location, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *LocationClient) List(ctx context.Context, opts LocationListOpts) ([]*Location, *Response, error) {
|
||||
path := "/locations?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.LocationListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
locations := make([]*Location, 0, len(body.Locations))
|
||||
for _, i := range body.Locations {
|
||||
locations = append(locations, LocationFromSchema(i))
|
||||
}
|
||||
return locations, resp, nil
|
||||
}
|
||||
|
||||
// All returns all locations.
|
||||
func (c *LocationClient) All(ctx context.Context) ([]*Location, error) {
|
||||
allLocations := []*Location{}
|
||||
|
||||
opts := LocationListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
locations, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allLocations = append(allLocations, locations...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allLocations, nil
|
||||
}
|
454
vendor/github.com/hetznercloud/hcloud-go/hcloud/network.go
generated
vendored
Normal file
454
vendor/github.com/hetznercloud/hcloud-go/hcloud/network.go
generated
vendored
Normal file
|
@ -0,0 +1,454 @@
|
|||
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 (
|
||||
NetworkSubnetTypeCloud NetworkSubnetType = "cloud"
|
||||
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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
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),
|
||||
NetworkZone: string(opts.Subnet.NetworkZone),
|
||||
}
|
||||
if opts.Subnet.IPRange != nil {
|
||||
reqBody.IPRange = opts.Subnet.IPRange.String()
|
||||
}
|
||||
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
|
||||
}
|
95
vendor/github.com/hetznercloud/hcloud-go/hcloud/pricing.go
generated
vendored
Normal file
95
vendor/github.com/hetznercloud/hcloud-go/hcloud/pricing.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Pricing specifies pricing information for various resources.
|
||||
type Pricing struct {
|
||||
Image ImagePricing
|
||||
FloatingIP FloatingIPPricing
|
||||
Traffic TrafficPricing
|
||||
ServerBackup ServerBackupPricing
|
||||
ServerTypes []ServerTypePricing
|
||||
LoadBalancerTypes []LoadBalancerTypePricing
|
||||
}
|
||||
|
||||
// Price represents a price. Net amount, gross amount, as well as VAT rate are
|
||||
// specified as strings and it is the user's responsibility to convert them to
|
||||
// appropriate types for calculations.
|
||||
type Price struct {
|
||||
Currency string
|
||||
VATRate string
|
||||
Net string
|
||||
Gross string
|
||||
}
|
||||
|
||||
// ImagePricing provides pricing information for imaegs.
|
||||
type ImagePricing struct {
|
||||
PerGBMonth Price
|
||||
}
|
||||
|
||||
// FloatingIPPricing provides pricing information for Floating IPs.
|
||||
type FloatingIPPricing struct {
|
||||
Monthly Price
|
||||
}
|
||||
|
||||
// TrafficPricing provides pricing information for traffic.
|
||||
type TrafficPricing struct {
|
||||
PerTB Price
|
||||
}
|
||||
|
||||
// ServerBackupPricing provides pricing information for server backups.
|
||||
type ServerBackupPricing struct {
|
||||
Percentage string
|
||||
}
|
||||
|
||||
// ServerTypePricing provides pricing information for a server type.
|
||||
type ServerTypePricing struct {
|
||||
ServerType *ServerType
|
||||
Pricings []ServerTypeLocationPricing
|
||||
}
|
||||
|
||||
// ServerTypeLocationPricing provides pricing information for a server type
|
||||
// at a location.
|
||||
type ServerTypeLocationPricing struct {
|
||||
Location *Location
|
||||
Hourly Price
|
||||
Monthly Price
|
||||
}
|
||||
|
||||
// LoadBalancerTypePricing provides pricing information for a Load Balancer type.
|
||||
type LoadBalancerTypePricing struct {
|
||||
LoadBalancerType *LoadBalancerType
|
||||
Pricings []LoadBalancerTypeLocationPricing
|
||||
}
|
||||
|
||||
// LoadBalancerTypeLocationPricing provides pricing information for a Load Balancer type
|
||||
// at a location.
|
||||
type LoadBalancerTypeLocationPricing struct {
|
||||
Location *Location
|
||||
Hourly Price
|
||||
Monthly Price
|
||||
}
|
||||
|
||||
// PricingClient is a client for the pricing API.
|
||||
type PricingClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Get retrieves pricing information.
|
||||
func (c *PricingClient) Get(ctx context.Context) (Pricing, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", "/pricing", nil)
|
||||
if err != nil {
|
||||
return Pricing{}, nil, err
|
||||
}
|
||||
|
||||
var body schema.PricingGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return Pricing{}, nil, err
|
||||
}
|
||||
return PricingFromSchema(body.Pricing), resp, nil
|
||||
}
|
891
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema.go
generated
vendored
Normal file
891
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema.go
generated
vendored
Normal file
|
@ -0,0 +1,891 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// This file provides converter functions to convert models in the
|
||||
// schema package to models in the hcloud package and vice versa.
|
||||
|
||||
// ActionFromSchema converts a schema.Action to an Action.
|
||||
func ActionFromSchema(s schema.Action) *Action {
|
||||
action := &Action{
|
||||
ID: s.ID,
|
||||
Status: ActionStatus(s.Status),
|
||||
Command: s.Command,
|
||||
Progress: s.Progress,
|
||||
Started: s.Started,
|
||||
Resources: []*ActionResource{},
|
||||
}
|
||||
if s.Finished != nil {
|
||||
action.Finished = *s.Finished
|
||||
}
|
||||
if s.Error != nil {
|
||||
action.ErrorCode = s.Error.Code
|
||||
action.ErrorMessage = s.Error.Message
|
||||
}
|
||||
for _, r := range s.Resources {
|
||||
action.Resources = append(action.Resources, &ActionResource{
|
||||
ID: r.ID,
|
||||
Type: ActionResourceType(r.Type),
|
||||
})
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
// ActionsFromSchema converts a slice of schema.Action to a slice of Action.
|
||||
func ActionsFromSchema(s []schema.Action) []*Action {
|
||||
var actions []*Action
|
||||
for _, a := range s {
|
||||
actions = append(actions, ActionFromSchema(a))
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
// FloatingIPFromSchema converts a schema.FloatingIP to a FloatingIP.
|
||||
func FloatingIPFromSchema(s schema.FloatingIP) *FloatingIP {
|
||||
f := &FloatingIP{
|
||||
ID: s.ID,
|
||||
Type: FloatingIPType(s.Type),
|
||||
HomeLocation: LocationFromSchema(s.HomeLocation),
|
||||
Created: s.Created,
|
||||
Blocked: s.Blocked,
|
||||
Protection: FloatingIPProtection{
|
||||
Delete: s.Protection.Delete,
|
||||
},
|
||||
Name: s.Name,
|
||||
}
|
||||
if s.Description != nil {
|
||||
f.Description = *s.Description
|
||||
}
|
||||
if s.Server != nil {
|
||||
f.Server = &Server{ID: *s.Server}
|
||||
}
|
||||
if f.Type == FloatingIPTypeIPv4 {
|
||||
f.IP = net.ParseIP(s.IP)
|
||||
} else {
|
||||
f.IP, f.Network, _ = net.ParseCIDR(s.IP)
|
||||
}
|
||||
f.DNSPtr = map[string]string{}
|
||||
for _, entry := range s.DNSPtr {
|
||||
f.DNSPtr[entry.IP] = entry.DNSPtr
|
||||
}
|
||||
f.Labels = map[string]string{}
|
||||
for key, value := range s.Labels {
|
||||
f.Labels[key] = value
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// ISOFromSchema converts a schema.ISO to an ISO.
|
||||
func ISOFromSchema(s schema.ISO) *ISO {
|
||||
return &ISO{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
Type: ISOType(s.Type),
|
||||
Deprecated: s.Deprecated,
|
||||
}
|
||||
}
|
||||
|
||||
// LocationFromSchema converts a schema.Location to a Location.
|
||||
func LocationFromSchema(s schema.Location) *Location {
|
||||
return &Location{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
Country: s.Country,
|
||||
City: s.City,
|
||||
Latitude: s.Latitude,
|
||||
Longitude: s.Longitude,
|
||||
NetworkZone: NetworkZone(s.NetworkZone),
|
||||
}
|
||||
}
|
||||
|
||||
// DatacenterFromSchema converts a schema.Datacenter to a Datacenter.
|
||||
func DatacenterFromSchema(s schema.Datacenter) *Datacenter {
|
||||
d := &Datacenter{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
Location: LocationFromSchema(s.Location),
|
||||
ServerTypes: DatacenterServerTypes{
|
||||
Available: []*ServerType{},
|
||||
Supported: []*ServerType{},
|
||||
},
|
||||
}
|
||||
for _, t := range s.ServerTypes.Available {
|
||||
d.ServerTypes.Available = append(d.ServerTypes.Available, &ServerType{ID: t})
|
||||
}
|
||||
for _, t := range s.ServerTypes.Supported {
|
||||
d.ServerTypes.Supported = append(d.ServerTypes.Supported, &ServerType{ID: t})
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// ServerFromSchema converts a schema.Server to a Server.
|
||||
func ServerFromSchema(s schema.Server) *Server {
|
||||
server := &Server{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Status: ServerStatus(s.Status),
|
||||
Created: s.Created,
|
||||
PublicNet: ServerPublicNetFromSchema(s.PublicNet),
|
||||
ServerType: ServerTypeFromSchema(s.ServerType),
|
||||
IncludedTraffic: s.IncludedTraffic,
|
||||
RescueEnabled: s.RescueEnabled,
|
||||
Datacenter: DatacenterFromSchema(s.Datacenter),
|
||||
Locked: s.Locked,
|
||||
Protection: ServerProtection{
|
||||
Delete: s.Protection.Delete,
|
||||
Rebuild: s.Protection.Rebuild,
|
||||
},
|
||||
}
|
||||
if s.Image != nil {
|
||||
server.Image = ImageFromSchema(*s.Image)
|
||||
}
|
||||
if s.BackupWindow != nil {
|
||||
server.BackupWindow = *s.BackupWindow
|
||||
}
|
||||
if s.OutgoingTraffic != nil {
|
||||
server.OutgoingTraffic = *s.OutgoingTraffic
|
||||
}
|
||||
if s.IngoingTraffic != nil {
|
||||
server.IngoingTraffic = *s.IngoingTraffic
|
||||
}
|
||||
if s.ISO != nil {
|
||||
server.ISO = ISOFromSchema(*s.ISO)
|
||||
}
|
||||
server.Labels = map[string]string{}
|
||||
for key, value := range s.Labels {
|
||||
server.Labels[key] = value
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// ServerPublicNetFromSchema converts a schema.ServerPublicNet to a ServerPublicNet.
|
||||
func ServerPublicNetFromSchema(s schema.ServerPublicNet) ServerPublicNet {
|
||||
publicNet := ServerPublicNet{
|
||||
IPv4: ServerPublicNetIPv4FromSchema(s.IPv4),
|
||||
IPv6: ServerPublicNetIPv6FromSchema(s.IPv6),
|
||||
}
|
||||
for _, id := range s.FloatingIPs {
|
||||
publicNet.FloatingIPs = append(publicNet.FloatingIPs, &FloatingIP{ID: id})
|
||||
}
|
||||
return publicNet
|
||||
}
|
||||
|
||||
// ServerPublicNetIPv4FromSchema converts a schema.ServerPublicNetIPv4 to
|
||||
// a ServerPublicNetIPv4.
|
||||
func ServerPublicNetIPv4FromSchema(s schema.ServerPublicNetIPv4) ServerPublicNetIPv4 {
|
||||
return ServerPublicNetIPv4{
|
||||
IP: net.ParseIP(s.IP),
|
||||
Blocked: s.Blocked,
|
||||
DNSPtr: s.DNSPtr,
|
||||
}
|
||||
}
|
||||
|
||||
// ServerPublicNetIPv6FromSchema converts a schema.ServerPublicNetIPv6 to
|
||||
// a ServerPublicNetIPv6.
|
||||
func ServerPublicNetIPv6FromSchema(s schema.ServerPublicNetIPv6) ServerPublicNetIPv6 {
|
||||
ipv6 := ServerPublicNetIPv6{
|
||||
Blocked: s.Blocked,
|
||||
DNSPtr: map[string]string{},
|
||||
}
|
||||
ipv6.IP, ipv6.Network, _ = net.ParseCIDR(s.IP)
|
||||
|
||||
for _, dnsPtr := range s.DNSPtr {
|
||||
ipv6.DNSPtr[dnsPtr.IP] = dnsPtr.DNSPtr
|
||||
}
|
||||
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{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
Cores: s.Cores,
|
||||
Memory: s.Memory,
|
||||
Disk: s.Disk,
|
||||
StorageType: StorageType(s.StorageType),
|
||||
CPUType: CPUType(s.CPUType),
|
||||
}
|
||||
for _, price := range s.Prices {
|
||||
st.Pricings = append(st.Pricings, ServerTypeLocationPricing{
|
||||
Location: &Location{Name: price.Location},
|
||||
Hourly: Price{
|
||||
Net: price.PriceHourly.Net,
|
||||
Gross: price.PriceHourly.Gross,
|
||||
},
|
||||
Monthly: Price{
|
||||
Net: price.PriceMonthly.Net,
|
||||
Gross: price.PriceMonthly.Gross,
|
||||
},
|
||||
})
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
// SSHKeyFromSchema converts a schema.SSHKey to a SSHKey.
|
||||
func SSHKeyFromSchema(s schema.SSHKey) *SSHKey {
|
||||
sshKey := &SSHKey{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Fingerprint: s.Fingerprint,
|
||||
PublicKey: s.PublicKey,
|
||||
Created: s.Created,
|
||||
}
|
||||
sshKey.Labels = map[string]string{}
|
||||
for key, value := range s.Labels {
|
||||
sshKey.Labels[key] = value
|
||||
}
|
||||
return sshKey
|
||||
}
|
||||
|
||||
// ImageFromSchema converts a schema.Image to an Image.
|
||||
func ImageFromSchema(s schema.Image) *Image {
|
||||
i := &Image{
|
||||
ID: s.ID,
|
||||
Type: ImageType(s.Type),
|
||||
Status: ImageStatus(s.Status),
|
||||
Description: s.Description,
|
||||
DiskSize: s.DiskSize,
|
||||
Created: s.Created,
|
||||
RapidDeploy: s.RapidDeploy,
|
||||
OSFlavor: s.OSFlavor,
|
||||
Protection: ImageProtection{
|
||||
Delete: s.Protection.Delete,
|
||||
},
|
||||
Deprecated: s.Deprecated,
|
||||
}
|
||||
if s.Name != nil {
|
||||
i.Name = *s.Name
|
||||
}
|
||||
if s.ImageSize != nil {
|
||||
i.ImageSize = *s.ImageSize
|
||||
}
|
||||
if s.OSVersion != nil {
|
||||
i.OSVersion = *s.OSVersion
|
||||
}
|
||||
if s.CreatedFrom != nil {
|
||||
i.CreatedFrom = &Server{
|
||||
ID: s.CreatedFrom.ID,
|
||||
Name: s.CreatedFrom.Name,
|
||||
}
|
||||
}
|
||||
if s.BoundTo != nil {
|
||||
i.BoundTo = &Server{
|
||||
ID: *s.BoundTo,
|
||||
}
|
||||
}
|
||||
i.Labels = map[string]string{}
|
||||
for key, value := range s.Labels {
|
||||
i.Labels[key] = value
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// VolumeFromSchema converts a schema.Volume to a Volume.
|
||||
func VolumeFromSchema(s schema.Volume) *Volume {
|
||||
v := &Volume{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Location: LocationFromSchema(s.Location),
|
||||
Size: s.Size,
|
||||
Status: VolumeStatus(s.Status),
|
||||
LinuxDevice: s.LinuxDevice,
|
||||
Protection: VolumeProtection{
|
||||
Delete: s.Protection.Delete,
|
||||
},
|
||||
Created: s.Created,
|
||||
}
|
||||
if s.Server != nil {
|
||||
v.Server = &Server{ID: *s.Server}
|
||||
}
|
||||
v.Labels = map[string]string{}
|
||||
for key, value := range s.Labels {
|
||||
v.Labels[key] = value
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// LoadBalancerTypeFromSchema converts a schema.LoadBalancerType to a LoadBalancerType.
|
||||
func LoadBalancerTypeFromSchema(s schema.LoadBalancerType) *LoadBalancerType {
|
||||
lt := &LoadBalancerType{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
MaxConnections: s.MaxConnections,
|
||||
MaxServices: s.MaxServices,
|
||||
MaxTargets: s.MaxTargets,
|
||||
MaxAssignedCertificates: s.MaxAssignedCertificates,
|
||||
}
|
||||
for _, price := range s.Prices {
|
||||
lt.Pricings = append(lt.Pricings, LoadBalancerTypeLocationPricing{
|
||||
Location: &Location{Name: price.Location},
|
||||
Hourly: Price{
|
||||
Net: price.PriceHourly.Net,
|
||||
Gross: price.PriceHourly.Gross,
|
||||
},
|
||||
Monthly: Price{
|
||||
Net: price.PriceMonthly.Net,
|
||||
Gross: price.PriceMonthly.Gross,
|
||||
},
|
||||
})
|
||||
}
|
||||
return lt
|
||||
}
|
||||
|
||||
// LoadBalancerFromSchema converts a schema.LoadBalancer to a LoadBalancer.
|
||||
func LoadBalancerFromSchema(s schema.LoadBalancer) *LoadBalancer {
|
||||
l := &LoadBalancer{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
PublicNet: LoadBalancerPublicNet{
|
||||
Enabled: s.PublicNet.Enabled,
|
||||
IPv4: LoadBalancerPublicNetIPv4{
|
||||
IP: net.ParseIP(s.PublicNet.IPv4.IP),
|
||||
},
|
||||
IPv6: LoadBalancerPublicNetIPv6{
|
||||
IP: net.ParseIP(s.PublicNet.IPv6.IP),
|
||||
},
|
||||
},
|
||||
Location: LocationFromSchema(s.Location),
|
||||
LoadBalancerType: LoadBalancerTypeFromSchema(s.LoadBalancerType),
|
||||
Algorithm: LoadBalancerAlgorithm{Type: LoadBalancerAlgorithmType(s.Algorithm.Type)},
|
||||
Protection: LoadBalancerProtection{
|
||||
Delete: s.Protection.Delete,
|
||||
},
|
||||
Labels: map[string]string{},
|
||||
Created: s.Created,
|
||||
IncludedTraffic: s.IncludedTraffic,
|
||||
}
|
||||
for _, privateNet := range s.PrivateNet {
|
||||
l.PrivateNet = append(l.PrivateNet, LoadBalancerPrivateNet{
|
||||
Network: &Network{ID: privateNet.Network},
|
||||
IP: net.ParseIP(privateNet.IP),
|
||||
})
|
||||
}
|
||||
if s.OutgoingTraffic != nil {
|
||||
l.OutgoingTraffic = *s.OutgoingTraffic
|
||||
}
|
||||
if s.IngoingTraffic != nil {
|
||||
l.IngoingTraffic = *s.IngoingTraffic
|
||||
}
|
||||
for _, service := range s.Services {
|
||||
l.Services = append(l.Services, LoadBalancerServiceFromSchema(service))
|
||||
}
|
||||
for _, target := range s.Targets {
|
||||
l.Targets = append(l.Targets, LoadBalancerTargetFromSchema(target))
|
||||
}
|
||||
for key, value := range s.Labels {
|
||||
l.Labels[key] = value
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// LoadBalancerServiceFromSchema converts a schema.LoadBalancerService to a LoadBalancerService.
|
||||
func LoadBalancerServiceFromSchema(s schema.LoadBalancerService) LoadBalancerService {
|
||||
ls := LoadBalancerService{
|
||||
Protocol: LoadBalancerServiceProtocol(s.Protocol),
|
||||
ListenPort: s.ListenPort,
|
||||
DestinationPort: s.DestinationPort,
|
||||
Proxyprotocol: s.Proxyprotocol,
|
||||
HealthCheck: LoadBalancerServiceHealthCheckFromSchema(s.HealthCheck),
|
||||
}
|
||||
if s.HTTP != nil {
|
||||
ls.HTTP = LoadBalancerServiceHTTP{
|
||||
CookieName: s.HTTP.CookieName,
|
||||
CookieLifetime: time.Duration(s.HTTP.CookieLifetime) * time.Second,
|
||||
RedirectHTTP: s.HTTP.RedirectHTTP,
|
||||
StickySessions: s.HTTP.StickySessions,
|
||||
}
|
||||
for _, certificateID := range s.HTTP.Certificates {
|
||||
ls.HTTP.Certificates = append(ls.HTTP.Certificates, &Certificate{ID: certificateID})
|
||||
}
|
||||
}
|
||||
return ls
|
||||
}
|
||||
|
||||
// LoadBalancerServiceHealthCheckFromSchema converts a schema.LoadBalancerServiceHealthCheck to a LoadBalancerServiceHealthCheck.
|
||||
func LoadBalancerServiceHealthCheckFromSchema(s *schema.LoadBalancerServiceHealthCheck) LoadBalancerServiceHealthCheck {
|
||||
lsh := LoadBalancerServiceHealthCheck{
|
||||
Protocol: LoadBalancerServiceProtocol(s.Protocol),
|
||||
Port: s.Port,
|
||||
Interval: time.Duration(s.Interval) * time.Second,
|
||||
Retries: s.Retries,
|
||||
Timeout: time.Duration(s.Timeout) * time.Second,
|
||||
}
|
||||
if s.HTTP != nil {
|
||||
lsh.HTTP = &LoadBalancerServiceHealthCheckHTTP{
|
||||
Domain: s.HTTP.Domain,
|
||||
Path: s.HTTP.Path,
|
||||
Response: s.HTTP.Response,
|
||||
StatusCodes: s.HTTP.StatusCodes,
|
||||
TLS: s.HTTP.TLS,
|
||||
}
|
||||
}
|
||||
return lsh
|
||||
}
|
||||
|
||||
// LoadBalancerTargetFromSchema converts a schema.LoadBalancerTarget to a LoadBalancerTarget.
|
||||
func LoadBalancerTargetFromSchema(s schema.LoadBalancerTarget) LoadBalancerTarget {
|
||||
lt := LoadBalancerTarget{
|
||||
Type: LoadBalancerTargetType(s.Type),
|
||||
UsePrivateIP: s.UsePrivateIP,
|
||||
}
|
||||
if s.Server != nil {
|
||||
lt.Server = &LoadBalancerTargetServer{
|
||||
Server: &Server{ID: s.Server.ID},
|
||||
}
|
||||
}
|
||||
if s.LabelSelector != nil {
|
||||
lt.LabelSelector = &LoadBalancerTargetLabelSelector{
|
||||
Selector: s.LabelSelector.Selector,
|
||||
}
|
||||
}
|
||||
if s.IP != nil {
|
||||
lt.IP = &LoadBalancerTargetIP{IP: s.IP.IP}
|
||||
}
|
||||
|
||||
for _, healthStatus := range s.HealthStatus {
|
||||
lt.HealthStatus = append(lt.HealthStatus, LoadBalancerTargetHealthStatusFromSchema(healthStatus))
|
||||
}
|
||||
for _, target := range s.Targets {
|
||||
lt.Targets = append(lt.Targets, LoadBalancerTargetFromSchema(target))
|
||||
}
|
||||
return lt
|
||||
}
|
||||
|
||||
// LoadBalancerTargetHealthStatusFromSchema converts a schema.LoadBalancerTarget to a LoadBalancerTarget.
|
||||
func LoadBalancerTargetHealthStatusFromSchema(s schema.LoadBalancerTargetHealthStatus) LoadBalancerTargetHealthStatus {
|
||||
return LoadBalancerTargetHealthStatus{
|
||||
ListenPort: s.ListenPort,
|
||||
Status: LoadBalancerTargetHealthStatusStatus(s.Status),
|
||||
}
|
||||
}
|
||||
|
||||
// CertificateFromSchema converts a schema.Certificate to a Certificate.
|
||||
func CertificateFromSchema(s schema.Certificate) *Certificate {
|
||||
c := &Certificate{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Certificate: s.Certificate,
|
||||
Created: s.Created,
|
||||
NotValidBefore: s.NotValidBefore,
|
||||
NotValidAfter: s.NotValidAfter,
|
||||
DomainNames: s.DomainNames,
|
||||
Fingerprint: s.Fingerprint,
|
||||
}
|
||||
if len(s.Labels) > 0 {
|
||||
c.Labels = make(map[string]string)
|
||||
}
|
||||
for key, value := range s.Labels {
|
||||
c.Labels[key] = value
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// PaginationFromSchema converts a schema.MetaPagination to a Pagination.
|
||||
func PaginationFromSchema(s schema.MetaPagination) Pagination {
|
||||
return Pagination{
|
||||
Page: s.Page,
|
||||
PerPage: s.PerPage,
|
||||
PreviousPage: s.PreviousPage,
|
||||
NextPage: s.NextPage,
|
||||
LastPage: s.LastPage,
|
||||
TotalEntries: s.TotalEntries,
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorFromSchema converts a schema.Error to an Error.
|
||||
func ErrorFromSchema(s schema.Error) Error {
|
||||
e := Error{
|
||||
Code: ErrorCode(s.Code),
|
||||
Message: s.Message,
|
||||
}
|
||||
|
||||
switch d := s.Details.(type) {
|
||||
case schema.ErrorDetailsInvalidInput:
|
||||
details := ErrorDetailsInvalidInput{
|
||||
Fields: []ErrorDetailsInvalidInputField{},
|
||||
}
|
||||
for _, field := range d.Fields {
|
||||
details.Fields = append(details.Fields, ErrorDetailsInvalidInputField{
|
||||
Name: field.Name,
|
||||
Messages: field.Messages,
|
||||
})
|
||||
}
|
||||
e.Details = details
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// PricingFromSchema converts a schema.Pricing to a Pricing.
|
||||
func PricingFromSchema(s schema.Pricing) Pricing {
|
||||
p := Pricing{
|
||||
Image: ImagePricing{
|
||||
PerGBMonth: Price{
|
||||
Currency: s.Currency,
|
||||
VATRate: s.VATRate,
|
||||
Net: s.Image.PricePerGBMonth.Net,
|
||||
Gross: s.Image.PricePerGBMonth.Gross,
|
||||
},
|
||||
},
|
||||
FloatingIP: FloatingIPPricing{
|
||||
Monthly: Price{
|
||||
Currency: s.Currency,
|
||||
VATRate: s.VATRate,
|
||||
Net: s.FloatingIP.PriceMonthly.Net,
|
||||
Gross: s.FloatingIP.PriceMonthly.Gross,
|
||||
},
|
||||
},
|
||||
Traffic: TrafficPricing{
|
||||
PerTB: Price{
|
||||
Currency: s.Currency,
|
||||
VATRate: s.VATRate,
|
||||
Net: s.Traffic.PricePerTB.Net,
|
||||
Gross: s.Traffic.PricePerTB.Gross,
|
||||
},
|
||||
},
|
||||
ServerBackup: ServerBackupPricing{
|
||||
Percentage: s.ServerBackup.Percentage,
|
||||
},
|
||||
}
|
||||
for _, serverType := range s.ServerTypes {
|
||||
var pricings []ServerTypeLocationPricing
|
||||
for _, price := range serverType.Prices {
|
||||
pricings = append(pricings, ServerTypeLocationPricing{
|
||||
Location: &Location{Name: price.Location},
|
||||
Hourly: Price{
|
||||
Currency: s.Currency,
|
||||
VATRate: s.VATRate,
|
||||
Net: price.PriceHourly.Net,
|
||||
Gross: price.PriceHourly.Gross,
|
||||
},
|
||||
Monthly: Price{
|
||||
Currency: s.Currency,
|
||||
VATRate: s.VATRate,
|
||||
Net: price.PriceMonthly.Net,
|
||||
Gross: price.PriceMonthly.Gross,
|
||||
},
|
||||
})
|
||||
}
|
||||
p.ServerTypes = append(p.ServerTypes, ServerTypePricing{
|
||||
ServerType: &ServerType{
|
||||
ID: serverType.ID,
|
||||
Name: serverType.Name,
|
||||
},
|
||||
Pricings: pricings,
|
||||
})
|
||||
}
|
||||
for _, loadBalancerType := range s.LoadBalancerTypes {
|
||||
var pricings []LoadBalancerTypeLocationPricing
|
||||
for _, price := range loadBalancerType.Prices {
|
||||
pricings = append(pricings, LoadBalancerTypeLocationPricing{
|
||||
Location: &Location{Name: price.Location},
|
||||
Hourly: Price{
|
||||
Currency: s.Currency,
|
||||
VATRate: s.VATRate,
|
||||
Net: price.PriceHourly.Net,
|
||||
Gross: price.PriceHourly.Gross,
|
||||
},
|
||||
Monthly: Price{
|
||||
Currency: s.Currency,
|
||||
VATRate: s.VATRate,
|
||||
Net: price.PriceMonthly.Net,
|
||||
Gross: price.PriceMonthly.Gross,
|
||||
},
|
||||
})
|
||||
}
|
||||
p.LoadBalancerTypes = append(p.LoadBalancerTypes, LoadBalancerTypePricing{
|
||||
LoadBalancerType: &LoadBalancerType{
|
||||
ID: loadBalancerType.ID,
|
||||
Name: loadBalancerType.Name,
|
||||
},
|
||||
Pricings: pricings,
|
||||
})
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func loadBalancerCreateOptsToSchema(opts LoadBalancerCreateOpts) schema.LoadBalancerCreateRequest {
|
||||
req := schema.LoadBalancerCreateRequest{
|
||||
Name: opts.Name,
|
||||
PublicInterface: opts.PublicInterface,
|
||||
}
|
||||
if opts.Algorithm != nil {
|
||||
req.Algorithm = &schema.LoadBalancerCreateRequestAlgorithm{
|
||||
Type: string(opts.Algorithm.Type),
|
||||
}
|
||||
}
|
||||
if opts.LoadBalancerType.ID != 0 {
|
||||
req.LoadBalancerType = opts.LoadBalancerType.ID
|
||||
} else if opts.LoadBalancerType.Name != "" {
|
||||
req.LoadBalancerType = opts.LoadBalancerType.Name
|
||||
}
|
||||
if opts.Location != nil {
|
||||
if opts.Location.ID != 0 {
|
||||
req.Location = String(strconv.Itoa(opts.Location.ID))
|
||||
} else {
|
||||
req.Location = String(opts.Location.Name)
|
||||
}
|
||||
}
|
||||
if opts.NetworkZone != "" {
|
||||
req.NetworkZone = String(string(opts.NetworkZone))
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
req.Labels = &opts.Labels
|
||||
}
|
||||
if opts.Network != nil {
|
||||
req.Network = Int(opts.Network.ID)
|
||||
}
|
||||
for _, target := range opts.Targets {
|
||||
schemaTarget := schema.LoadBalancerCreateRequestTarget{}
|
||||
switch target.Type {
|
||||
case LoadBalancerTargetTypeServer:
|
||||
schemaTarget.Type = string(LoadBalancerTargetTypeServer)
|
||||
schemaTarget.Server = &schema.LoadBalancerCreateRequestTargetServer{ID: target.Server.Server.ID}
|
||||
case LoadBalancerTargetTypeLabelSelector:
|
||||
schemaTarget.Type = string(LoadBalancerTargetTypeLabelSelector)
|
||||
schemaTarget.LabelSelector = &schema.LoadBalancerCreateRequestTargetLabelSelector{Selector: target.LabelSelector.Selector}
|
||||
case LoadBalancerTargetTypeIP:
|
||||
schemaTarget.Type = string(LoadBalancerTargetTypeIP)
|
||||
schemaTarget.IP = &schema.LoadBalancerCreateRequestTargetIP{IP: target.IP.IP}
|
||||
}
|
||||
req.Targets = append(req.Targets, schemaTarget)
|
||||
}
|
||||
for _, service := range opts.Services {
|
||||
schemaService := schema.LoadBalancerCreateRequestService{
|
||||
Protocol: string(service.Protocol),
|
||||
ListenPort: service.ListenPort,
|
||||
DestinationPort: service.DestinationPort,
|
||||
Proxyprotocol: service.Proxyprotocol,
|
||||
}
|
||||
if service.HTTP != nil {
|
||||
schemaService.HTTP = &schema.LoadBalancerCreateRequestServiceHTTP{
|
||||
RedirectHTTP: service.HTTP.RedirectHTTP,
|
||||
StickySessions: service.HTTP.StickySessions,
|
||||
CookieName: service.HTTP.CookieName,
|
||||
}
|
||||
if sec := service.HTTP.CookieLifetime.Seconds(); sec != 0 {
|
||||
schemaService.HTTP.CookieLifetime = Int(int(sec))
|
||||
}
|
||||
if service.HTTP.Certificates != nil {
|
||||
certificates := []int{}
|
||||
for _, certificate := range service.HTTP.Certificates {
|
||||
certificates = append(certificates, certificate.ID)
|
||||
}
|
||||
schemaService.HTTP.Certificates = &certificates
|
||||
}
|
||||
}
|
||||
if service.HealthCheck != nil {
|
||||
schemaHealthCheck := &schema.LoadBalancerCreateRequestServiceHealthCheck{
|
||||
Protocol: string(service.HealthCheck.Protocol),
|
||||
Port: service.HealthCheck.Port,
|
||||
Retries: service.HealthCheck.Retries,
|
||||
}
|
||||
if service.HealthCheck.Interval != nil {
|
||||
schemaHealthCheck.Interval = Int(int(service.HealthCheck.Interval.Seconds()))
|
||||
}
|
||||
if service.HealthCheck.Timeout != nil {
|
||||
schemaHealthCheck.Timeout = Int(int(service.HealthCheck.Timeout.Seconds()))
|
||||
}
|
||||
if service.HealthCheck.HTTP != nil {
|
||||
schemaHealthCheckHTTP := &schema.LoadBalancerCreateRequestServiceHealthCheckHTTP{
|
||||
Domain: service.HealthCheck.HTTP.Domain,
|
||||
Path: service.HealthCheck.HTTP.Path,
|
||||
Response: service.HealthCheck.HTTP.Response,
|
||||
TLS: service.HealthCheck.HTTP.TLS,
|
||||
}
|
||||
if service.HealthCheck.HTTP.StatusCodes != nil {
|
||||
schemaHealthCheckHTTP.StatusCodes = &service.HealthCheck.HTTP.StatusCodes
|
||||
}
|
||||
schemaHealthCheck.HTTP = schemaHealthCheckHTTP
|
||||
}
|
||||
schemaService.HealthCheck = schemaHealthCheck
|
||||
}
|
||||
req.Services = append(req.Services, schemaService)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func loadBalancerAddServiceOptsToSchema(opts LoadBalancerAddServiceOpts) schema.LoadBalancerActionAddServiceRequest {
|
||||
req := schema.LoadBalancerActionAddServiceRequest{
|
||||
Protocol: string(opts.Protocol),
|
||||
ListenPort: opts.ListenPort,
|
||||
DestinationPort: opts.DestinationPort,
|
||||
Proxyprotocol: opts.Proxyprotocol,
|
||||
}
|
||||
if opts.HTTP != nil {
|
||||
req.HTTP = &schema.LoadBalancerActionAddServiceRequestHTTP{
|
||||
CookieName: opts.HTTP.CookieName,
|
||||
RedirectHTTP: opts.HTTP.RedirectHTTP,
|
||||
StickySessions: opts.HTTP.StickySessions,
|
||||
}
|
||||
if opts.HTTP.CookieLifetime != nil {
|
||||
req.HTTP.CookieLifetime = Int(int(opts.HTTP.CookieLifetime.Seconds()))
|
||||
}
|
||||
if opts.HTTP.Certificates != nil {
|
||||
certificates := []int{}
|
||||
for _, certificate := range opts.HTTP.Certificates {
|
||||
certificates = append(certificates, certificate.ID)
|
||||
}
|
||||
req.HTTP.Certificates = &certificates
|
||||
}
|
||||
}
|
||||
if opts.HealthCheck != nil {
|
||||
req.HealthCheck = &schema.LoadBalancerActionAddServiceRequestHealthCheck{
|
||||
Protocol: string(opts.HealthCheck.Protocol),
|
||||
Port: opts.HealthCheck.Port,
|
||||
Retries: opts.HealthCheck.Retries,
|
||||
}
|
||||
if opts.HealthCheck.Interval != nil {
|
||||
req.HealthCheck.Interval = Int(int(opts.HealthCheck.Interval.Seconds()))
|
||||
}
|
||||
if opts.HealthCheck.Timeout != nil {
|
||||
req.HealthCheck.Timeout = Int(int(opts.HealthCheck.Timeout.Seconds()))
|
||||
}
|
||||
if opts.HealthCheck.HTTP != nil {
|
||||
req.HealthCheck.HTTP = &schema.LoadBalancerActionAddServiceRequestHealthCheckHTTP{
|
||||
Domain: opts.HealthCheck.HTTP.Domain,
|
||||
Path: opts.HealthCheck.HTTP.Path,
|
||||
Response: opts.HealthCheck.HTTP.Response,
|
||||
TLS: opts.HealthCheck.HTTP.TLS,
|
||||
}
|
||||
if opts.HealthCheck.HTTP.StatusCodes != nil {
|
||||
req.HealthCheck.HTTP.StatusCodes = &opts.HealthCheck.HTTP.StatusCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func loadBalancerUpdateServiceOptsToSchema(opts LoadBalancerUpdateServiceOpts) schema.LoadBalancerActionUpdateServiceRequest {
|
||||
req := schema.LoadBalancerActionUpdateServiceRequest{
|
||||
DestinationPort: opts.DestinationPort,
|
||||
Proxyprotocol: opts.Proxyprotocol,
|
||||
}
|
||||
if opts.Protocol != "" {
|
||||
req.Protocol = String(string(opts.Protocol))
|
||||
}
|
||||
if opts.HTTP != nil {
|
||||
req.HTTP = &schema.LoadBalancerActionUpdateServiceRequestHTTP{
|
||||
CookieName: opts.HTTP.CookieName,
|
||||
RedirectHTTP: opts.HTTP.RedirectHTTP,
|
||||
StickySessions: opts.HTTP.StickySessions,
|
||||
}
|
||||
if opts.HTTP.CookieLifetime != nil {
|
||||
req.HTTP.CookieLifetime = Int(int(opts.HTTP.CookieLifetime.Seconds()))
|
||||
}
|
||||
if opts.HTTP.Certificates != nil {
|
||||
certificates := []int{}
|
||||
for _, certificate := range opts.HTTP.Certificates {
|
||||
certificates = append(certificates, certificate.ID)
|
||||
}
|
||||
req.HTTP.Certificates = &certificates
|
||||
}
|
||||
}
|
||||
if opts.HealthCheck != nil {
|
||||
req.HealthCheck = &schema.LoadBalancerActionUpdateServiceRequestHealthCheck{
|
||||
Port: opts.HealthCheck.Port,
|
||||
Retries: opts.HealthCheck.Retries,
|
||||
}
|
||||
if opts.HealthCheck.Interval != nil {
|
||||
req.HealthCheck.Interval = Int(int(opts.HealthCheck.Interval.Seconds()))
|
||||
}
|
||||
if opts.HealthCheck.Timeout != nil {
|
||||
req.HealthCheck.Timeout = Int(int(opts.HealthCheck.Timeout.Seconds()))
|
||||
}
|
||||
if opts.HealthCheck.Protocol != "" {
|
||||
req.HealthCheck.Protocol = String(string(opts.HealthCheck.Protocol))
|
||||
}
|
||||
if opts.HealthCheck.HTTP != nil {
|
||||
req.HealthCheck.HTTP = &schema.LoadBalancerActionUpdateServiceRequestHealthCheckHTTP{
|
||||
Domain: opts.HealthCheck.HTTP.Domain,
|
||||
Path: opts.HealthCheck.HTTP.Path,
|
||||
Response: opts.HealthCheck.HTTP.Response,
|
||||
TLS: opts.HealthCheck.HTTP.TLS,
|
||||
}
|
||||
if opts.HealthCheck.HTTP.StatusCodes != nil {
|
||||
req.HealthCheck.HTTP.StatusCodes = &opts.HealthCheck.HTTP.StatusCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
return req
|
||||
}
|
39
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/action.go
generated
vendored
Normal file
39
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/action.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
// Action defines the schema of an action.
|
||||
type Action struct {
|
||||
ID int `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Command string `json:"command"`
|
||||
Progress int `json:"progress"`
|
||||
Started time.Time `json:"started"`
|
||||
Finished *time.Time `json:"finished"`
|
||||
Error *ActionError `json:"error"`
|
||||
Resources []ActionResourceReference `json:"resources"`
|
||||
}
|
||||
|
||||
// ActionResourceReference defines the schema of an action resource reference.
|
||||
type ActionResourceReference struct {
|
||||
ID int `json:"id"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// ActionError defines the schema of an error embedded
|
||||
// in an action.
|
||||
type ActionError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ActionGetResponse is the schema of the response when
|
||||
// retrieving a single action.
|
||||
type ActionGetResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ActionListResponse defines the schema of the response when listing actions.
|
||||
type ActionListResponse struct {
|
||||
Actions []Action `json:"actions"`
|
||||
}
|
52
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/certificate.go
generated
vendored
Normal file
52
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/certificate.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
// Certificate defines the schema of an certificate.
|
||||
type Certificate struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Certificate string `json:"certificate"`
|
||||
Created time.Time `json:"created"`
|
||||
NotValidBefore time.Time `json:"not_valid_before"`
|
||||
NotValidAfter time.Time `json:"not_valid_after"`
|
||||
DomainNames []string `json:"domain_names"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
}
|
||||
|
||||
// CertificateListResponse defines the schema of the response when
|
||||
// listing Certificates.
|
||||
type CertificateListResponse struct {
|
||||
Certificates []Certificate `json:"certificates"`
|
||||
}
|
||||
|
||||
// CertificateGetResponse defines the schema of the response when
|
||||
// retrieving a single Certificate.
|
||||
type CertificateGetResponse struct {
|
||||
Certificate Certificate `json:"certificate"`
|
||||
}
|
||||
|
||||
// CertificateCreateRequest defines the schema of the request to create a certificate.
|
||||
type CertificateCreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// CertificateCreateResponse defines the schema of the response when creating a certificate.
|
||||
type CertificateCreateResponse struct {
|
||||
Certificate Certificate `json:"certificate"`
|
||||
}
|
||||
|
||||
// CertificateUpdateRequest defines the schema of the request to update a certificate.
|
||||
type CertificateUpdateRequest struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// CertificateUpdateResponse defines the schema of the response when updating a certificate.
|
||||
type CertificateUpdateResponse struct {
|
||||
Certificate Certificate `json:"certificate"`
|
||||
}
|
23
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/datacenter.go
generated
vendored
Normal file
23
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/datacenter.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package schema
|
||||
|
||||
// Datacenter defines the schema of a datacenter.
|
||||
type Datacenter struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Location Location `json:"location"`
|
||||
ServerTypes struct {
|
||||
Supported []int `json:"supported"`
|
||||
Available []int `json:"available"`
|
||||
} `json:"server_types"`
|
||||
}
|
||||
|
||||
// DatacenterGetResponse defines the schema of the response when retrieving a single datacenter.
|
||||
type DatacenterGetResponse struct {
|
||||
Datacenter Datacenter `json:"datacenter"`
|
||||
}
|
||||
|
||||
// DatacenterListResponse defines the schema of the response when listing datacenters.
|
||||
type DatacenterListResponse struct {
|
||||
Datacenters []Datacenter `json:"datacenters"`
|
||||
}
|
43
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/error.go
generated
vendored
Normal file
43
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/error.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package schema
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// Error represents the schema of an error response.
|
||||
type Error struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
DetailsRaw json.RawMessage `json:"details"`
|
||||
Details interface{}
|
||||
}
|
||||
|
||||
// UnmarshalJSON overrides default json unmarshalling.
|
||||
func (e *Error) UnmarshalJSON(data []byte) (err error) {
|
||||
type Alias Error
|
||||
alias := (*Alias)(e)
|
||||
if err = json.Unmarshal(data, alias); err != nil {
|
||||
return
|
||||
}
|
||||
switch e.Code {
|
||||
case "invalid_input":
|
||||
details := ErrorDetailsInvalidInput{}
|
||||
if err = json.Unmarshal(e.DetailsRaw, &details); err != nil {
|
||||
return
|
||||
}
|
||||
alias.Details = details
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ErrorResponse defines the schema of a response containing an error.
|
||||
type ErrorResponse struct {
|
||||
Error Error `json:"error"`
|
||||
}
|
||||
|
||||
// ErrorDetailsInvalidInput defines the schema of the Details field
|
||||
// of an error with code 'invalid_input'.
|
||||
type ErrorDetailsInvalidInput struct {
|
||||
Fields []struct {
|
||||
Name string `json:"name"`
|
||||
Messages []string `json:"messages"`
|
||||
} `json:"fields"`
|
||||
}
|
118
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/floating_ip.go
generated
vendored
Normal file
118
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/floating_ip.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
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"`
|
||||
DNSPtr []FloatingIPDNSPtr `json:"dns_ptr"`
|
||||
HomeLocation Location `json:"home_location"`
|
||||
Blocked bool `json:"blocked"`
|
||||
Protection FloatingIPProtection `json:"protection"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// FloatingIPProtection represents the protection level of a Floating IP.
|
||||
type FloatingIPProtection struct {
|
||||
Delete bool `json:"delete"`
|
||||
}
|
||||
|
||||
// FloatingIPDNSPtr contains reverse DNS information for a
|
||||
// IPv4 or IPv6 Floating IP.
|
||||
type FloatingIPDNSPtr struct {
|
||||
IP string `json:"ip"`
|
||||
DNSPtr string `json:"dns_ptr"`
|
||||
}
|
||||
|
||||
// FloatingIPGetResponse defines the schema of the response when
|
||||
// retrieving a single Floating IP.
|
||||
type FloatingIPGetResponse struct {
|
||||
FloatingIP FloatingIP `json:"floating_ip"`
|
||||
}
|
||||
|
||||
// FloatingIPUpdateRequest defines the schema of the request to update a Floating IP.
|
||||
type FloatingIPUpdateRequest struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// FloatingIPUpdateResponse defines the schema of the response when updating a Floating IP.
|
||||
type FloatingIPUpdateResponse struct {
|
||||
FloatingIP FloatingIP `json:"floating_ip"`
|
||||
}
|
||||
|
||||
// FloatingIPListResponse defines the schema of the response when
|
||||
// listing Floating IPs.
|
||||
type FloatingIPListResponse struct {
|
||||
FloatingIPs []FloatingIP `json:"floating_ips"`
|
||||
}
|
||||
|
||||
// FloatingIPCreateRequest defines the schema of the request to
|
||||
// create a Floating IP.
|
||||
type FloatingIPCreateRequest struct {
|
||||
Type string `json:"type"`
|
||||
HomeLocation *string `json:"home_location,omitempty"`
|
||||
Server *int `json:"server,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// FloatingIPCreateResponse defines the schema of the response
|
||||
// when creating a Floating IP.
|
||||
type FloatingIPCreateResponse struct {
|
||||
FloatingIP FloatingIP `json:"floating_ip"`
|
||||
Action *Action `json:"action"`
|
||||
}
|
||||
|
||||
// FloatingIPActionAssignRequest defines the schema of the request to
|
||||
// create an assign Floating IP action.
|
||||
type FloatingIPActionAssignRequest struct {
|
||||
Server int `json:"server"`
|
||||
}
|
||||
|
||||
// FloatingIPActionAssignResponse defines the schema of the response when
|
||||
// creating an assign action.
|
||||
type FloatingIPActionAssignResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// FloatingIPActionUnassignRequest defines the schema of the request to
|
||||
// create an unassign Floating IP action.
|
||||
type FloatingIPActionUnassignRequest struct{}
|
||||
|
||||
// FloatingIPActionUnassignResponse defines the schema of the response when
|
||||
// creating an unassign action.
|
||||
type FloatingIPActionUnassignResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// FloatingIPActionChangeDNSPtrRequest defines the schema for the request to
|
||||
// change a Floating IP's reverse DNS pointer.
|
||||
type FloatingIPActionChangeDNSPtrRequest struct {
|
||||
IP string `json:"ip"`
|
||||
DNSPtr *string `json:"dns_ptr"`
|
||||
}
|
||||
|
||||
// FloatingIPActionChangeDNSPtrResponse defines the schema of the response when
|
||||
// creating a change_dns_ptr Floating IP action.
|
||||
type FloatingIPActionChangeDNSPtrResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// FloatingIPActionChangeProtectionRequest defines the schema of the request to change the resource protection of a Floating IP.
|
||||
type FloatingIPActionChangeProtectionRequest struct {
|
||||
Delete *bool `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
// FloatingIPActionChangeProtectionResponse defines the schema of the response when changing the resource protection of a Floating IP.
|
||||
type FloatingIPActionChangeProtectionResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
68
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/image.go
generated
vendored
Normal file
68
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/image.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
// Image defines the schema of an image.
|
||||
type Image struct {
|
||||
ID int `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
Name *string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
ImageSize *float32 `json:"image_size"`
|
||||
DiskSize float32 `json:"disk_size"`
|
||||
Created time.Time `json:"created"`
|
||||
CreatedFrom *ImageCreatedFrom `json:"created_from"`
|
||||
BoundTo *int `json:"bound_to"`
|
||||
OSFlavor string `json:"os_flavor"`
|
||||
OSVersion *string `json:"os_version"`
|
||||
RapidDeploy bool `json:"rapid_deploy"`
|
||||
Protection ImageProtection `json:"protection"`
|
||||
Deprecated time.Time `json:"deprecated"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// ImageProtection represents the protection level of a image.
|
||||
type ImageProtection struct {
|
||||
Delete bool `json:"delete"`
|
||||
}
|
||||
|
||||
// ImageCreatedFrom defines the schema of the images created from reference.
|
||||
type ImageCreatedFrom struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// ImageGetResponse defines the schema of the response when
|
||||
// retrieving a single image.
|
||||
type ImageGetResponse struct {
|
||||
Image Image `json:"image"`
|
||||
}
|
||||
|
||||
// ImageListResponse defines the schema of the response when
|
||||
// listing images.
|
||||
type ImageListResponse struct {
|
||||
Images []Image `json:"images"`
|
||||
}
|
||||
|
||||
// ImageUpdateRequest defines the schema of the request to update an image.
|
||||
type ImageUpdateRequest struct {
|
||||
Description *string `json:"description,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// ImageUpdateResponse defines the schema of the response when updating an image.
|
||||
type ImageUpdateResponse struct {
|
||||
Image Image `json:"image"`
|
||||
}
|
||||
|
||||
// ImageActionChangeProtectionRequest defines the schema of the request to change the resource protection of an image.
|
||||
type ImageActionChangeProtectionRequest struct {
|
||||
Delete *bool `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
// ImageActionChangeProtectionResponse defines the schema of the response when changing the resource protection of an image.
|
||||
type ImageActionChangeProtectionResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
22
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/iso.go
generated
vendored
Normal file
22
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/iso.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
// ISO defines the schema of an ISO image.
|
||||
type ISO struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"`
|
||||
Deprecated time.Time `json:"deprecated"`
|
||||
}
|
||||
|
||||
// ISOGetResponse defines the schema of the response when retrieving a single ISO.
|
||||
type ISOGetResponse struct {
|
||||
ISO ISO `json:"iso"`
|
||||
}
|
||||
|
||||
// ISOListResponse defines the schema of the response when listing ISOs.
|
||||
type ISOListResponse struct {
|
||||
ISOs []ISO `json:"isos"`
|
||||
}
|
386
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/load_balancer.go
generated
vendored
Normal file
386
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/load_balancer.go
generated
vendored
Normal file
|
@ -0,0 +1,386 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
type LoadBalancer struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
PublicNet LoadBalancerPublicNet `json:"public_net"`
|
||||
PrivateNet []LoadBalancerPrivateNet `json:"private_net"`
|
||||
Location Location `json:"location"`
|
||||
LoadBalancerType LoadBalancerType `json:"load_balancer_type"`
|
||||
Protection LoadBalancerProtection `json:"protection"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Created time.Time `json:"created"`
|
||||
Services []LoadBalancerService `json:"services"`
|
||||
Targets []LoadBalancerTarget `json:"targets"`
|
||||
Algorithm LoadBalancerAlgorithm `json:"algorithm"`
|
||||
IncludedTraffic uint64 `json:"included_traffic"`
|
||||
OutgoingTraffic *uint64 `json:"outgoing_traffic"`
|
||||
IngoingTraffic *uint64 `json:"ingoing_traffic"`
|
||||
}
|
||||
|
||||
type LoadBalancerPublicNet struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
IPv4 LoadBalancerPublicNetIPv4 `json:"ipv4"`
|
||||
IPv6 LoadBalancerPublicNetIPv6 `json:"ipv6"`
|
||||
}
|
||||
|
||||
type LoadBalancerPublicNetIPv4 struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type LoadBalancerPublicNetIPv6 struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type LoadBalancerPrivateNet struct {
|
||||
Network int `json:"network"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type LoadBalancerAlgorithm struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type LoadBalancerProtection struct {
|
||||
Delete bool `json:"delete"`
|
||||
}
|
||||
|
||||
type LoadBalancerService struct {
|
||||
Protocol string `json:"protocol"`
|
||||
ListenPort int `json:"listen_port"`
|
||||
DestinationPort int `json:"destination_port"`
|
||||
Proxyprotocol bool `json:"proxyprotocol"`
|
||||
HTTP *LoadBalancerServiceHTTP `json:"http"`
|
||||
HealthCheck *LoadBalancerServiceHealthCheck `json:"health_check"`
|
||||
}
|
||||
|
||||
type LoadBalancerServiceHTTP struct {
|
||||
CookieName string `json:"cookie_name"`
|
||||
CookieLifetime int `json:"cookie_lifetime"`
|
||||
Certificates []int `json:"certificates"`
|
||||
RedirectHTTP bool `json:"redirect_http"`
|
||||
StickySessions bool `json:"sticky_sessions"`
|
||||
}
|
||||
|
||||
type LoadBalancerServiceHealthCheck struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Port int `json:"port"`
|
||||
Interval int `json:"interval"`
|
||||
Timeout int `json:"timeout"`
|
||||
Retries int `json:"retries"`
|
||||
HTTP *LoadBalancerServiceHealthCheckHTTP `json:"http"`
|
||||
}
|
||||
|
||||
type LoadBalancerServiceHealthCheckHTTP struct {
|
||||
Domain string `json:"domain"`
|
||||
Path string `json:"path"`
|
||||
Response string `json:"response"`
|
||||
StatusCodes []string `json:"status_codes"`
|
||||
TLS bool `json:"tls"`
|
||||
}
|
||||
|
||||
type LoadBalancerTarget struct {
|
||||
Type string `json:"type"`
|
||||
Server *LoadBalancerTargetServer `json:"server"`
|
||||
LabelSelector *LoadBalancerTargetLabelSelector `json:"label_selector"`
|
||||
IP *LoadBalancerTargetIP `json:"ip"`
|
||||
HealthStatus []LoadBalancerTargetHealthStatus `json:"health_status"`
|
||||
UsePrivateIP bool `json:"use_private_ip"`
|
||||
Targets []LoadBalancerTarget `json:"targets,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerTargetHealthStatus struct {
|
||||
ListenPort int `json:"listen_port"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type LoadBalancerTargetServer struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type LoadBalancerTargetLabelSelector struct {
|
||||
Selector string `json:"selector"`
|
||||
}
|
||||
|
||||
type LoadBalancerTargetIP struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type LoadBalancerListResponse struct {
|
||||
LoadBalancers []LoadBalancer `json:"load_balancers"`
|
||||
}
|
||||
|
||||
type LoadBalancerGetResponse struct {
|
||||
LoadBalancer LoadBalancer `json:"load_balancer"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddTargetRequest struct {
|
||||
Type string `json:"type"`
|
||||
Server *LoadBalancerActionAddTargetRequestServer `json:"server,omitempty"`
|
||||
LabelSelector *LoadBalancerActionAddTargetRequestLabelSelector `json:"label_selector,omitempty"`
|
||||
IP *LoadBalancerActionAddTargetRequestIP `json:"ip,omitempty"`
|
||||
UsePrivateIP *bool `json:"use_private_ip,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddTargetRequestServer struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddTargetRequestLabelSelector struct {
|
||||
Selector string `json:"selector"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddTargetRequestIP struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddTargetResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionRemoveTargetRequest struct {
|
||||
Type string `json:"type"`
|
||||
Server *LoadBalancerActionRemoveTargetRequestServer `json:"server,omitempty"`
|
||||
LabelSelector *LoadBalancerActionRemoveTargetRequestLabelSelector `json:"label_selector,omitempty"`
|
||||
IP *LoadBalancerActionRemoveTargetRequestIP `json:"ip,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionRemoveTargetRequestServer struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionRemoveTargetRequestLabelSelector struct {
|
||||
Selector string `json:"selector"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionRemoveTargetRequestIP struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionRemoveTargetResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddServiceRequest struct {
|
||||
Protocol string `json:"protocol"`
|
||||
ListenPort *int `json:"listen_port,omitempty"`
|
||||
DestinationPort *int `json:"destination_port,omitempty"`
|
||||
Proxyprotocol *bool `json:"proxyprotocol,omitempty"`
|
||||
HTTP *LoadBalancerActionAddServiceRequestHTTP `json:"http,omitempty"`
|
||||
HealthCheck *LoadBalancerActionAddServiceRequestHealthCheck `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddServiceRequestHTTP struct {
|
||||
CookieName *string `json:"cookie_name,omitempty"`
|
||||
CookieLifetime *int `json:"cookie_lifetime,omitempty"`
|
||||
Certificates *[]int `json:"certificates,omitempty"`
|
||||
RedirectHTTP *bool `json:"redirect_http,omitempty"`
|
||||
StickySessions *bool `json:"sticky_sessions,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddServiceRequestHealthCheck struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Port *int `json:"port,omitempty"`
|
||||
Interval *int `json:"interval,omitempty"`
|
||||
Timeout *int `json:"timeout,omitempty"`
|
||||
Retries *int `json:"retries,omitempty"`
|
||||
HTTP *LoadBalancerActionAddServiceRequestHealthCheckHTTP `json:"http,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddServiceRequestHealthCheckHTTP struct {
|
||||
Domain *string `json:"domain,omitempty"`
|
||||
Path *string `json:"path,omitempty"`
|
||||
Response *string `json:"response,omitempty"`
|
||||
StatusCodes *[]string `json:"status_codes,omitempty"`
|
||||
TLS *bool `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAddServiceResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionUpdateServiceRequest struct {
|
||||
ListenPort int `json:"listen_port"`
|
||||
Protocol *string `json:"protocol,omitempty"`
|
||||
DestinationPort *int `json:"destination_port,omitempty"`
|
||||
Proxyprotocol *bool `json:"proxyprotocol,omitempty"`
|
||||
HTTP *LoadBalancerActionUpdateServiceRequestHTTP `json:"http,omitempty"`
|
||||
HealthCheck *LoadBalancerActionUpdateServiceRequestHealthCheck `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionUpdateServiceRequestHTTP struct {
|
||||
CookieName *string `json:"cookie_name,omitempty"`
|
||||
CookieLifetime *int `json:"cookie_lifetime,omitempty"`
|
||||
Certificates *[]int `json:"certificates,omitempty"`
|
||||
RedirectHTTP *bool `json:"redirect_http,omitempty"`
|
||||
StickySessions *bool `json:"sticky_sessions,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionUpdateServiceRequestHealthCheck struct {
|
||||
Protocol *string `json:"protocol,omitempty"`
|
||||
Port *int `json:"port,omitempty"`
|
||||
Interval *int `json:"interval,omitempty"`
|
||||
Timeout *int `json:"timeout,omitempty"`
|
||||
Retries *int `json:"retries,omitempty"`
|
||||
HTTP *LoadBalancerActionUpdateServiceRequestHealthCheckHTTP `json:"http,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionUpdateServiceRequestHealthCheckHTTP struct {
|
||||
Domain *string `json:"domain,omitempty"`
|
||||
Path *string `json:"path,omitempty"`
|
||||
Response *string `json:"response,omitempty"`
|
||||
StatusCodes *[]string `json:"status_codes,omitempty"`
|
||||
TLS *bool `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionUpdateServiceResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerDeleteServiceRequest struct {
|
||||
ListenPort int `json:"listen_port"`
|
||||
}
|
||||
|
||||
type LoadBalancerDeleteServiceResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
LoadBalancerType interface{} `json:"load_balancer_type"` // int or string
|
||||
Algorithm *LoadBalancerCreateRequestAlgorithm `json:"algorithm,omitempty"`
|
||||
Location *string `json:"location,omitempty"`
|
||||
NetworkZone *string `json:"network_zone,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
Targets []LoadBalancerCreateRequestTarget `json:"targets,omitempty"`
|
||||
Services []LoadBalancerCreateRequestService `json:"services,omitempty"`
|
||||
PublicInterface *bool `json:"public_interface,omitempty"`
|
||||
Network *int `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestAlgorithm struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestTarget struct {
|
||||
Type string `json:"type"`
|
||||
Server *LoadBalancerCreateRequestTargetServer `json:"server,omitempty"`
|
||||
LabelSelector *LoadBalancerCreateRequestTargetLabelSelector `json:"label_selector,omitempty"`
|
||||
IP *LoadBalancerCreateRequestTargetIP `json:"ip,omitempty"`
|
||||
UsePrivateIP *bool `json:"use_private_ip,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestTargetServer struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestTargetLabelSelector struct {
|
||||
Selector string `json:"selector"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestTargetIP struct {
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestService struct {
|
||||
Protocol string `json:"protocol"`
|
||||
ListenPort *int `json:"listen_port,omitempty"`
|
||||
DestinationPort *int `json:"destination_port,omitempty"`
|
||||
Proxyprotocol *bool `json:"proxyprotocol,omitempty"`
|
||||
HTTP *LoadBalancerCreateRequestServiceHTTP `json:"http,omitempty"`
|
||||
HealthCheck *LoadBalancerCreateRequestServiceHealthCheck `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestServiceHTTP struct {
|
||||
CookieName *string `json:"cookie_name,omitempty"`
|
||||
CookieLifetime *int `json:"cookie_lifetime,omitempty"`
|
||||
Certificates *[]int `json:"certificates,omitempty"`
|
||||
RedirectHTTP *bool `json:"redirect_http,omitempty"`
|
||||
StickySessions *bool `json:"sticky_sessions,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestServiceHealthCheck struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Port *int `json:"port,omitempty"`
|
||||
Interval *int `json:"interval,omitempty"`
|
||||
Timeout *int `json:"timeout,omitempty"`
|
||||
Retries *int `json:"retries,omitempty"`
|
||||
HTTP *LoadBalancerCreateRequestServiceHealthCheckHTTP `json:"http,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateRequestServiceHealthCheckHTTP struct {
|
||||
Domain *string `json:"domain,omitempty"`
|
||||
Path *string `json:"path,omitempty"`
|
||||
Response *string `json:"response,omitempty"`
|
||||
StatusCodes *[]string `json:"status_codes,omitempty"`
|
||||
TLS *bool `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerCreateResponse struct {
|
||||
LoadBalancer LoadBalancer `json:"load_balancer"`
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionChangeProtectionRequest struct {
|
||||
Delete *bool `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionChangeProtectionResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerUpdateRequest struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerUpdateResponse struct {
|
||||
LoadBalancer LoadBalancer `json:"load_balancer"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionChangeAlgorithmRequest struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionChangeAlgorithmResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAttachToNetworkRequest struct {
|
||||
Network int `json:"network"`
|
||||
IP *string `json:"ip,omitempty"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionAttachToNetworkResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionDetachFromNetworkRequest struct {
|
||||
Network int `json:"network"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionDetachFromNetworkResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionEnablePublicInterfaceRequest struct{}
|
||||
|
||||
type LoadBalancerActionEnablePublicInterfaceResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionDisablePublicInterfaceRequest struct{}
|
||||
|
||||
type LoadBalancerActionDisablePublicInterfaceResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type LoadBalancerActionChangeTypeRequest struct {
|
||||
LoadBalancerType interface{} `json:"load_balancer_type"` // int or string
|
||||
}
|
||||
|
||||
type LoadBalancerActionChangeTypeResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
25
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/load_balancer_type.go
generated
vendored
Normal file
25
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/load_balancer_type.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package schema
|
||||
|
||||
// LoadBalancerType defines the schema of a LoadBalancer type.
|
||||
type LoadBalancerType struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
MaxConnections int `json:"max_connections"`
|
||||
MaxServices int `json:"max_services"`
|
||||
MaxTargets int `json:"max_targets"`
|
||||
MaxAssignedCertificates int `json:"max_assigned_certificates"`
|
||||
Prices []PricingLoadBalancerTypePrice `json:"prices"`
|
||||
}
|
||||
|
||||
// LoadBalancerTypeListResponse defines the schema of the response when
|
||||
// listing LoadBalancer types.
|
||||
type LoadBalancerTypeListResponse struct {
|
||||
LoadBalancerTypes []LoadBalancerType `json:"load_balancer_types"`
|
||||
}
|
||||
|
||||
// LoadBalancerTypeGetResponse defines the schema of the response when
|
||||
// retrieving a single LoadBalancer type.
|
||||
type LoadBalancerTypeGetResponse struct {
|
||||
LoadBalancerType LoadBalancerType `json:"load_balancer_type"`
|
||||
}
|
23
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/location.go
generated
vendored
Normal file
23
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/location.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package schema
|
||||
|
||||
// Location defines the schema of a location.
|
||||
type Location struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Country string `json:"country"`
|
||||
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.
|
||||
type LocationGetResponse struct {
|
||||
Location Location `json:"location"`
|
||||
}
|
||||
|
||||
// LocationListResponse defines the schema of the response when listing locations.
|
||||
type LocationListResponse struct {
|
||||
Locations []Location `json:"locations"`
|
||||
}
|
23
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/meta.go
generated
vendored
Normal file
23
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/meta.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package schema
|
||||
|
||||
// Meta defines the schema of meta information which may be included
|
||||
// in responses.
|
||||
type Meta struct {
|
||||
Pagination *MetaPagination `json:"pagination"`
|
||||
}
|
||||
|
||||
// MetaPagination defines the schema of pagination information.
|
||||
type MetaPagination struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"per_page"`
|
||||
PreviousPage int `json:"previous_page"`
|
||||
NextPage int `json:"next_page"`
|
||||
LastPage int `json:"last_page"`
|
||||
TotalEntries int `json:"total_entries"`
|
||||
}
|
||||
|
||||
// MetaResponse defines the schema of a response containing
|
||||
// meta information.
|
||||
type MetaResponse struct {
|
||||
Meta Meta `json:"meta"`
|
||||
}
|
150
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/network.go
generated
vendored
Normal file
150
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/network.go
generated
vendored
Normal 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"`
|
||||
}
|
74
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/pricing.go
generated
vendored
Normal file
74
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/pricing.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
package schema
|
||||
|
||||
// Pricing defines the schema for pricing information.
|
||||
type Pricing struct {
|
||||
Currency string `json:"currency"`
|
||||
VATRate string `json:"vat_rate"`
|
||||
Image PricingImage `json:"image"`
|
||||
FloatingIP PricingFloatingIP `json:"floating_ip"`
|
||||
Traffic PricingTraffic `json:"traffic"`
|
||||
ServerBackup PricingServerBackup `json:"server_backup"`
|
||||
ServerTypes []PricingServerType `json:"server_types"`
|
||||
LoadBalancerTypes []PricingLoadBalancerType `json:"load_balancer_types"`
|
||||
}
|
||||
|
||||
// Price defines the schema of a single price with net and gross amount.
|
||||
type Price struct {
|
||||
Net string `json:"net"`
|
||||
Gross string `json:"gross"`
|
||||
}
|
||||
|
||||
// PricingImage defines the schema of pricing information for an image.
|
||||
type PricingImage struct {
|
||||
PricePerGBMonth Price `json:"price_per_gb_month"`
|
||||
}
|
||||
|
||||
// PricingFloatingIP defines the schema of pricing information for a Floating IP.
|
||||
type PricingFloatingIP struct {
|
||||
PriceMonthly Price `json:"price_monthly"`
|
||||
}
|
||||
|
||||
// PricingTraffic defines the schema of pricing information for traffic.
|
||||
type PricingTraffic struct {
|
||||
PricePerTB Price `json:"price_per_tb"`
|
||||
}
|
||||
|
||||
// PricingServerBackup defines the schema of pricing information for server backups.
|
||||
type PricingServerBackup struct {
|
||||
Percentage string `json:"percentage"`
|
||||
}
|
||||
|
||||
// PricingServerType defines the schema of pricing information for a server type.
|
||||
type PricingServerType struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Prices []PricingServerTypePrice `json:"prices"`
|
||||
}
|
||||
|
||||
// PricingServerTypePrice defines the schema of pricing information for a server
|
||||
// type at a location.
|
||||
type PricingServerTypePrice struct {
|
||||
Location string `json:"location"`
|
||||
PriceHourly Price `json:"price_hourly"`
|
||||
PriceMonthly Price `json:"price_monthly"`
|
||||
}
|
||||
|
||||
// PricingLoadBalancerType defines the schema of pricing information for a Load Balancer type.
|
||||
type PricingLoadBalancerType struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Prices []PricingLoadBalancerTypePrice `json:"prices"`
|
||||
}
|
||||
|
||||
// PricingLoadBalancerTypePrice defines the schema of pricing information for a Load Balancer
|
||||
// type at a location.
|
||||
type PricingLoadBalancerTypePrice struct {
|
||||
Location string `json:"location"`
|
||||
PriceHourly Price `json:"price_hourly"`
|
||||
PriceMonthly Price `json:"price_monthly"`
|
||||
}
|
||||
|
||||
// PricingGetResponse defines the schema of the response when retrieving pricing information.
|
||||
type PricingGetResponse struct {
|
||||
Pricing Pricing `json:"pricing"`
|
||||
}
|
366
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server.go
generated
vendored
Normal file
366
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server.go
generated
vendored
Normal file
|
@ -0,0 +1,366 @@
|
|||
package schema
|
||||
|
||||
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"`
|
||||
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.
|
||||
type ServerProtection struct {
|
||||
Delete bool `json:"delete"`
|
||||
Rebuild bool `json:"rebuild"`
|
||||
}
|
||||
|
||||
// ServerPublicNet defines the schema of a server's
|
||||
// public network information.
|
||||
type ServerPublicNet struct {
|
||||
IPv4 ServerPublicNetIPv4 `json:"ipv4"`
|
||||
IPv6 ServerPublicNetIPv6 `json:"ipv6"`
|
||||
FloatingIPs []int `json:"floating_ips"`
|
||||
}
|
||||
|
||||
// ServerPublicNetIPv4 defines the schema of a server's public
|
||||
// network information for an IPv4.
|
||||
type ServerPublicNetIPv4 struct {
|
||||
IP string `json:"ip"`
|
||||
Blocked bool `json:"blocked"`
|
||||
DNSPtr string `json:"dns_ptr"`
|
||||
}
|
||||
|
||||
// ServerPublicNetIPv6 defines the schema of a server's public
|
||||
// network information for an IPv6.
|
||||
type ServerPublicNetIPv6 struct {
|
||||
IP string `json:"ip"`
|
||||
Blocked bool `json:"blocked"`
|
||||
DNSPtr []ServerPublicNetIPv6DNSPtr `json:"dns_ptr"`
|
||||
}
|
||||
|
||||
// ServerPublicNetIPv6DNSPtr defines the schema of a server's
|
||||
// public network information for an IPv6 reverse DNS.
|
||||
type ServerPublicNetIPv6DNSPtr struct {
|
||||
IP string `json:"ip"`
|
||||
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 {
|
||||
Server Server `json:"server"`
|
||||
}
|
||||
|
||||
// ServerListResponse defines the schema of the response when
|
||||
// listing servers.
|
||||
type ServerListResponse struct {
|
||||
Servers []Server `json:"servers"`
|
||||
}
|
||||
|
||||
// ServerCreateRequest defines the schema for the request to
|
||||
// create a server.
|
||||
type ServerCreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
ServerType interface{} `json:"server_type"` // int or string
|
||||
Image interface{} `json:"image"` // int or string
|
||||
SSHKeys []int `json:"ssh_keys,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
Datacenter string `json:"datacenter,omitempty"`
|
||||
UserData string `json:"user_data,omitempty"`
|
||||
StartAfterCreate *bool `json:"start_after_create,omitempty"`
|
||||
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
|
||||
// creating a server.
|
||||
type ServerCreateResponse struct {
|
||||
Server Server `json:"server"`
|
||||
Action Action `json:"action"`
|
||||
RootPassword *string `json:"root_password"`
|
||||
NextActions []Action `json:"next_actions"`
|
||||
}
|
||||
|
||||
// ServerUpdateRequest defines the schema of the request to update a server.
|
||||
type ServerUpdateRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// ServerUpdateResponse defines the schema of the response when updating a server.
|
||||
type ServerUpdateResponse struct {
|
||||
Server Server `json:"server"`
|
||||
}
|
||||
|
||||
// ServerActionPoweronRequest defines the schema for the request to
|
||||
// create a poweron server action.
|
||||
type ServerActionPoweronRequest struct{}
|
||||
|
||||
// ServerActionPoweronResponse defines the schema of the response when
|
||||
// creating a poweron server action.
|
||||
type ServerActionPoweronResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionPoweroffRequest defines the schema for the request to
|
||||
// create a poweroff server action.
|
||||
type ServerActionPoweroffRequest struct{}
|
||||
|
||||
// ServerActionPoweroffResponse defines the schema of the response when
|
||||
// creating a poweroff server action.
|
||||
type ServerActionPoweroffResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionRebootRequest defines the schema for the request to
|
||||
// create a reboot server action.
|
||||
type ServerActionRebootRequest struct{}
|
||||
|
||||
// ServerActionRebootResponse defines the schema of the response when
|
||||
// creating a reboot server action.
|
||||
type ServerActionRebootResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionResetRequest defines the schema for the request to
|
||||
// create a reset server action.
|
||||
type ServerActionResetRequest struct{}
|
||||
|
||||
// ServerActionResetResponse defines the schema of the response when
|
||||
// creating a reset server action.
|
||||
type ServerActionResetResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionShutdownRequest defines the schema for the request to
|
||||
// create a shutdown server action.
|
||||
type ServerActionShutdownRequest struct{}
|
||||
|
||||
// ServerActionShutdownResponse defines the schema of the response when
|
||||
// creating a shutdown server action.
|
||||
type ServerActionShutdownResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionResetPasswordRequest defines the schema for the request to
|
||||
// create a reset_password server action.
|
||||
type ServerActionResetPasswordRequest struct{}
|
||||
|
||||
// ServerActionResetPasswordResponse defines the schema of the response when
|
||||
// creating a reset_password server action.
|
||||
type ServerActionResetPasswordResponse struct {
|
||||
Action Action `json:"action"`
|
||||
RootPassword string `json:"root_password"`
|
||||
}
|
||||
|
||||
// ServerActionCreateImageRequest defines the schema for the request to
|
||||
// create a create_image server action.
|
||||
type ServerActionCreateImageRequest struct {
|
||||
Type *string `json:"type"`
|
||||
Description *string `json:"description"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// ServerActionCreateImageResponse defines the schema of the response when
|
||||
// creating a create_image server action.
|
||||
type ServerActionCreateImageResponse struct {
|
||||
Action Action `json:"action"`
|
||||
Image Image `json:"image"`
|
||||
}
|
||||
|
||||
// ServerActionEnableRescueRequest defines the schema for the request to
|
||||
// create a enable_rescue server action.
|
||||
type ServerActionEnableRescueRequest struct {
|
||||
Type *string `json:"type,omitempty"`
|
||||
SSHKeys []int `json:"ssh_keys,omitempty"`
|
||||
}
|
||||
|
||||
// ServerActionEnableRescueResponse defines the schema of the response when
|
||||
// creating a enable_rescue server action.
|
||||
type ServerActionEnableRescueResponse struct {
|
||||
Action Action `json:"action"`
|
||||
RootPassword string `json:"root_password"`
|
||||
}
|
||||
|
||||
// ServerActionDisableRescueRequest defines the schema for the request to
|
||||
// create a disable_rescue server action.
|
||||
type ServerActionDisableRescueRequest struct{}
|
||||
|
||||
// ServerActionDisableRescueResponse defines the schema of the response when
|
||||
// creating a disable_rescue server action.
|
||||
type ServerActionDisableRescueResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionRebuildRequest defines the schema for the request to
|
||||
// rebuild a server.
|
||||
type ServerActionRebuildRequest struct {
|
||||
Image interface{} `json:"image"` // int or string
|
||||
}
|
||||
|
||||
// ServerActionRebuildResponse defines the schema of the response when
|
||||
// creating a rebuild server action.
|
||||
type ServerActionRebuildResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionAttachISORequest defines the schema for the request to
|
||||
// attach an ISO to a server.
|
||||
type ServerActionAttachISORequest struct {
|
||||
ISO interface{} `json:"iso"` // int or string
|
||||
}
|
||||
|
||||
// ServerActionAttachISOResponse defines the schema of the response when
|
||||
// creating a attach_iso server action.
|
||||
type ServerActionAttachISOResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionDetachISORequest defines the schema for the request to
|
||||
// detach an ISO from a server.
|
||||
type ServerActionDetachISORequest struct{}
|
||||
|
||||
// ServerActionDetachISOResponse defines the schema of the response when
|
||||
// creating a detach_iso server action.
|
||||
type ServerActionDetachISOResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionEnableBackupRequest defines the schema for the request to
|
||||
// enable backup for a server.
|
||||
type ServerActionEnableBackupRequest struct {
|
||||
BackupWindow *string `json:"backup_window,omitempty"`
|
||||
}
|
||||
|
||||
// ServerActionEnableBackupResponse defines the schema of the response when
|
||||
// creating a enable_backup server action.
|
||||
type ServerActionEnableBackupResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionDisableBackupRequest defines the schema for the request to
|
||||
// disable backup for a server.
|
||||
type ServerActionDisableBackupRequest struct{}
|
||||
|
||||
// ServerActionDisableBackupResponse defines the schema of the response when
|
||||
// creating a disable_backup server action.
|
||||
type ServerActionDisableBackupResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionChangeTypeRequest defines the schema for the request to
|
||||
// change a server's type.
|
||||
type ServerActionChangeTypeRequest struct {
|
||||
ServerType interface{} `json:"server_type"` // int or string
|
||||
UpgradeDisk bool `json:"upgrade_disk"`
|
||||
}
|
||||
|
||||
// ServerActionChangeTypeResponse defines the schema of the response when
|
||||
// creating a change_type server action.
|
||||
type ServerActionChangeTypeResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionChangeDNSPtrRequest defines the schema for the request to
|
||||
// change a server's reverse DNS pointer.
|
||||
type ServerActionChangeDNSPtrRequest struct {
|
||||
IP string `json:"ip"`
|
||||
DNSPtr *string `json:"dns_ptr"`
|
||||
}
|
||||
|
||||
// ServerActionChangeDNSPtrResponse defines the schema of the response when
|
||||
// creating a change_dns_ptr server action.
|
||||
type ServerActionChangeDNSPtrResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
type ServerActionChangeProtectionResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// ServerActionRequestConsoleRequest defines the schema of the request to
|
||||
// request a WebSocket VNC console.
|
||||
type ServerActionRequestConsoleRequest struct{}
|
||||
|
||||
// ServerActionRequestConsoleResponse defines the schema of the response when
|
||||
// requesting a WebSocket VNC console.
|
||||
type ServerActionRequestConsoleResponse struct {
|
||||
Action Action `json:"action"`
|
||||
WSSURL string `json:"wss_url"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
26
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server_type.go
generated
vendored
Normal file
26
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/server_type.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package schema
|
||||
|
||||
// ServerType defines the schema of a server type.
|
||||
type ServerType struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Cores int `json:"cores"`
|
||||
Memory float32 `json:"memory"`
|
||||
Disk int `json:"disk"`
|
||||
StorageType string `json:"storage_type"`
|
||||
CPUType string `json:"cpu_type"`
|
||||
Prices []PricingServerTypePrice `json:"prices"`
|
||||
}
|
||||
|
||||
// ServerTypeListResponse defines the schema of the response when
|
||||
// listing server types.
|
||||
type ServerTypeListResponse struct {
|
||||
ServerTypes []ServerType `json:"server_types"`
|
||||
}
|
||||
|
||||
// ServerTypeGetResponse defines the schema of the response when
|
||||
// retrieving a single server type.
|
||||
type ServerTypeGetResponse struct {
|
||||
ServerType ServerType `json:"server_type"`
|
||||
}
|
50
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/ssh_key.go
generated
vendored
Normal file
50
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/ssh_key.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
// SSHKey defines the schema of a SSH key.
|
||||
type SSHKey struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
PublicKey string `json:"public_key"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// SSHKeyCreateRequest defines the schema of the request
|
||||
// to create a SSH key.
|
||||
type SSHKeyCreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
PublicKey string `json:"public_key"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// SSHKeyCreateResponse defines the schema of the response
|
||||
// when creating a SSH key.
|
||||
type SSHKeyCreateResponse struct {
|
||||
SSHKey SSHKey `json:"ssh_key"`
|
||||
}
|
||||
|
||||
// SSHKeyListResponse defines the schema of the response
|
||||
// when listing SSH keys.
|
||||
type SSHKeyListResponse struct {
|
||||
SSHKeys []SSHKey `json:"ssh_keys"`
|
||||
}
|
||||
|
||||
// SSHKeyGetResponse defines the schema of the response
|
||||
// when retrieving a single SSH key.
|
||||
type SSHKeyGetResponse struct {
|
||||
SSHKey SSHKey `json:"ssh_key"`
|
||||
}
|
||||
|
||||
// SSHKeyUpdateRequest defines the schema of the request to update a SSH key.
|
||||
type SSHKeyUpdateRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// SSHKeyUpdateResponse defines the schema of the response when updating a SSH key.
|
||||
type SSHKeyUpdateResponse struct {
|
||||
SSHKey SSHKey `json:"ssh_key"`
|
||||
}
|
110
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/volume.go
generated
vendored
Normal file
110
vendor/github.com/hetznercloud/hcloud-go/hcloud/schema/volume.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
package schema
|
||||
|
||||
import "time"
|
||||
|
||||
// Volume defines the schema of a volume.
|
||||
type Volume struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Server *int `json:"server"`
|
||||
Status string `json:"status"`
|
||||
Location Location `json:"location"`
|
||||
Size int `json:"size"`
|
||||
Protection VolumeProtection `json:"protection"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
LinuxDevice string `json:"linux_device"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
// VolumeCreateRequest defines the schema of the request
|
||||
// to create a volume.
|
||||
type VolumeCreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
Size int `json:"size"`
|
||||
Server *int `json:"server,omitempty"`
|
||||
Location interface{} `json:"location,omitempty"` // int, string, or nil
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
Automount *bool `json:"automount,omitempty"`
|
||||
Format *string `json:"format,omitempty"`
|
||||
}
|
||||
|
||||
// VolumeCreateResponse defines the schema of the response
|
||||
// when creating a volume.
|
||||
type VolumeCreateResponse struct {
|
||||
Volume Volume `json:"volume"`
|
||||
Action *Action `json:"action"`
|
||||
NextActions []Action `json:"next_actions"`
|
||||
}
|
||||
|
||||
// VolumeListResponse defines the schema of the response
|
||||
// when listing volumes.
|
||||
type VolumeListResponse struct {
|
||||
Volumes []Volume `json:"volumes"`
|
||||
}
|
||||
|
||||
// VolumeGetResponse defines the schema of the response
|
||||
// when retrieving a single volume.
|
||||
type VolumeGetResponse struct {
|
||||
Volume Volume `json:"volume"`
|
||||
}
|
||||
|
||||
// VolumeUpdateRequest defines the schema of the request to update a volume.
|
||||
type VolumeUpdateRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Labels *map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// VolumeUpdateResponse defines the schema of the response when updating a volume.
|
||||
type VolumeUpdateResponse struct {
|
||||
Volume Volume `json:"volume"`
|
||||
}
|
||||
|
||||
// VolumeProtection defines the schema of a volume's resource protection.
|
||||
type VolumeProtection struct {
|
||||
Delete bool `json:"delete"`
|
||||
}
|
||||
|
||||
// VolumeActionChangeProtectionRequest defines the schema of the request to
|
||||
// change the resource protection of a volume.
|
||||
type VolumeActionChangeProtectionRequest struct {
|
||||
Delete *bool `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
// VolumeActionChangeProtectionResponse defines the schema of the response when
|
||||
// changing the resource protection of a volume.
|
||||
type VolumeActionChangeProtectionResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// VolumeActionAttachVolumeRequest defines the schema of the request to
|
||||
// attach a volume to a server.
|
||||
type VolumeActionAttachVolumeRequest struct {
|
||||
Server int `json:"server"`
|
||||
Automount *bool `json:"automount,omitempty"`
|
||||
}
|
||||
|
||||
// VolumeActionAttachVolumeResponse defines the schema of the response when
|
||||
// attaching a volume to a server.
|
||||
type VolumeActionAttachVolumeResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// VolumeActionDetachVolumeRequest defines the schema of the request to
|
||||
// create an detach volume action.
|
||||
type VolumeActionDetachVolumeRequest struct{}
|
||||
|
||||
// VolumeActionDetachVolumeResponse defines the schema of the response when
|
||||
// creating an detach volume action.
|
||||
type VolumeActionDetachVolumeResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
// VolumeActionResizeVolumeRequest defines the schema of the request to resize a volume.
|
||||
type VolumeActionResizeVolumeRequest struct {
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
// VolumeActionResizeVolumeResponse defines the schema of the response when resizing a volume.
|
||||
type VolumeActionResizeVolumeResponse struct {
|
||||
Action Action `json:"action"`
|
||||
}
|
953
vendor/github.com/hetznercloud/hcloud-go/hcloud/server.go
generated
vendored
Normal file
953
vendor/github.com/hetznercloud/hcloud-go/hcloud/server.go
generated
vendored
Normal file
|
@ -0,0 +1,953 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Server represents a server in the Hetzner Cloud.
|
||||
type Server struct {
|
||||
ID int
|
||||
Name string
|
||||
Status ServerStatus
|
||||
Created time.Time
|
||||
PublicNet ServerPublicNet
|
||||
PrivateNet []ServerPrivateNet
|
||||
ServerType *ServerType
|
||||
Datacenter *Datacenter
|
||||
IncludedTraffic uint64
|
||||
OutgoingTraffic uint64
|
||||
IngoingTraffic uint64
|
||||
BackupWindow string
|
||||
RescueEnabled bool
|
||||
Locked bool
|
||||
ISO *ISO
|
||||
Image *Image
|
||||
Protection ServerProtection
|
||||
Labels map[string]string
|
||||
Volumes []*Volume
|
||||
}
|
||||
|
||||
// ServerProtection represents the protection level of a server.
|
||||
type ServerProtection struct {
|
||||
Delete, Rebuild bool
|
||||
}
|
||||
|
||||
// ServerStatus specifies a server's status.
|
||||
type ServerStatus string
|
||||
|
||||
const (
|
||||
// ServerStatusInitializing is the status when a server is initializing.
|
||||
ServerStatusInitializing ServerStatus = "initializing"
|
||||
|
||||
// ServerStatusOff is the status when a server is off.
|
||||
ServerStatusOff ServerStatus = "off"
|
||||
|
||||
// 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.
|
||||
type ServerPublicNet struct {
|
||||
IPv4 ServerPublicNetIPv4
|
||||
IPv6 ServerPublicNetIPv6
|
||||
FloatingIPs []*FloatingIP
|
||||
}
|
||||
|
||||
// ServerPublicNetIPv4 represents a server's public IPv4 address.
|
||||
type ServerPublicNetIPv4 struct {
|
||||
IP net.IP
|
||||
Blocked bool
|
||||
DNSPtr string
|
||||
}
|
||||
|
||||
// ServerPublicNetIPv6 represents a server's public IPv6 network and address.
|
||||
type ServerPublicNetIPv6 struct {
|
||||
IP net.IP
|
||||
Network *net.IPNet
|
||||
Blocked bool
|
||||
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()]
|
||||
}
|
||||
|
||||
// ServerRescueType represents rescue types.
|
||||
type ServerRescueType string
|
||||
|
||||
// List of rescue types.
|
||||
const (
|
||||
ServerRescueTypeLinux32 ServerRescueType = "linux32"
|
||||
ServerRescueTypeLinux64 ServerRescueType = "linux64"
|
||||
ServerRescueTypeFreeBSD64 ServerRescueType = "freebsd64"
|
||||
)
|
||||
|
||||
// ServerClient is a client for the servers API.
|
||||
type ServerClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a server by its ID. If the server does not exist, nil is returned.
|
||||
func (c *ServerClient) GetByID(ctx context.Context, id int) (*Server, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/servers/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ServerGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return ServerFromSchema(body.Server), resp, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
servers, response, err := c.List(ctx, ServerListOpts{Name: name})
|
||||
if len(servers) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return servers[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a server by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a server by its name. If the server does not exist, nil is returned.
|
||||
func (c *ServerClient) Get(ctx context.Context, idOrName string) (*Server, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *ServerClient) List(ctx context.Context, opts ServerListOpts) ([]*Server, *Response, error) {
|
||||
path := "/servers?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ServerListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
servers := make([]*Server, 0, len(body.Servers))
|
||||
for _, s := range body.Servers {
|
||||
servers = append(servers, ServerFromSchema(s))
|
||||
}
|
||||
return servers, resp, nil
|
||||
}
|
||||
|
||||
// All returns all servers.
|
||||
func (c *ServerClient) All(ctx context.Context) ([]*Server, error) {
|
||||
return c.AllWithOpts(ctx, ServerListOpts{ListOpts: ListOpts{PerPage: 50}})
|
||||
}
|
||||
|
||||
// AllWithOpts returns all servers for the given options.
|
||||
func (c *ServerClient) AllWithOpts(ctx context.Context, opts ServerListOpts) ([]*Server, error) {
|
||||
allServers := []*Server{}
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
servers, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allServers = append(allServers, servers...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allServers, nil
|
||||
}
|
||||
|
||||
// ServerCreateOpts specifies options for creating a new server.
|
||||
type ServerCreateOpts struct {
|
||||
Name string
|
||||
ServerType *ServerType
|
||||
Image *Image
|
||||
SSHKeys []*SSHKey
|
||||
Location *Location
|
||||
Datacenter *Datacenter
|
||||
UserData string
|
||||
StartAfterCreate *bool
|
||||
Labels map[string]string
|
||||
Automount *bool
|
||||
Volumes []*Volume
|
||||
Networks []*Network
|
||||
}
|
||||
|
||||
// Validate checks if options are valid.
|
||||
func (o ServerCreateOpts) Validate() error {
|
||||
if o.Name == "" {
|
||||
return errors.New("missing name")
|
||||
}
|
||||
if o.ServerType == nil || (o.ServerType.ID == 0 && o.ServerType.Name == "") {
|
||||
return errors.New("missing server type")
|
||||
}
|
||||
if o.Image == nil || (o.Image.ID == 0 && o.Image.Name == "") {
|
||||
return errors.New("missing image")
|
||||
}
|
||||
if o.Location != nil && o.Datacenter != nil {
|
||||
return errors.New("location and datacenter are mutually exclusive")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServerCreateResult is the result of a create server call.
|
||||
type ServerCreateResult struct {
|
||||
Server *Server
|
||||
Action *Action
|
||||
RootPassword string
|
||||
NextActions []*Action
|
||||
}
|
||||
|
||||
// Create creates a new server.
|
||||
func (c *ServerClient) Create(ctx context.Context, opts ServerCreateOpts) (ServerCreateResult, *Response, error) {
|
||||
if err := opts.Validate(); err != nil {
|
||||
return ServerCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
var reqBody schema.ServerCreateRequest
|
||||
reqBody.UserData = opts.UserData
|
||||
reqBody.Name = opts.Name
|
||||
reqBody.Automount = opts.Automount
|
||||
reqBody.StartAfterCreate = opts.StartAfterCreate
|
||||
if opts.ServerType.ID != 0 {
|
||||
reqBody.ServerType = opts.ServerType.ID
|
||||
} else if opts.ServerType.Name != "" {
|
||||
reqBody.ServerType = opts.ServerType.Name
|
||||
}
|
||||
if opts.Image.ID != 0 {
|
||||
reqBody.Image = opts.Image.ID
|
||||
} else if opts.Image.Name != "" {
|
||||
reqBody.Image = opts.Image.Name
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
reqBody.Labels = &opts.Labels
|
||||
}
|
||||
for _, sshKey := range opts.SSHKeys {
|
||||
reqBody.SSHKeys = append(reqBody.SSHKeys, sshKey.ID)
|
||||
}
|
||||
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 {
|
||||
reqBody.Location = strconv.Itoa(opts.Location.ID)
|
||||
} else {
|
||||
reqBody.Location = opts.Location.Name
|
||||
}
|
||||
}
|
||||
if opts.Datacenter != nil {
|
||||
if opts.Datacenter.ID != 0 {
|
||||
reqBody.Datacenter = strconv.Itoa(opts.Datacenter.ID)
|
||||
} else {
|
||||
reqBody.Datacenter = opts.Datacenter.Name
|
||||
}
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return ServerCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "POST", "/servers", bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return ServerCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.ServerCreateResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return ServerCreateResult{}, resp, err
|
||||
}
|
||||
result := ServerCreateResult{
|
||||
Server: ServerFromSchema(respBody.Server),
|
||||
Action: ActionFromSchema(respBody.Action),
|
||||
NextActions: ActionsFromSchema(respBody.NextActions),
|
||||
}
|
||||
if respBody.RootPassword != nil {
|
||||
result.RootPassword = *respBody.RootPassword
|
||||
}
|
||||
return result, resp, nil
|
||||
}
|
||||
|
||||
// Delete deletes a server.
|
||||
func (c *ServerClient) Delete(ctx context.Context, server *Server) (*Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/servers/%d", server.ID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// ServerUpdateOpts specifies options for updating a server.
|
||||
type ServerUpdateOpts struct {
|
||||
Name string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Update updates a server.
|
||||
func (c *ServerClient) Update(ctx context.Context, server *Server, opts ServerUpdateOpts) (*Server, *Response, error) {
|
||||
reqBody := schema.ServerUpdateRequest{
|
||||
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("/servers/%d", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerUpdateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ServerFromSchema(respBody.Server), resp, nil
|
||||
}
|
||||
|
||||
// Poweron starts a server.
|
||||
func (c *ServerClient) Poweron(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/poweron", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionPoweronResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// Reboot reboots a server.
|
||||
func (c *ServerClient) Reboot(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/reboot", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionRebootResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// Reset resets a server.
|
||||
func (c *ServerClient) Reset(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/reset", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionResetResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// Shutdown shuts down a server.
|
||||
func (c *ServerClient) Shutdown(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/shutdown", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionShutdownResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// Poweroff stops a server.
|
||||
func (c *ServerClient) Poweroff(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/poweroff", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionPoweroffResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// ServerResetPasswordResult is the result of resetting a server's password.
|
||||
type ServerResetPasswordResult struct {
|
||||
Action *Action
|
||||
RootPassword string
|
||||
}
|
||||
|
||||
// ResetPassword resets a server's password.
|
||||
func (c *ServerClient) ResetPassword(ctx context.Context, server *Server) (ServerResetPasswordResult, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/reset_password", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return ServerResetPasswordResult{}, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionResetPasswordResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return ServerResetPasswordResult{}, resp, err
|
||||
}
|
||||
return ServerResetPasswordResult{
|
||||
Action: ActionFromSchema(respBody.Action),
|
||||
RootPassword: respBody.RootPassword,
|
||||
}, resp, nil
|
||||
}
|
||||
|
||||
// ServerCreateImageOpts specifies options for creating an image from a server.
|
||||
type ServerCreateImageOpts struct {
|
||||
Type ImageType
|
||||
Description *string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Validate checks if options are valid.
|
||||
func (o ServerCreateImageOpts) Validate() error {
|
||||
switch o.Type {
|
||||
case ImageTypeSnapshot, ImageTypeBackup:
|
||||
break
|
||||
case "":
|
||||
break
|
||||
default:
|
||||
return errors.New("invalid type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServerCreateImageResult is the result of creating an image from a server.
|
||||
type ServerCreateImageResult struct {
|
||||
Action *Action
|
||||
Image *Image
|
||||
}
|
||||
|
||||
// CreateImage creates an image from a server.
|
||||
func (c *ServerClient) CreateImage(ctx context.Context, server *Server, opts *ServerCreateImageOpts) (ServerCreateImageResult, *Response, error) {
|
||||
var reqBody schema.ServerActionCreateImageRequest
|
||||
if opts != nil {
|
||||
if err := opts.Validate(); err != nil {
|
||||
return ServerCreateImageResult{}, nil, fmt.Errorf("invalid options: %s", err)
|
||||
}
|
||||
if opts.Description != nil {
|
||||
reqBody.Description = opts.Description
|
||||
}
|
||||
if opts.Type != "" {
|
||||
reqBody.Type = String(string(opts.Type))
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
reqBody.Labels = &opts.Labels
|
||||
}
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return ServerCreateImageResult{}, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/servers/%d/actions/create_image", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return ServerCreateImageResult{}, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionCreateImageResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return ServerCreateImageResult{}, resp, err
|
||||
}
|
||||
return ServerCreateImageResult{
|
||||
Action: ActionFromSchema(respBody.Action),
|
||||
Image: ImageFromSchema(respBody.Image),
|
||||
}, resp, nil
|
||||
}
|
||||
|
||||
// ServerEnableRescueOpts specifies options for enabling rescue mode for a server.
|
||||
type ServerEnableRescueOpts struct {
|
||||
Type ServerRescueType
|
||||
SSHKeys []*SSHKey
|
||||
}
|
||||
|
||||
// ServerEnableRescueResult is the result of enabling rescue mode for a server.
|
||||
type ServerEnableRescueResult struct {
|
||||
Action *Action
|
||||
RootPassword string
|
||||
}
|
||||
|
||||
// EnableRescue enables rescue mode for a server.
|
||||
func (c *ServerClient) EnableRescue(ctx context.Context, server *Server, opts ServerEnableRescueOpts) (ServerEnableRescueResult, *Response, error) {
|
||||
reqBody := schema.ServerActionEnableRescueRequest{
|
||||
Type: String(string(opts.Type)),
|
||||
}
|
||||
for _, sshKey := range opts.SSHKeys {
|
||||
reqBody.SSHKeys = append(reqBody.SSHKeys, sshKey.ID)
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return ServerEnableRescueResult{}, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/servers/%d/actions/enable_rescue", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return ServerEnableRescueResult{}, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionEnableRescueResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return ServerEnableRescueResult{}, resp, err
|
||||
}
|
||||
result := ServerEnableRescueResult{
|
||||
Action: ActionFromSchema(respBody.Action),
|
||||
RootPassword: respBody.RootPassword,
|
||||
}
|
||||
return result, resp, nil
|
||||
}
|
||||
|
||||
// DisableRescue disables rescue mode for a server.
|
||||
func (c *ServerClient) DisableRescue(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/disable_rescue", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionDisableRescueResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// ServerRebuildOpts specifies options for rebuilding a server.
|
||||
type ServerRebuildOpts struct {
|
||||
Image *Image
|
||||
}
|
||||
|
||||
// Rebuild rebuilds a server.
|
||||
func (c *ServerClient) Rebuild(ctx context.Context, server *Server, opts ServerRebuildOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.ServerActionRebuildRequest{}
|
||||
if opts.Image.ID != 0 {
|
||||
reqBody.Image = opts.Image.ID
|
||||
} else {
|
||||
reqBody.Image = opts.Image.Name
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/servers/%d/actions/rebuild", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionRebuildResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// AttachISO attaches an ISO to a server.
|
||||
func (c *ServerClient) AttachISO(ctx context.Context, server *Server, iso *ISO) (*Action, *Response, error) {
|
||||
reqBody := schema.ServerActionAttachISORequest{}
|
||||
if iso.ID != 0 {
|
||||
reqBody.ISO = iso.ID
|
||||
} else {
|
||||
reqBody.ISO = iso.Name
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/servers/%d/actions/attach_iso", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionAttachISOResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// DetachISO detaches the currently attached ISO from a server.
|
||||
func (c *ServerClient) DetachISO(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/detach_iso", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionDetachISOResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// EnableBackup enables backup for a server. Pass in an empty backup window to let the
|
||||
// API pick a window for you. See the API documentation at docs.hetzner.cloud for a list
|
||||
// of valid backup windows.
|
||||
func (c *ServerClient) EnableBackup(ctx context.Context, server *Server, window string) (*Action, *Response, error) {
|
||||
reqBody := schema.ServerActionEnableBackupRequest{}
|
||||
if window != "" {
|
||||
reqBody.BackupWindow = String(window)
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/servers/%d/actions/enable_backup", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionEnableBackupResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// DisableBackup disables backup for a server.
|
||||
func (c *ServerClient) DisableBackup(ctx context.Context, server *Server) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/disable_backup", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionDisableBackupResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// ServerChangeTypeOpts specifies options for changing a server's type.
|
||||
type ServerChangeTypeOpts struct {
|
||||
ServerType *ServerType // new server type
|
||||
UpgradeDisk bool // whether disk should be upgraded
|
||||
}
|
||||
|
||||
// ChangeType changes a server's type.
|
||||
func (c *ServerClient) ChangeType(ctx context.Context, server *Server, opts ServerChangeTypeOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.ServerActionChangeTypeRequest{
|
||||
UpgradeDisk: opts.UpgradeDisk,
|
||||
}
|
||||
if opts.ServerType.ID != 0 {
|
||||
reqBody.ServerType = opts.ServerType.ID
|
||||
} else {
|
||||
reqBody.ServerType = opts.ServerType.Name
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/servers/%d/actions/change_type", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionChangeTypeResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// ChangeDNSPtr changes or resets the reverse DNS pointer for a server IP address.
|
||||
// Pass a nil ptr to reset the reverse DNS pointer to its default value.
|
||||
func (c *ServerClient) ChangeDNSPtr(ctx context.Context, server *Server, ip string, ptr *string) (*Action, *Response, error) {
|
||||
reqBody := schema.ServerActionChangeDNSPtrRequest{
|
||||
IP: ip,
|
||||
DNSPtr: ptr,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/servers/%d/actions/change_dns_ptr", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionChangeDNSPtrResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// ServerChangeProtectionOpts specifies options for changing the resource protection level of a server.
|
||||
type ServerChangeProtectionOpts struct {
|
||||
Rebuild *bool
|
||||
Delete *bool
|
||||
}
|
||||
|
||||
// ChangeProtection changes the resource protection level of a server.
|
||||
func (c *ServerClient) ChangeProtection(ctx context.Context, server *Server, opts ServerChangeProtectionOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.ServerActionChangeProtectionRequest{
|
||||
Rebuild: opts.Rebuild,
|
||||
Delete: opts.Delete,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionChangeProtectionResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// ServerRequestConsoleResult is the result of requesting a WebSocket VNC console.
|
||||
type ServerRequestConsoleResult struct {
|
||||
Action *Action
|
||||
WSSURL string
|
||||
Password string
|
||||
}
|
||||
|
||||
// RequestConsole requests a WebSocket VNC console.
|
||||
func (c *ServerClient) RequestConsole(ctx context.Context, server *Server) (ServerRequestConsoleResult, *Response, error) {
|
||||
path := fmt.Sprintf("/servers/%d/actions/request_console", server.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, nil)
|
||||
if err != nil {
|
||||
return ServerRequestConsoleResult{}, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.ServerActionRequestConsoleResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return ServerRequestConsoleResult{}, resp, err
|
||||
}
|
||||
return ServerRequestConsoleResult{
|
||||
Action: ActionFromSchema(respBody.Action),
|
||||
WSSURL: respBody.WSSURL,
|
||||
Password: respBody.Password,
|
||||
}, resp, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
149
vendor/github.com/hetznercloud/hcloud-go/hcloud/server_type.go
generated
vendored
Normal file
149
vendor/github.com/hetznercloud/hcloud-go/hcloud/server_type.go
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// ServerType represents a server type in the Hetzner Cloud.
|
||||
type ServerType struct {
|
||||
ID int
|
||||
Name string
|
||||
Description string
|
||||
Cores int
|
||||
Memory float32
|
||||
Disk int
|
||||
StorageType StorageType
|
||||
CPUType CPUType
|
||||
Pricings []ServerTypeLocationPricing
|
||||
}
|
||||
|
||||
// StorageType specifies the type of storage.
|
||||
type StorageType string
|
||||
|
||||
const (
|
||||
// StorageTypeLocal is the type for local storage.
|
||||
StorageTypeLocal StorageType = "local"
|
||||
|
||||
// StorageTypeCeph is the type for remote storage.
|
||||
StorageTypeCeph StorageType = "ceph"
|
||||
)
|
||||
|
||||
// CPUType specifies the type of the CPU.
|
||||
type CPUType string
|
||||
|
||||
const (
|
||||
// CPUTypeShared is the type for shared CPU.
|
||||
CPUTypeShared CPUType = "shared"
|
||||
|
||||
//CPUTypeDedicated is the type for dedicated CPU.
|
||||
CPUTypeDedicated CPUType = "dedicated"
|
||||
)
|
||||
|
||||
// ServerTypeClient is a client for the server types API.
|
||||
type ServerTypeClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a server type by its ID. If the server type does not exist, nil is returned.
|
||||
func (c *ServerTypeClient) GetByID(ctx context.Context, id int) (*ServerType, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/server_types/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ServerTypeGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return ServerTypeFromSchema(body.ServerType), resp, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
serverTypes, response, err := c.List(ctx, ServerTypeListOpts{Name: name})
|
||||
if len(serverTypes) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return serverTypes[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a server type by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a server type by its name. If the server type does not exist, nil is returned.
|
||||
func (c *ServerTypeClient) Get(ctx context.Context, idOrName string) (*ServerType, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *ServerTypeClient) List(ctx context.Context, opts ServerTypeListOpts) ([]*ServerType, *Response, error) {
|
||||
path := "/server_types?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.ServerTypeListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
serverTypes := make([]*ServerType, 0, len(body.ServerTypes))
|
||||
for _, s := range body.ServerTypes {
|
||||
serverTypes = append(serverTypes, ServerTypeFromSchema(s))
|
||||
}
|
||||
return serverTypes, resp, nil
|
||||
}
|
||||
|
||||
// All returns all server types.
|
||||
func (c *ServerTypeClient) All(ctx context.Context) ([]*ServerType, error) {
|
||||
allServerTypes := []*ServerType{}
|
||||
|
||||
opts := ServerTypeListOpts{}
|
||||
opts.PerPage = 50
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
serverTypes, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allServerTypes = append(allServerTypes, serverTypes...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allServerTypes, nil
|
||||
}
|
233
vendor/github.com/hetznercloud/hcloud-go/hcloud/ssh_key.go
generated
vendored
Normal file
233
vendor/github.com/hetznercloud/hcloud-go/hcloud/ssh_key.go
generated
vendored
Normal file
|
@ -0,0 +1,233 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// SSHKey represents a SSH key in the Hetzner Cloud.
|
||||
type SSHKey struct {
|
||||
ID int
|
||||
Name string
|
||||
Fingerprint string
|
||||
PublicKey string
|
||||
Labels map[string]string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
// SSHKeyClient is a client for the SSH keys API.
|
||||
type SSHKeyClient struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// GetByID retrieves a SSH key by its ID. If the SSH key does not exist, nil is returned.
|
||||
func (c *SSHKeyClient) GetByID(ctx context.Context, id int) (*SSHKey, *Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/ssh_keys/%d", id), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.SSHKeyGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return SSHKeyFromSchema(body.SSHKey), resp, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
sshKeys, response, err := c.List(ctx, SSHKeyListOpts{Name: name})
|
||||
if len(sshKeys) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
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) {
|
||||
sshKeys, response, err := c.List(ctx, SSHKeyListOpts{Fingerprint: fingerprint})
|
||||
if len(sshKeys) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return sshKeys[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a SSH key by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a SSH key by its name. If the SSH key does not exist, nil is returned.
|
||||
func (c *SSHKeyClient) Get(ctx context.Context, idOrName string) (*SSHKey, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *SSHKeyClient) List(ctx context.Context, opts SSHKeyListOpts) ([]*SSHKey, *Response, error) {
|
||||
path := "/ssh_keys?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.SSHKeyListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sshKeys := make([]*SSHKey, 0, len(body.SSHKeys))
|
||||
for _, s := range body.SSHKeys {
|
||||
sshKeys = append(sshKeys, SSHKeyFromSchema(s))
|
||||
}
|
||||
return sshKeys, resp, nil
|
||||
}
|
||||
|
||||
// All returns all SSH keys.
|
||||
func (c *SSHKeyClient) All(ctx context.Context) ([]*SSHKey, error) {
|
||||
return c.AllWithOpts(ctx, SSHKeyListOpts{ListOpts: ListOpts{PerPage: 50}})
|
||||
}
|
||||
|
||||
// AllWithOpts returns all SSH keys with the given options.
|
||||
func (c *SSHKeyClient) AllWithOpts(ctx context.Context, opts SSHKeyListOpts) ([]*SSHKey, error) {
|
||||
allSSHKeys := []*SSHKey{}
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
sshKeys, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allSSHKeys = append(allSSHKeys, sshKeys...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allSSHKeys, nil
|
||||
}
|
||||
|
||||
// SSHKeyCreateOpts specifies parameters for creating a SSH key.
|
||||
type SSHKeyCreateOpts struct {
|
||||
Name string
|
||||
PublicKey string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Validate checks if options are valid.
|
||||
func (o SSHKeyCreateOpts) Validate() error {
|
||||
if o.Name == "" {
|
||||
return errors.New("missing name")
|
||||
}
|
||||
if o.PublicKey == "" {
|
||||
return errors.New("missing public key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create creates a new SSH key with the given options.
|
||||
func (c *SSHKeyClient) Create(ctx context.Context, opts SSHKeyCreateOpts) (*SSHKey, *Response, error) {
|
||||
if err := opts.Validate(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
reqBody := schema.SSHKeyCreateRequest{
|
||||
Name: opts.Name,
|
||||
PublicKey: opts.PublicKey,
|
||||
}
|
||||
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", "/ssh_keys", bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.SSHKeyCreateResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return SSHKeyFromSchema(respBody.SSHKey), resp, nil
|
||||
}
|
||||
|
||||
// Delete deletes a SSH key.
|
||||
func (c *SSHKeyClient) Delete(ctx context.Context, sshKey *SSHKey) (*Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/ssh_keys/%d", sshKey.ID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// SSHKeyUpdateOpts specifies options for updating a SSH key.
|
||||
type SSHKeyUpdateOpts struct {
|
||||
Name string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Update updates a SSH key.
|
||||
func (c *SSHKeyClient) Update(ctx context.Context, sshKey *SSHKey, opts SSHKeyUpdateOpts) (*SSHKey, *Response, error) {
|
||||
reqBody := schema.SSHKeyUpdateRequest{
|
||||
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("/ssh_keys/%d", sshKey.ID)
|
||||
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.SSHKeyUpdateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return SSHKeyFromSchema(respBody.SSHKey), resp, nil
|
||||
}
|
399
vendor/github.com/hetznercloud/hcloud-go/hcloud/volume.go
generated
vendored
Normal file
399
vendor/github.com/hetznercloud/hcloud-go/hcloud/volume.go
generated
vendored
Normal file
|
@ -0,0 +1,399 @@
|
|||
package hcloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hetznercloud/hcloud-go/hcloud/schema"
|
||||
)
|
||||
|
||||
// Volume represents a volume in the Hetzner Cloud.
|
||||
type Volume struct {
|
||||
ID int
|
||||
Name string
|
||||
Status VolumeStatus
|
||||
Server *Server
|
||||
Location *Location
|
||||
Size int
|
||||
Protection VolumeProtection
|
||||
Labels map[string]string
|
||||
LinuxDevice string
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
// VolumeProtection represents the protection level of a volume.
|
||||
type VolumeProtection struct {
|
||||
Delete bool
|
||||
}
|
||||
|
||||
// VolumeClient is a client for the volume API.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.VolumeGetResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
if IsError(err, ErrorCodeNotFound) {
|
||||
return nil, resp, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
return VolumeFromSchema(body.Volume), resp, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if name == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
volumes, response, err := c.List(ctx, VolumeListOpts{Name: name})
|
||||
if len(volumes) == 0 {
|
||||
return nil, response, err
|
||||
}
|
||||
return volumes[0], response, err
|
||||
}
|
||||
|
||||
// Get retrieves a volume by its ID if the input can be parsed as an integer, otherwise it
|
||||
// retrieves a volume by its name. If the volume does not exist, nil is returned.
|
||||
func (c *VolumeClient) Get(ctx context.Context, idOrName string) (*Volume, *Response, error) {
|
||||
if id, err := strconv.Atoi(idOrName); err == nil {
|
||||
return c.GetByID(ctx, int(id))
|
||||
}
|
||||
return c.GetByName(ctx, idOrName)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Please note that filters specified in opts are not taken into account
|
||||
// when their value corresponds to their zero value or when they are empty.
|
||||
func (c *VolumeClient) List(ctx context.Context, opts VolumeListOpts) ([]*Volume, *Response, error) {
|
||||
path := "/volumes?" + opts.values().Encode()
|
||||
req, err := c.client.NewRequest(ctx, "GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body schema.VolumeListResponse
|
||||
resp, err := c.client.Do(req, &body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
volumes := make([]*Volume, 0, len(body.Volumes))
|
||||
for _, s := range body.Volumes {
|
||||
volumes = append(volumes, VolumeFromSchema(s))
|
||||
}
|
||||
return volumes, resp, nil
|
||||
}
|
||||
|
||||
// All returns all volumes.
|
||||
func (c *VolumeClient) All(ctx context.Context) ([]*Volume, error) {
|
||||
return c.AllWithOpts(ctx, VolumeListOpts{ListOpts: ListOpts{PerPage: 50}})
|
||||
}
|
||||
|
||||
// AllWithOpts returns all volumes with the given options.
|
||||
func (c *VolumeClient) AllWithOpts(ctx context.Context, opts VolumeListOpts) ([]*Volume, error) {
|
||||
allVolumes := []*Volume{}
|
||||
|
||||
_, err := c.client.all(func(page int) (*Response, error) {
|
||||
opts.Page = page
|
||||
volumes, resp, err := c.List(ctx, opts)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
allVolumes = append(allVolumes, volumes...)
|
||||
return resp, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return allVolumes, nil
|
||||
}
|
||||
|
||||
// VolumeCreateOpts specifies parameters for creating a volume.
|
||||
type VolumeCreateOpts struct {
|
||||
Name string
|
||||
Size int
|
||||
Server *Server
|
||||
Location *Location
|
||||
Labels map[string]string
|
||||
Automount *bool
|
||||
Format *string
|
||||
}
|
||||
|
||||
// Validate checks if options are valid.
|
||||
func (o VolumeCreateOpts) Validate() error {
|
||||
if o.Name == "" {
|
||||
return errors.New("missing name")
|
||||
}
|
||||
if o.Size <= 0 {
|
||||
return errors.New("size must be greater than 0")
|
||||
}
|
||||
if o.Server == nil && o.Location == nil {
|
||||
return errors.New("one of server or location must be provided")
|
||||
}
|
||||
if o.Server != nil && o.Location != nil {
|
||||
return errors.New("only one of server or location must be provided")
|
||||
}
|
||||
if o.Server == nil && (o.Automount != nil && *o.Automount) {
|
||||
return errors.New("server must be provided when automount is true")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VolumeCreateResult is the result of creating a volume.
|
||||
type VolumeCreateResult struct {
|
||||
Volume *Volume
|
||||
Action *Action
|
||||
NextActions []*Action
|
||||
}
|
||||
|
||||
// Create creates a new volume with the given options.
|
||||
func (c *VolumeClient) Create(ctx context.Context, opts VolumeCreateOpts) (VolumeCreateResult, *Response, error) {
|
||||
if err := opts.Validate(); err != nil {
|
||||
return VolumeCreateResult{}, nil, err
|
||||
}
|
||||
reqBody := schema.VolumeCreateRequest{
|
||||
Name: opts.Name,
|
||||
Size: opts.Size,
|
||||
Automount: opts.Automount,
|
||||
Format: opts.Format,
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
reqBody.Labels = &opts.Labels
|
||||
}
|
||||
if opts.Server != nil {
|
||||
reqBody.Server = Int(opts.Server.ID)
|
||||
}
|
||||
if opts.Location != nil {
|
||||
if opts.Location.ID != 0 {
|
||||
reqBody.Location = opts.Location.ID
|
||||
} else {
|
||||
reqBody.Location = opts.Location.Name
|
||||
}
|
||||
}
|
||||
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return VolumeCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "POST", "/volumes", bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return VolumeCreateResult{}, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.VolumeCreateResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return VolumeCreateResult{}, resp, err
|
||||
}
|
||||
|
||||
var action *Action
|
||||
if respBody.Action != nil {
|
||||
action = ActionFromSchema(*respBody.Action)
|
||||
}
|
||||
|
||||
return VolumeCreateResult{
|
||||
Volume: VolumeFromSchema(respBody.Volume),
|
||||
Action: action,
|
||||
NextActions: ActionsFromSchema(respBody.NextActions),
|
||||
}, resp, nil
|
||||
}
|
||||
|
||||
// Delete deletes a volume.
|
||||
func (c *VolumeClient) Delete(ctx context.Context, volume *Volume) (*Response, error) {
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", fmt.Sprintf("/volumes/%d", volume.ID), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// VolumeUpdateOpts specifies options for updating a volume.
|
||||
type VolumeUpdateOpts struct {
|
||||
Name string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// Update updates a volume.
|
||||
func (c *VolumeClient) Update(ctx context.Context, volume *Volume, opts VolumeUpdateOpts) (*Volume, *Response, error) {
|
||||
reqBody := schema.VolumeUpdateRequest{
|
||||
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("/volumes/%d", volume.ID)
|
||||
req, err := c.client.NewRequest(ctx, "PUT", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.VolumeUpdateResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return VolumeFromSchema(respBody.Volume), resp, nil
|
||||
}
|
||||
|
||||
// VolumeAttachOpts specifies options for attaching a volume.
|
||||
type VolumeAttachOpts struct {
|
||||
Server *Server
|
||||
Automount *bool
|
||||
}
|
||||
|
||||
// AttachWithOpts attaches a volume to a server.
|
||||
func (c *VolumeClient) AttachWithOpts(ctx context.Context, volume *Volume, opts VolumeAttachOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.VolumeActionAttachVolumeRequest{
|
||||
Server: opts.Server.ID,
|
||||
Automount: opts.Automount,
|
||||
}
|
||||
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/volumes/%d/actions/attach", volume.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.VolumeActionAttachVolumeResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// Attach attaches a volume to a server.
|
||||
func (c *VolumeClient) Attach(ctx context.Context, volume *Volume, server *Server) (*Action, *Response, error) {
|
||||
return c.AttachWithOpts(ctx, volume, VolumeAttachOpts{Server: server})
|
||||
}
|
||||
|
||||
// Detach detaches a volume from a server.
|
||||
func (c *VolumeClient) Detach(ctx context.Context, volume *Volume) (*Action, *Response, error) {
|
||||
var reqBody schema.VolumeActionDetachVolumeRequest
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/volumes/%d/actions/detach", volume.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var respBody schema.VolumeActionDetachVolumeResponse
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, nil
|
||||
}
|
||||
|
||||
// VolumeChangeProtectionOpts specifies options for changing the resource protection level of a volume.
|
||||
type VolumeChangeProtectionOpts struct {
|
||||
Delete *bool
|
||||
}
|
||||
|
||||
// ChangeProtection changes the resource protection level of a volume.
|
||||
func (c *VolumeClient) ChangeProtection(ctx context.Context, volume *Volume, opts VolumeChangeProtectionOpts) (*Action, *Response, error) {
|
||||
reqBody := schema.VolumeActionChangeProtectionRequest{
|
||||
Delete: opts.Delete,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/volumes/%d/actions/change_protection", volume.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.VolumeActionChangeProtectionResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
||||
|
||||
// Resize changes the size of a volume.
|
||||
func (c *VolumeClient) Resize(ctx context.Context, volume *Volume, size int) (*Action, *Response, error) {
|
||||
reqBody := schema.VolumeActionResizeVolumeRequest{
|
||||
Size: size,
|
||||
}
|
||||
reqBodyData, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/volumes/%d/actions/resize", volume.ID)
|
||||
req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
respBody := schema.VolumeActionResizeVolumeResponse{}
|
||||
resp, err := c.client.Do(req, &respBody)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return ActionFromSchema(respBody.Action), resp, err
|
||||
}
|
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
|
@ -230,7 +230,7 @@ github.com/golang/protobuf/ptypes/wrappers
|
|||
# github.com/golang/snappy v0.0.1
|
||||
## explicit
|
||||
github.com/golang/snappy
|
||||
# github.com/google/go-cmp v0.4.0
|
||||
# github.com/google/go-cmp v0.5.0
|
||||
github.com/google/go-cmp/cmp
|
||||
github.com/google/go-cmp/cmp/internal/diff
|
||||
github.com/google/go-cmp/cmp/internal/flags
|
||||
|
@ -298,6 +298,10 @@ github.com/hashicorp/golang-lru
|
|||
github.com/hashicorp/golang-lru/simplelru
|
||||
# github.com/hashicorp/serf v0.9.0
|
||||
github.com/hashicorp/serf/coordinate
|
||||
# github.com/hetznercloud/hcloud-go v1.21.1
|
||||
## explicit
|
||||
github.com/hetznercloud/hcloud-go/hcloud
|
||||
github.com/hetznercloud/hcloud-go/hcloud/schema
|
||||
# github.com/influxdata/influxdb v1.8.1
|
||||
## explicit
|
||||
github.com/influxdata/influxdb/client/v2
|
||||
|
|
Loading…
Reference in a new issue