mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-13 17:14: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/dockerswarm"
|
||||||
"github.com/prometheus/prometheus/discovery/ec2"
|
"github.com/prometheus/prometheus/discovery/ec2"
|
||||||
"github.com/prometheus/prometheus/discovery/file"
|
"github.com/prometheus/prometheus/discovery/file"
|
||||||
|
"github.com/prometheus/prometheus/discovery/hetzner"
|
||||||
"github.com/prometheus/prometheus/discovery/kubernetes"
|
"github.com/prometheus/prometheus/discovery/kubernetes"
|
||||||
"github.com/prometheus/prometheus/discovery/marathon"
|
"github.com/prometheus/prometheus/discovery/marathon"
|
||||||
"github.com/prometheus/prometheus/discovery/openstack"
|
"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{
|
AlertingConfig: AlertingConfig{
|
||||||
AlertmanagerConfigs: []*AlertmanagerConfig{
|
AlertmanagerConfigs: []*AlertmanagerConfig{
|
||||||
|
@ -717,7 +746,7 @@ func TestElideSecrets(t *testing.T) {
|
||||||
yamlConfig := string(config)
|
yamlConfig := string(config)
|
||||||
|
|
||||||
matches := secretRe.FindAllStringIndex(yamlConfig, -1)
|
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"),
|
testutil.Assert(t, !strings.Contains(yamlConfig, "mysecret"),
|
||||||
"yaml marshal reveals authentication credentials.")
|
"yaml marshal reveals authentication credentials.")
|
||||||
}
|
}
|
||||||
|
@ -963,6 +992,10 @@ var expectedErrors = []struct {
|
||||||
filename: "empty_static_config.bad.yml",
|
filename: "empty_static_config.bad.yml",
|
||||||
errMsg: "empty or null section in static_configs",
|
errMsg: "empty or null section in static_configs",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
filename: "hetzner_role.bad.yml",
|
||||||
|
errMsg: "unknown role",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBadConfigs(t *testing.T) {
|
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
|
cert_file: valid_cert_file
|
||||||
key_file: valid_key_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:
|
alerting:
|
||||||
alertmanagers:
|
alertmanagers:
|
||||||
- scheme: https
|
- 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/ec2" // register ec2
|
||||||
_ "github.com/prometheus/prometheus/discovery/file" // register file
|
_ "github.com/prometheus/prometheus/discovery/file" // register file
|
||||||
_ "github.com/prometheus/prometheus/discovery/gce" // register gce
|
_ "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/kubernetes" // register kubernetes
|
||||||
_ "github.com/prometheus/prometheus/discovery/marathon" // register marathon
|
_ "github.com/prometheus/prometheus/discovery/marathon" // register marathon
|
||||||
_ "github.com/prometheus/prometheus/discovery/openstack" // register openstack
|
_ "github.com/prometheus/prometheus/discovery/openstack" // register openstack
|
||||||
|
|
|
@ -215,6 +215,10 @@ file_sd_configs:
|
||||||
gce_sd_configs:
|
gce_sd_configs:
|
||||||
[ - <gce_sd_config> ... ]
|
[ - <gce_sd_config> ... ]
|
||||||
|
|
||||||
|
# List of Hetzner service discovery configurations.
|
||||||
|
hetzner_sd_configs:
|
||||||
|
[ - <hetzner_sd_config> ... ]
|
||||||
|
|
||||||
# List of Kubernetes service discovery configurations.
|
# List of Kubernetes service discovery configurations.
|
||||||
kubernetes_sd_configs:
|
kubernetes_sd_configs:
|
||||||
[ - <kubernetes_sd_config> ... ]
|
[ - <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
|
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.
|
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_config>`
|
||||||
|
|
||||||
Kubernetes SD configurations allow retrieving scrape targets from
|
Kubernetes SD configurations allow retrieving scrape targets from
|
||||||
|
@ -1494,6 +1573,10 @@ dockerswarm_sd_configs:
|
||||||
gce_sd_configs:
|
gce_sd_configs:
|
||||||
[ - <gce_sd_config> ... ]
|
[ - <gce_sd_config> ... ]
|
||||||
|
|
||||||
|
# List of Hetzner service discovery configurations.
|
||||||
|
hetzner_sd_configs:
|
||||||
|
[ - <hetzner_sd_config> ... ]
|
||||||
|
|
||||||
# List of Kubernetes service discovery configurations.
|
# List of Kubernetes service discovery configurations.
|
||||||
kubernetes_sd_configs:
|
kubernetes_sd_configs:
|
||||||
[ - <kubernetes_sd_config> ... ]
|
[ - <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-hclog v0.12.2 // indirect
|
||||||
github.com/hashicorp/go-immutable-radix v1.2.0 // indirect
|
github.com/hashicorp/go-immutable-radix v1.2.0 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // 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/influxdata/influxdb v1.8.1
|
||||||
github.com/json-iterator/go v1.1.10
|
github.com/json-iterator/go v1.1.10
|
||||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
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.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 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
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 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
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=
|
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.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 h1:+Zd/16AJ9lxk9RzfTDyv/TLhZ8UerqYS0/+JGCIDaa0=
|
||||||
github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU=
|
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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
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
|
// This package is intended to be a more powerful and safer alternative to
|
||||||
// reflect.DeepEqual for comparing whether two values are semantically equal.
|
// 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:
|
// The primary features of cmp are:
|
||||||
//
|
//
|
||||||
|
@ -86,6 +90,52 @@ import (
|
||||||
// If there is a cycle, then the pointed at values are considered equal
|
// 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.
|
// only if both addresses were previously visited in the same path step.
|
||||||
func Equal(x, y interface{}, opts ...Option) bool {
|
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)
|
vx := reflect.ValueOf(x)
|
||||||
vy := reflect.ValueOf(y)
|
vy := reflect.ValueOf(y)
|
||||||
|
|
||||||
|
@ -108,33 +158,7 @@ func Equal(x, y interface{}, opts ...Option) bool {
|
||||||
t = vx.Type()
|
t = vx.Type()
|
||||||
}
|
}
|
||||||
|
|
||||||
s := newState(opts)
|
return &pathStep{t, vx, vy}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type state struct {
|
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.
|
// assuming that T is assignable to R.
|
||||||
// Otherwise, it returns the input value as is.
|
// Otherwise, it returns the input value as is.
|
||||||
func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
|
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 !flags.AtLeastGo110 {
|
||||||
if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
|
if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
|
||||||
return reflect.New(t).Elem()
|
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) {
|
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 vax, vay reflect.Value // Addressable versions of vx and vy
|
||||||
|
|
||||||
var mayForce, mayForceInit bool
|
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
|
// For retrieveUnexportedField to work, the parent struct must
|
||||||
// be addressable. Create a new copy of the values if
|
// be addressable. Create a new copy of the values if
|
||||||
// necessary to make them addressable.
|
// necessary to make them addressable.
|
||||||
|
addr = vx.CanAddr() || vy.CanAddr()
|
||||||
vax = makeAddressable(vx)
|
vax = makeAddressable(vx)
|
||||||
vay = makeAddressable(vy)
|
vay = makeAddressable(vy)
|
||||||
}
|
}
|
||||||
|
@ -393,6 +419,7 @@ func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
|
||||||
mayForceInit = true
|
mayForceInit = true
|
||||||
}
|
}
|
||||||
step.mayForce = mayForce
|
step.mayForce = mayForce
|
||||||
|
step.paddr = addr
|
||||||
step.pvx = vax
|
step.pvx = vax
|
||||||
step.pvy = vay
|
step.pvy = vay
|
||||||
step.field = t.Field(i)
|
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
|
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")
|
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.
|
// a struct such that the value has read-write permissions.
|
||||||
//
|
//
|
||||||
// The parent struct, v, must be addressable, while f must be a StructField
|
// The parent struct, v, must be addressable, while f must be a StructField
|
||||||
// describing the field to retrieve.
|
// describing the field to retrieve. If addr is false,
|
||||||
func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value {
|
// then the returned value will be shallowed copied to be non-addressable.
|
||||||
// See https://github.com/google/go-cmp/issues/167 for discussion of the
|
func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value {
|
||||||
// following expression.
|
ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem()
|
||||||
return 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.
|
// is more important than obtaining a minimal Levenshtein distance.
|
||||||
package diff
|
package diff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp/internal/flags"
|
||||||
|
)
|
||||||
|
|
||||||
// EditType represents a single operation within an edit-script.
|
// EditType represents a single operation within an edit-script.
|
||||||
type EditType uint8
|
type EditType uint8
|
||||||
|
|
||||||
|
@ -112,6 +119,8 @@ func (r Result) Similar() bool {
|
||||||
return r.NumSame+1 >= r.NumDiff
|
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
|
// Difference reports whether two lists of lengths nx and ny are equal
|
||||||
// given the definition of equality provided as f.
|
// 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 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.
|
// 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:
|
// Invariants:
|
||||||
// • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx
|
// • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx
|
||||||
// • 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny
|
// • 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 {
|
if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 {
|
||||||
break
|
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.
|
// Search in a diagonal pattern for a match.
|
||||||
z := zigzag(i)
|
z := zigzag(i)
|
||||||
p := point{fwdFrontier.X + z, fwdFrontier.Y - z}
|
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.
|
// assumes that the GC implementation does not use a moving collector.
|
||||||
return Pointer{v.Pointer(), v.Type()}
|
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.
|
// which is necessary if the GC ever uses a moving collector.
|
||||||
return Pointer{unsafe.Pointer(v.Pointer()), v.Type()}
|
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.
|
// pvx, pvy, and field are only valid if unexported is true.
|
||||||
unexported bool
|
unexported bool
|
||||||
mayForce bool // Forcibly allow visibility
|
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
|
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.
|
// Forcibly obtain read-write access to an unexported struct field.
|
||||||
if sf.mayForce {
|
if sf.mayForce {
|
||||||
vx = retrieveUnexportedField(sf.pvx, sf.field)
|
vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr)
|
||||||
vy = retrieveUnexportedField(sf.pvy, sf.field)
|
vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr)
|
||||||
return vx, vy // CanInterface reports true
|
return vx, vy // CanInterface reports true
|
||||||
}
|
}
|
||||||
return sf.vx, sf.vy // CanInterface reports false
|
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 {
|
if r.root.NumDiff == 0 {
|
||||||
return ""
|
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) {
|
func assert(ok bool) {
|
||||||
|
|
198
vendor/github.com/google/go-cmp/cmp/report_compare.go
generated
vendored
198
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"
|
"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.
|
// numContextRecords is the number of surrounding equal records to print.
|
||||||
const numContextRecords = 2
|
const numContextRecords = 2
|
||||||
|
|
||||||
|
@ -71,24 +63,66 @@ func (opts formatOptions) WithTypeMode(t typeMode) formatOptions {
|
||||||
opts.TypeMode = t
|
opts.TypeMode = t
|
||||||
return opts
|
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
|
// FormatDiff converts a valueNode tree into a textNode tree, where the later
|
||||||
// is a textual representation of the differences detected in the former.
|
// 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.
|
// Check whether we have specialized formatting for this node.
|
||||||
// This is not necessary, but helpful for producing more readable outputs.
|
// This is not necessary, but helpful for producing more readable outputs.
|
||||||
if opts.CanFormatDiffSlice(v) {
|
if opts.CanFormatDiffSlice(v) {
|
||||||
return opts.FormatDiffSlice(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.
|
// For leaf nodes, format the value based on the reflect.Values alone.
|
||||||
if v.MaxDepth == 0 {
|
if v.MaxDepth == 0 {
|
||||||
switch opts.DiffMode {
|
switch opts.DiffMode {
|
||||||
case diffUnknown, diffIdentical:
|
case diffUnknown, diffIdentical:
|
||||||
// Format Equal.
|
// Format Equal.
|
||||||
if v.NumDiff == 0 {
|
if v.NumDiff == 0 {
|
||||||
outx := opts.FormatValue(v.ValueX, visitedPointers{})
|
outx := opts.FormatValue(v.ValueX, parentKind, ptrs)
|
||||||
outy := opts.FormatValue(v.ValueY, visitedPointers{})
|
outy := opts.FormatValue(v.ValueY, parentKind, ptrs)
|
||||||
if v.NumIgnored > 0 && v.NumSame == 0 {
|
if v.NumIgnored > 0 && v.NumSame == 0 {
|
||||||
return textEllipsis
|
return textEllipsis
|
||||||
} else if outx.Len() < outy.Len() {
|
} else if outx.Len() < outy.Len() {
|
||||||
|
@ -101,8 +135,13 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode {
|
||||||
// Format unequal.
|
// Format unequal.
|
||||||
assert(opts.DiffMode == diffUnknown)
|
assert(opts.DiffMode == diffUnknown)
|
||||||
var list textList
|
var list textList
|
||||||
outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, visitedPointers{})
|
outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs)
|
||||||
outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, visitedPointers{})
|
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 {
|
if outx != nil {
|
||||||
list = append(list, textRecord{Diff: '-', Value: outx})
|
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)
|
return opts.WithTypeMode(emitType).FormatType(v.Type, list)
|
||||||
case diffRemoved:
|
case diffRemoved:
|
||||||
return opts.FormatValue(v.ValueX, visitedPointers{})
|
return opts.FormatValue(v.ValueX, parentKind, ptrs)
|
||||||
case diffInserted:
|
case diffInserted:
|
||||||
return opts.FormatValue(v.ValueY, visitedPointers{})
|
return opts.FormatValue(v.ValueY, parentKind, ptrs)
|
||||||
default:
|
default:
|
||||||
panic("invalid diff mode")
|
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.
|
// Descend into the child value node.
|
||||||
if v.TransformerName != "" {
|
if v.TransformerName != "" {
|
||||||
out := opts.WithTypeMode(emitType).FormatDiff(v.Value)
|
out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
|
||||||
out = textWrap{"Inverse(" + v.TransformerName + ", ", out, ")"}
|
out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"}
|
||||||
return opts.FormatType(v.Type, out)
|
return opts.FormatType(v.Type, out)
|
||||||
} else {
|
} else {
|
||||||
switch k := v.Type.Kind(); k {
|
switch k := v.Type.Kind(); k {
|
||||||
case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map:
|
case reflect.Struct, reflect.Array, reflect.Slice:
|
||||||
return opts.FormatType(v.Type, opts.formatDiffList(v.Records, k))
|
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:
|
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:
|
case reflect.Interface:
|
||||||
return opts.WithTypeMode(emitType).FormatDiff(v.Value)
|
out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("%v cannot have children", k))
|
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.
|
// Derive record name based on the data structure kind.
|
||||||
var name string
|
var name string
|
||||||
var formatKey func(reflect.Value) string
|
var formatKey func(reflect.Value) string
|
||||||
|
@ -154,7 +216,17 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
name = "entry"
|
name = "entry"
|
||||||
opts = opts.WithTypeMode(elideType)
|
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.
|
// Handle unification.
|
||||||
|
@ -163,6 +235,11 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
||||||
var list textList
|
var list textList
|
||||||
var deferredEllipsis bool // Add final "..." to indicate records were dropped
|
var deferredEllipsis bool // Add final "..." to indicate records were dropped
|
||||||
for _, r := range recs {
|
for _, r := range recs {
|
||||||
|
if len(list) == maxLen {
|
||||||
|
deferredEllipsis = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// Elide struct fields that are zero value.
|
// Elide struct fields that are zero value.
|
||||||
if k == reflect.Struct {
|
if k == reflect.Struct {
|
||||||
var isZero bool
|
var isZero bool
|
||||||
|
@ -186,23 +263,31 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
||||||
}
|
}
|
||||||
continue
|
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})
|
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if deferredEllipsis {
|
if deferredEllipsis {
|
||||||
list.AppendEllipsis(diffStats{})
|
list.AppendEllipsis(diffStats{})
|
||||||
}
|
}
|
||||||
return textWrap{"{", list, "}"}
|
return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||||
case diffUnknown:
|
case diffUnknown:
|
||||||
default:
|
default:
|
||||||
panic("invalid diff mode")
|
panic("invalid diff mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle differencing.
|
// Handle differencing.
|
||||||
|
var numDiffs int
|
||||||
var list textList
|
var list textList
|
||||||
|
var keys []reflect.Value // invariant: len(list) == len(keys)
|
||||||
groups := coalesceAdjacentRecords(name, recs)
|
groups := coalesceAdjacentRecords(name, recs)
|
||||||
|
maxGroup := diffStats{Name: name}
|
||||||
for i, ds := range groups {
|
for i, ds := range groups {
|
||||||
|
if maxLen >= 0 && numDiffs >= maxLen {
|
||||||
|
maxGroup = maxGroup.Append(ds)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Handle equal records.
|
// Handle equal records.
|
||||||
if ds.NumDiff() == 0 {
|
if ds.NumDiff() == 0 {
|
||||||
// Compute the number of leading and trailing records to print.
|
// 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.
|
// Format the equal values.
|
||||||
for _, r := range recs[:numLo] {
|
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})
|
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||||
|
keys = append(keys, r.Key)
|
||||||
}
|
}
|
||||||
if numEqual > numLo+numHi {
|
if numEqual > numLo+numHi {
|
||||||
ds.NumIdentical -= numLo + numHi
|
ds.NumIdentical -= numLo + numHi
|
||||||
list.AppendEllipsis(ds)
|
list.AppendEllipsis(ds)
|
||||||
|
for len(keys) < len(list) {
|
||||||
|
keys = append(keys, reflect.Value{})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, r := range recs[numEqual-numHi : numEqual] {
|
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})
|
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||||
|
keys = append(keys, r.Key)
|
||||||
}
|
}
|
||||||
recs = recs[numEqual:]
|
recs = recs[numEqual:]
|
||||||
continue
|
continue
|
||||||
|
@ -247,24 +337,70 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te
|
||||||
case opts.CanFormatDiffSlice(r.Value):
|
case opts.CanFormatDiffSlice(r.Value):
|
||||||
out := opts.FormatDiffSlice(r.Value)
|
out := opts.FormatDiffSlice(r.Value)
|
||||||
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||||
|
keys = append(keys, r.Key)
|
||||||
case r.Value.NumChildren == r.Value.MaxDepth:
|
case r.Value.NumChildren == r.Value.MaxDepth:
|
||||||
outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value)
|
outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs)
|
||||||
outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value)
|
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 {
|
if outx != nil {
|
||||||
list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx})
|
list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx})
|
||||||
|
keys = append(keys, r.Key)
|
||||||
}
|
}
|
||||||
if outy != nil {
|
if outy != nil {
|
||||||
list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy})
|
list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy})
|
||||||
|
keys = append(keys, r.Key)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
out := opts.FormatDiff(r.Value)
|
out := opts.FormatDiff(r.Value, ptrs)
|
||||||
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
|
||||||
|
keys = append(keys, r.Key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recs = recs[ds.NumDiff():]
|
recs = recs[ds.NumDiff():]
|
||||||
|
numDiffs += ds.NumDiff()
|
||||||
}
|
}
|
||||||
|
if maxGroup.IsZero() {
|
||||||
assert(len(recs) == 0)
|
assert(len(recs) == 0)
|
||||||
return textWrap{"{", list, "}"}
|
} 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
|
// 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"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp/internal/flags"
|
|
||||||
"github.com/google/go-cmp/cmp/internal/value"
|
"github.com/google/go-cmp/cmp/internal/value"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,14 +20,22 @@ type formatValueOptions struct {
|
||||||
// methods like error.Error or fmt.Stringer.String.
|
// methods like error.Error or fmt.Stringer.String.
|
||||||
AvoidStringer bool
|
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,
|
// PrintAddresses controls whether to print the address of all pointers,
|
||||||
// slice elements, and maps.
|
// slice elements, and maps.
|
||||||
PrintAddresses bool
|
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.
|
// 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:
|
default:
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
if opts.DiffMode == diffIdentical {
|
||||||
|
return s // elide type for identical nodes
|
||||||
|
}
|
||||||
case elideType:
|
case elideType:
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the type label, applying special handling for unnamed types.
|
// Determine the type label, applying special handling for unnamed types.
|
||||||
typeName := t.String()
|
typeName := value.TypeString(t, opts.QualifiedNames)
|
||||||
if t.Name() == "" {
|
if t.Name() == "" {
|
||||||
// According to Go grammar, certain type literals contain symbols that
|
// According to Go grammar, certain type literals contain symbols that
|
||||||
// do not strongly bind to the next lexicographical token (e.g., *T).
|
// 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:
|
case reflect.Chan, reflect.Func, reflect.Ptr:
|
||||||
typeName = "(" + typeName + ")"
|
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.
|
// wrapParens wraps s with a set of parenthesis, but avoids it if the
|
||||||
if s, ok := s.(textWrap); ok {
|
// wrapped node itself is already surrounded by a pair of parenthesis or braces.
|
||||||
hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")")
|
// It handles unwrapping one level of pointer-reference nodes.
|
||||||
hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}")
|
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 {
|
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
|
// FormatValue prints the reflect.Value, taking extra care to avoid descending
|
||||||
// into pointers already in m. As pointers are visited, m is also updated.
|
// into pointers already in ptrs. As pointers are visited, ptrs is also updated.
|
||||||
func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
|
func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) {
|
||||||
if !v.IsValid() {
|
if !v.IsValid() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t := v.Type()
|
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.
|
// Check whether there is an Error or String method to call.
|
||||||
if !opts.AvoidStringer && v.CanInterface() {
|
if !opts.AvoidStringer && v.CanInterface() {
|
||||||
// Avoid calling Error or String methods on nil receivers since many
|
// Avoid calling Error or String methods on nil receivers since many
|
||||||
// implementations crash when doing so.
|
// implementations crash when doing so.
|
||||||
if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
|
if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
|
||||||
|
var prefix, strVal string
|
||||||
switch v := v.Interface().(type) {
|
switch v := v.Interface().(type) {
|
||||||
case error:
|
case error:
|
||||||
return textLine("e" + formatString(v.Error()))
|
prefix, strVal = "e", v.Error()
|
||||||
case fmt.Stringer:
|
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() {
|
switch t.Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return textLine(fmt.Sprint(v.Bool()))
|
return textLine(fmt.Sprint(v.Bool()))
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
return textLine(fmt.Sprint(v.Int()))
|
return textLine(fmt.Sprint(v.Int()))
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
// Unnamed uints are usually bytes or words, so use hexadecimal.
|
return textLine(fmt.Sprint(v.Uint()))
|
||||||
if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
|
case reflect.Uint8:
|
||||||
|
if parentKind == reflect.Slice || parentKind == reflect.Array {
|
||||||
return textLine(formatHex(v.Uint()))
|
return textLine(formatHex(v.Uint()))
|
||||||
}
|
}
|
||||||
return textLine(fmt.Sprint(v.Uint()))
|
return textLine(fmt.Sprint(v.Uint()))
|
||||||
|
case reflect.Uintptr:
|
||||||
|
return textLine(formatHex(v.Uint()))
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
return textLine(fmt.Sprint(v.Float()))
|
return textLine(fmt.Sprint(v.Float()))
|
||||||
case reflect.Complex64, reflect.Complex128:
|
case reflect.Complex64, reflect.Complex128:
|
||||||
return textLine(fmt.Sprint(v.Complex()))
|
return textLine(fmt.Sprint(v.Complex()))
|
||||||
case reflect.String:
|
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()))
|
return textLine(formatString(v.String()))
|
||||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||||
return textLine(formatPointer(v))
|
return textLine(formatPointer(value.PointerOf(v), true))
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
var list textList
|
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++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
vv := v.Field(i)
|
vv := v.Field(i)
|
||||||
if value.IsZero(vv) {
|
if value.IsZero(vv) {
|
||||||
continue // Elide fields with zero values
|
continue // Elide fields with zero values
|
||||||
}
|
}
|
||||||
s := opts.WithTypeMode(autoType).FormatValue(vv, m)
|
if len(list) == maxLen {
|
||||||
list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
|
list.AppendEllipsis(diffStats{})
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return textWrap{"{", list, "}"}
|
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{Prefix: "{", Value: list, Suffix: "}"}
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
return textNil
|
return textNil
|
||||||
}
|
}
|
||||||
if opts.PrintAddresses {
|
|
||||||
ptr = formatPointer(v)
|
|
||||||
}
|
|
||||||
fallthrough
|
fallthrough
|
||||||
case reflect.Array:
|
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
|
var list textList
|
||||||
for i := 0; i < v.Len(); i++ {
|
for i := 0; i < v.Len(); i++ {
|
||||||
vi := v.Index(i)
|
if len(list) == maxLen {
|
||||||
if vi.CanAddr() { // Check for cyclic elements
|
list.AppendEllipsis(diffStats{})
|
||||||
p := vi.Addr()
|
break
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs)
|
||||||
s := opts.WithTypeMode(elideType).FormatValue(vi, m)
|
|
||||||
list = append(list, textRecord{Value: s})
|
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:
|
case reflect.Map:
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
return textNil
|
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
|
var list textList
|
||||||
for _, k := range value.SortKeys(v.MapKeys()) {
|
for _, k := range value.SortKeys(v.MapKeys()) {
|
||||||
sk := formatMapKey(k)
|
if len(list) == maxLen {
|
||||||
sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
|
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})
|
list = append(list, textRecord{Key: sk, Value: sv})
|
||||||
}
|
}
|
||||||
if opts.PrintAddresses {
|
|
||||||
ptr = formatPointer(v)
|
out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||||
}
|
out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
|
||||||
return textWrap{ptr + "{", list, "}"}
|
return out
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
return textNil
|
return textNil
|
||||||
}
|
}
|
||||||
if m.Visit(v) || opts.ShallowPointers {
|
|
||||||
return textLine(formatPointer(v))
|
// Check pointer for cycles.
|
||||||
}
|
ptrRef, visited := ptrs.Push(v)
|
||||||
if opts.PrintAddresses {
|
if visited {
|
||||||
ptr = formatPointer(v)
|
out = makeLeafReference(ptrRef, opts.PrintAddresses)
|
||||||
|
return &textWrap{Prefix: "&", Value: out}
|
||||||
}
|
}
|
||||||
|
defer ptrs.Pop()
|
||||||
|
|
||||||
skipType = true // Let the underlying value print the type instead
|
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:
|
case reflect.Interface:
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
return textNil
|
return textNil
|
||||||
|
@ -197,7 +289,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out t
|
||||||
// Interfaces accept different concrete types,
|
// Interfaces accept different concrete types,
|
||||||
// so configure the underlying value to explicitly print the type.
|
// so configure the underlying value to explicitly print the type.
|
||||||
skipType = true // Print the concrete type instead
|
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:
|
default:
|
||||||
panic(fmt.Sprintf("%v kind not handled", v.Kind()))
|
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.
|
// formatMapKey formats v as if it were a map key.
|
||||||
// The result is guaranteed to be a single line.
|
// 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
|
var opts formatOptions
|
||||||
|
opts.DiffMode = diffIdentical
|
||||||
opts.TypeMode = elideType
|
opts.TypeMode = elideType
|
||||||
opts.ShallowPointers = true
|
opts.PrintAddresses = disambiguate
|
||||||
s := opts.FormatValue(v, visitedPointers{}).String()
|
opts.AvoidStringer = disambiguate
|
||||||
|
opts.QualifiedNames = disambiguate
|
||||||
|
s := opts.FormatValue(v, reflect.Map, ptrs).String()
|
||||||
return strings.TrimSpace(s)
|
return strings.TrimSpace(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +322,7 @@ func formatString(s string) string {
|
||||||
rawInvalid := func(r rune) bool {
|
rawInvalid := func(r rune) bool {
|
||||||
return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
|
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 "`" + s + "`"
|
||||||
}
|
}
|
||||||
return qs
|
return qs
|
||||||
|
@ -256,23 +351,3 @@ func formatHex(u uint64) string {
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(f, u)
|
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
|
|
||||||
}
|
|
||||||
|
|
133
vendor/github.com/google/go-cmp/cmp/report_slices.go
generated
vendored
133
vendor/github.com/google/go-cmp/cmp/report_slices.go
generated
vendored
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
@ -23,11 +24,25 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
|
||||||
return false // Must be formatting in diff mode
|
return false // Must be formatting in diff mode
|
||||||
case v.NumDiff == 0:
|
case v.NumDiff == 0:
|
||||||
return false // No differences detected
|
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():
|
case !v.ValueX.IsValid() || !v.ValueY.IsValid():
|
||||||
return false // Both values must be valid
|
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() {
|
switch t := v.Type; t.Kind() {
|
||||||
|
@ -82,7 +97,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
||||||
}
|
}
|
||||||
if isText || isBinary {
|
if isText || isBinary {
|
||||||
var numLines, lastLineIdx, maxLineLen int
|
var numLines, lastLineIdx, maxLineLen int
|
||||||
isBinary = false
|
isBinary = !utf8.ValidString(sx) || !utf8.ValidString(sy)
|
||||||
for i, r := range sx + sy {
|
for i, r := range sx + sy {
|
||||||
if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError {
|
if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError {
|
||||||
isBinary = true
|
isBinary = true
|
||||||
|
@ -97,7 +112,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isText = !isBinary
|
isText = !isBinary
|
||||||
isLinedText = isText && numLines >= 4 && maxLineLen <= 256
|
isLinedText = isText && numLines >= 4 && maxLineLen <= 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format the string into printable records.
|
// Format the string into printable records.
|
||||||
|
@ -117,6 +132,83 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
delim = "\n"
|
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,
|
// If the text appears to be single-lined text,
|
||||||
// then perform differencing in approximately fixed-sized chunks.
|
// then perform differencing in approximately fixed-sized chunks.
|
||||||
// The output is printed as quoted strings.
|
// The output is printed as quoted strings.
|
||||||
|
@ -129,6 +221,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
delim = ""
|
delim = ""
|
||||||
|
|
||||||
// If the text appears to be binary data,
|
// If the text appears to be binary data,
|
||||||
// then perform differencing in approximately fixed-sized chunks.
|
// then perform differencing in approximately fixed-sized chunks.
|
||||||
// The output is inspired by hexdump.
|
// 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}
|
return textRecord{Diff: d, Value: textLine(s), Comment: comment}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// For all other slices of primitive types,
|
// For all other slices of primitive types,
|
||||||
// then perform differencing in approximately fixed-sized chunks.
|
// then perform differencing in approximately fixed-sized chunks.
|
||||||
// The size of each chunk depends on the width of the element kind.
|
// 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() {
|
switch t.Elem().Kind() {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
ss = append(ss, fmt.Sprint(v.Index(i).Int()))
|
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()))
|
ss = append(ss, formatHex(v.Index(i).Uint()))
|
||||||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
|
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
|
||||||
ss = append(ss, fmt.Sprint(v.Index(i).Interface()))
|
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.
|
// Wrap the output with appropriate type information.
|
||||||
var out textNode = textWrap{"{", list, "}"}
|
var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
|
||||||
if !isText {
|
if !isText {
|
||||||
// The "{...}" byte-sequence literal is not valid Go syntax for strings.
|
// The "{...}" byte-sequence literal is not valid Go syntax for strings.
|
||||||
// Emit the type for extra clarity (e.g. "string{...}").
|
// Emit the type for extra clarity (e.g. "string{...}").
|
||||||
|
@ -196,12 +292,12 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
|
||||||
}
|
}
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.String:
|
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("")) {
|
if t != reflect.TypeOf(string("")) {
|
||||||
out = opts.FormatType(t, out)
|
out = opts.FormatType(t, out)
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
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)) {
|
if t != reflect.TypeOf([]byte(nil)) {
|
||||||
out = opts.FormatType(t, out)
|
out = opts.FormatType(t, out)
|
||||||
}
|
}
|
||||||
|
@ -242,9 +338,22 @@ func (opts formatOptions) formatDiffSlice(
|
||||||
return n0 - v.Len()
|
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 := coalesceAdjacentEdits(name, es)
|
||||||
groups = coalesceInterveningIdentical(groups, chunkSize/4)
|
groups = coalesceInterveningIdentical(groups, chunkSize/4)
|
||||||
|
maxGroup := diffStats{Name: name}
|
||||||
for i, ds := range groups {
|
for i, ds := range groups {
|
||||||
|
if maxLen >= 0 && numDiffs >= maxLen {
|
||||||
|
maxGroup = maxGroup.Append(ds)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Print equal.
|
// Print equal.
|
||||||
if ds.NumDiff() == 0 {
|
if ds.NumDiff() == 0 {
|
||||||
// Compute the number of leading and trailing equal bytes to print.
|
// Compute the number of leading and trailing equal bytes to print.
|
||||||
|
@ -273,12 +382,18 @@ func (opts formatOptions) formatDiffSlice(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print unequal.
|
// Print unequal.
|
||||||
|
len0 := len(list)
|
||||||
nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved)
|
nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved)
|
||||||
vx = vx.Slice(nx, vx.Len())
|
vx = vx.Slice(nx, vx.Len())
|
||||||
ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted)
|
ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted)
|
||||||
vy = vy.Slice(ny, vy.Len())
|
vy = vy.Slice(ny, vy.Len())
|
||||||
|
numDiffs += len(list) - len0
|
||||||
}
|
}
|
||||||
|
if maxGroup.IsZero() {
|
||||||
assert(vx.Len() == 0 && vy.Len() == 0)
|
assert(vx.Len() == 0 && vy.Len() == 0)
|
||||||
|
} else {
|
||||||
|
list.AppendEllipsis(maxGroup)
|
||||||
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
72
vendor/github.com/google/go-cmp/cmp/report_text.go
generated
vendored
72
vendor/github.com/google/go-cmp/cmp/report_text.go
generated
vendored
|
@ -10,12 +10,15 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp/internal/flags"
|
"github.com/google/go-cmp/cmp/internal/flags"
|
||||||
)
|
)
|
||||||
|
|
||||||
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
|
var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
|
||||||
|
|
||||||
|
const maxColumnLength = 80
|
||||||
|
|
||||||
type indentMode int
|
type indentMode int
|
||||||
|
|
||||||
func (n indentMode) appendIndent(b []byte, d diffMode) []byte {
|
func (n indentMode) appendIndent(b []byte, d diffMode) []byte {
|
||||||
|
@ -94,18 +97,19 @@ type textWrap struct {
|
||||||
Prefix string // e.g., "bytes.Buffer{"
|
Prefix string // e.g., "bytes.Buffer{"
|
||||||
Value textNode // textWrap | textList | textLine
|
Value textNode // textWrap | textList | textLine
|
||||||
Suffix string // e.g., "}"
|
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)
|
return len(s.Prefix) + s.Value.Len() + len(s.Suffix)
|
||||||
}
|
}
|
||||||
func (s1 textWrap) Equal(s2 textNode) bool {
|
func (s1 *textWrap) Equal(s2 textNode) bool {
|
||||||
if s2, ok := s2.(textWrap); ok {
|
if s2, ok := s2.(*textWrap); ok {
|
||||||
return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix
|
return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
func (s textWrap) String() string {
|
func (s *textWrap) String() string {
|
||||||
var d diffMode
|
var d diffMode
|
||||||
var n indentMode
|
var n indentMode
|
||||||
_, s2 := s.formatCompactTo(nil, d)
|
_, s2 := s.formatCompactTo(nil, d)
|
||||||
|
@ -114,7 +118,7 @@ func (s textWrap) String() string {
|
||||||
b = append(b, '\n') // Trailing newline
|
b = append(b, '\n') // Trailing newline
|
||||||
return string(b)
|
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
|
n0 := len(b) // Original buffer length
|
||||||
b = append(b, s.Prefix...)
|
b = append(b, s.Prefix...)
|
||||||
b, s.Value = s.Value.formatCompactTo(b, d)
|
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
|
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 = append(b, s.Prefix...)
|
||||||
b = s.Value.formatExpandedTo(b, d, n)
|
b = s.Value.formatExpandedTo(b, d, n)
|
||||||
b = append(b, s.Suffix...)
|
b = append(b, s.Suffix...)
|
||||||
|
@ -139,6 +143,7 @@ type textRecord struct {
|
||||||
Diff diffMode // e.g., 0 or '-' or '+'
|
Diff diffMode // e.g., 0 or '-' or '+'
|
||||||
Key string // e.g., "MyField"
|
Key string // e.g., "MyField"
|
||||||
Value textNode // textWrap | textLine
|
Value textNode // textWrap | textLine
|
||||||
|
ElideComma bool // avoid trailing comma
|
||||||
Comment fmt.Stringer // e.g., "6 identical fields"
|
Comment fmt.Stringer // e.g., "6 identical fields"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +151,12 @@ type textRecord struct {
|
||||||
// exists at the end. If cs is non-zero it coalesces the statistics with the
|
// exists at the end. If cs is non-zero it coalesces the statistics with the
|
||||||
// previous diffStats.
|
// previous diffStats.
|
||||||
func (s *textList) AppendEllipsis(ds 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 len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) {
|
||||||
if hasStats {
|
if hasStats {
|
||||||
*s = append(*s, textRecord{Value: textEllipsis, Comment: ds})
|
*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds})
|
||||||
} else {
|
} else {
|
||||||
*s = append(*s, textRecord{Value: textEllipsis})
|
*s = append(*s, textRecord{Value: textEllipsis, ElideComma: true})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -191,7 +196,7 @@ func (s1 textList) Equal(s2 textNode) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s textList) String() string {
|
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) {
|
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
|
// Force multi-lined output when printing a removed/inserted node that
|
||||||
// is sufficiently long.
|
// is sufficiently long.
|
||||||
if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > 80 {
|
if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength {
|
||||||
multiLine = true
|
multiLine = true
|
||||||
}
|
}
|
||||||
if !multiLine {
|
if !multiLine {
|
||||||
|
@ -236,16 +241,50 @@ func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
|
||||||
_, isLine := r.Value.(textLine)
|
_, isLine := r.Value.(textLine)
|
||||||
return r.Key == "" || !isLine
|
return r.Key == "" || !isLine
|
||||||
},
|
},
|
||||||
func(r textRecord) int { return len(r.Key) },
|
func(r textRecord) int { return utf8.RuneCountInString(r.Key) },
|
||||||
)
|
)
|
||||||
alignValueLens := s.alignLens(
|
alignValueLens := s.alignLens(
|
||||||
func(r textRecord) bool {
|
func(r textRecord) bool {
|
||||||
_, isLine := r.Value.(textLine)
|
_, isLine := r.Value.(textLine)
|
||||||
return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil
|
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.
|
// Format the list as a multi-lined output.
|
||||||
n++
|
n++
|
||||||
for i, r := range s {
|
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 = alignKeyLens[i].appendChar(b, ' ')
|
||||||
|
|
||||||
b = r.Value.formatExpandedTo(b, d|r.Diff, n)
|
b = r.Value.formatExpandedTo(b, d|r.Diff, n)
|
||||||
if !r.Value.Equal(textEllipsis) {
|
if !r.ElideComma {
|
||||||
b = append(b, ',')
|
b = append(b, ',')
|
||||||
}
|
}
|
||||||
b = alignValueLens[i].appendChar(b, ' ')
|
b = alignValueLens[i].appendChar(b, ' ')
|
||||||
|
@ -332,6 +371,11 @@ type diffStats struct {
|
||||||
NumModified int
|
NumModified int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s diffStats) IsZero() bool {
|
||||||
|
s.Name = ""
|
||||||
|
return s == diffStats{}
|
||||||
|
}
|
||||||
|
|
||||||
func (s diffStats) NumDiff() int {
|
func (s diffStats) NumDiff() int {
|
||||||
return s.NumRemoved + s.NumInserted + s.NumModified
|
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
|
# github.com/golang/snappy v0.0.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/golang/snappy
|
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
|
||||||
github.com/google/go-cmp/cmp/internal/diff
|
github.com/google/go-cmp/cmp/internal/diff
|
||||||
github.com/google/go-cmp/cmp/internal/flags
|
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/golang-lru/simplelru
|
||||||
# github.com/hashicorp/serf v0.9.0
|
# github.com/hashicorp/serf v0.9.0
|
||||||
github.com/hashicorp/serf/coordinate
|
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
|
# github.com/influxdata/influxdb v1.8.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/influxdata/influxdb/client/v2
|
github.com/influxdata/influxdb/client/v2
|
||||||
|
|
Loading…
Reference in a new issue