mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-24 21:24:05 -08:00
Add DigitalOcean service discovery (#7407)
Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>
This commit is contained in:
parent
7b4f81b397
commit
c61141ce51
|
@ -18,6 +18,7 @@ import (
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/discovery/azure"
|
"github.com/prometheus/prometheus/discovery/azure"
|
||||||
"github.com/prometheus/prometheus/discovery/consul"
|
"github.com/prometheus/prometheus/discovery/consul"
|
||||||
|
"github.com/prometheus/prometheus/discovery/digitalocean"
|
||||||
"github.com/prometheus/prometheus/discovery/dns"
|
"github.com/prometheus/prometheus/discovery/dns"
|
||||||
"github.com/prometheus/prometheus/discovery/ec2"
|
"github.com/prometheus/prometheus/discovery/ec2"
|
||||||
"github.com/prometheus/prometheus/discovery/file"
|
"github.com/prometheus/prometheus/discovery/file"
|
||||||
|
@ -40,6 +41,8 @@ type ServiceDiscoveryConfig struct {
|
||||||
FileSDConfigs []*file.SDConfig `yaml:"file_sd_configs,omitempty"`
|
FileSDConfigs []*file.SDConfig `yaml:"file_sd_configs,omitempty"`
|
||||||
// List of Consul service discovery configurations.
|
// List of Consul service discovery configurations.
|
||||||
ConsulSDConfigs []*consul.SDConfig `yaml:"consul_sd_configs,omitempty"`
|
ConsulSDConfigs []*consul.SDConfig `yaml:"consul_sd_configs,omitempty"`
|
||||||
|
// List of DigitalOcean service discovery configurations.
|
||||||
|
DigitalOceanSDConfigs []*digitalocean.SDConfig `yaml:"digitalocean_sd_configs,omitempty"`
|
||||||
// List of Serverset service discovery configurations.
|
// List of Serverset service discovery configurations.
|
||||||
ServersetSDConfigs []*zookeeper.ServersetSDConfig `yaml:"serverset_sd_configs,omitempty"`
|
ServersetSDConfigs []*zookeeper.ServersetSDConfig `yaml:"serverset_sd_configs,omitempty"`
|
||||||
// NerveSDConfigs is a list of Nerve service discovery configurations.
|
// NerveSDConfigs is a list of Nerve service discovery configurations.
|
||||||
|
@ -72,6 +75,11 @@ func (c *ServiceDiscoveryConfig) Validate() error {
|
||||||
return errors.New("empty or null section in consul_sd_configs")
|
return errors.New("empty or null section in consul_sd_configs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, cfg := range c.DigitalOceanSDConfigs {
|
||||||
|
if cfg == nil {
|
||||||
|
return errors.New("empty or null section in digitalocean_sd_configs")
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, cfg := range c.DNSSDConfigs {
|
for _, cfg := range c.DNSSDConfigs {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return errors.New("empty or null section in dns_sd_configs")
|
return errors.New("empty or null section in dns_sd_configs")
|
||||||
|
|
193
discovery/digitalocean/digitalocean.go
Normal file
193
discovery/digitalocean/digitalocean.go
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
// 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 digitalocean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/digitalocean/godo"
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
|
config_util "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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
doLabel = model.MetaLabelPrefix + "digitalocean_"
|
||||||
|
doLabelID = doLabel + "droplet_id"
|
||||||
|
doLabelName = doLabel + "droplet_name"
|
||||||
|
doLabelImage = doLabel + "image"
|
||||||
|
doLabelPrivateIPv4 = doLabel + "private_ipv4"
|
||||||
|
doLabelPublicIPv4 = doLabel + "public_ipv4"
|
||||||
|
doLabelPublicIPv6 = doLabel + "public_ipv6"
|
||||||
|
doLabelRegion = doLabel + "region"
|
||||||
|
doLabelSize = doLabel + "size"
|
||||||
|
doLabelStatus = doLabel + "status"
|
||||||
|
doLabelFeatures = doLabel + "features"
|
||||||
|
doLabelTags = doLabel + "tags"
|
||||||
|
separator = ","
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultSDConfig is the default DigitalOcean SD configuration.
|
||||||
|
var DefaultSDConfig = SDConfig{
|
||||||
|
Port: 80,
|
||||||
|
RefreshInterval: model.Duration(60 * time.Second),
|
||||||
|
}
|
||||||
|
|
||||||
|
// SDConfig is the configuration for DigitalOcean based service discovery.
|
||||||
|
type SDConfig struct {
|
||||||
|
HTTPClientConfig config_util.HTTPClientConfig `yaml:",inline"`
|
||||||
|
|
||||||
|
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery periodically performs DigitalOcean requests. It implements
|
||||||
|
// the Discoverer interface.
|
||||||
|
type Discovery struct {
|
||||||
|
*refresh.Discovery
|
||||||
|
client *godo.Client
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDiscovery returns a new Discovery which periodically refreshes its targets.
|
||||||
|
func NewDiscovery(conf *SDConfig, logger log.Logger) (*Discovery, error) {
|
||||||
|
d := &Discovery{
|
||||||
|
port: conf.Port,
|
||||||
|
}
|
||||||
|
|
||||||
|
rt, err := config_util.NewRoundTripperFromConfig(conf.HTTPClientConfig, "digitalocean_sd", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.client, err = godo.New(
|
||||||
|
&http.Client{
|
||||||
|
Transport: rt,
|
||||||
|
Timeout: 5 * time.Duration(conf.RefreshInterval),
|
||||||
|
},
|
||||||
|
godo.SetUserAgent(fmt.Sprintf("Prometheus/%s", version.Version)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error setting up digital ocean agent: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Discovery = refresh.NewDiscovery(
|
||||||
|
logger,
|
||||||
|
"digitalocean",
|
||||||
|
time.Duration(conf.RefreshInterval),
|
||||||
|
d.refresh,
|
||||||
|
)
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
|
||||||
|
tg := &targetgroup.Group{
|
||||||
|
Source: "DigitalOcean",
|
||||||
|
}
|
||||||
|
|
||||||
|
droplets, err := d.listDroplets()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, droplet := range droplets {
|
||||||
|
if droplet.Networks == nil || len(droplet.Networks.V4) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
privateIPv4, err := droplet.PrivateIPv4()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error while reading private IPv4 of droplet %d: %w", droplet.ID, err)
|
||||||
|
}
|
||||||
|
publicIPv4, err := droplet.PublicIPv4()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error while reading public IPv4 of droplet %d: %w", droplet.ID, err)
|
||||||
|
}
|
||||||
|
publicIPv6, err := droplet.PublicIPv6()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error while reading public IPv6 of droplet %d: %w", droplet.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := model.LabelSet{
|
||||||
|
doLabelID: model.LabelValue(fmt.Sprintf("%d", droplet.ID)),
|
||||||
|
doLabelName: model.LabelValue(droplet.Name),
|
||||||
|
doLabelImage: model.LabelValue(droplet.Image.Slug),
|
||||||
|
doLabelPrivateIPv4: model.LabelValue(privateIPv4),
|
||||||
|
doLabelPublicIPv4: model.LabelValue(publicIPv4),
|
||||||
|
doLabelPublicIPv6: model.LabelValue(publicIPv6),
|
||||||
|
doLabelRegion: model.LabelValue(droplet.Region.Slug),
|
||||||
|
doLabelSize: model.LabelValue(droplet.SizeSlug),
|
||||||
|
doLabelStatus: model.LabelValue(droplet.Status),
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := net.JoinHostPort(publicIPv4, strconv.FormatUint(uint64(d.port), 10))
|
||||||
|
labels[model.AddressLabel] = model.LabelValue(addr)
|
||||||
|
|
||||||
|
if len(droplet.Features) > 0 {
|
||||||
|
// We surround the separated list with the separator as well. This way regular expressions
|
||||||
|
// in relabeling rules don't have to consider feature positions.
|
||||||
|
features := separator + strings.Join(droplet.Features, separator) + separator
|
||||||
|
labels[doLabelFeatures] = model.LabelValue(features)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(droplet.Tags) > 0 {
|
||||||
|
// We surround the separated list with the separator as well. This way regular expressions
|
||||||
|
// in relabeling rules don't have to consider tag positions.
|
||||||
|
tags := separator + strings.Join(droplet.Tags, separator) + separator
|
||||||
|
labels[doLabelTags] = model.LabelValue(tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
tg.Targets = append(tg.Targets, labels)
|
||||||
|
}
|
||||||
|
return []*targetgroup.Group{tg}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Discovery) listDroplets() ([]godo.Droplet, error) {
|
||||||
|
var (
|
||||||
|
droplets []godo.Droplet
|
||||||
|
opts = &godo.ListOptions{Page: 1}
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
paginatedDroplets, resp, err := d.client.Droplets.List(context.Background(), opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error while listing droplets page %d: %w", opts.Page, err)
|
||||||
|
}
|
||||||
|
droplets = append(droplets, paginatedDroplets...)
|
||||||
|
if resp.Links == nil || resp.Links.IsLastPage() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
opts.Page++
|
||||||
|
}
|
||||||
|
return droplets, nil
|
||||||
|
}
|
125
discovery/digitalocean/digitalocean_test.go
Normal file
125
discovery/digitalocean/digitalocean_test.go
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// 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 digitalocean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
"github.com/prometheus/prometheus/util/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DigitalOceanSDTestSuite struct {
|
||||||
|
Mock *SDMock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DigitalOceanSDTestSuite) TearDownSuite() {
|
||||||
|
s.Mock.ShutdownServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DigitalOceanSDTestSuite) SetupTest(t *testing.T) {
|
||||||
|
s.Mock = NewSDMock(t)
|
||||||
|
s.Mock.Setup()
|
||||||
|
|
||||||
|
s.Mock.HandleDropletsList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDigitalOceanSDRefresh(t *testing.T) {
|
||||||
|
sdmock := &DigitalOceanSDTestSuite{}
|
||||||
|
sdmock.SetupTest(t)
|
||||||
|
t.Cleanup(sdmock.TearDownSuite)
|
||||||
|
|
||||||
|
cfg := DefaultSDConfig
|
||||||
|
cfg.HTTPClientConfig.BearerToken = tokenID
|
||||||
|
d, err := NewDiscovery(&cfg, log.NewNopLogger())
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
endpoint, err := url.Parse(sdmock.Mock.Endpoint())
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
d.client.BaseURL = endpoint
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
tgs, err := d.refresh(ctx)
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
|
||||||
|
testutil.Equals(t, 1, len(tgs))
|
||||||
|
|
||||||
|
tg := tgs[0]
|
||||||
|
testutil.Assert(t, tg != nil, "tg should not be nil")
|
||||||
|
testutil.Assert(t, tg.Targets != nil, "tg.targets should not be nil")
|
||||||
|
testutil.Equals(t, 4, len(tg.Targets))
|
||||||
|
|
||||||
|
for i, lbls := range []model.LabelSet{
|
||||||
|
{
|
||||||
|
"__address__": model.LabelValue("104.236.32.182:80"),
|
||||||
|
"__meta_digitalocean_droplet_id": model.LabelValue("3164444"),
|
||||||
|
"__meta_digitalocean_droplet_name": model.LabelValue("example.com"),
|
||||||
|
"__meta_digitalocean_image": model.LabelValue("ubuntu-16-04-x64"),
|
||||||
|
"__meta_digitalocean_private_ipv4": model.LabelValue(""),
|
||||||
|
"__meta_digitalocean_public_ipv4": model.LabelValue("104.236.32.182"),
|
||||||
|
"__meta_digitalocean_public_ipv6": model.LabelValue("2604:A880:0800:0010:0000:0000:02DD:4001"),
|
||||||
|
"__meta_digitalocean_region": model.LabelValue("nyc3"),
|
||||||
|
"__meta_digitalocean_size": model.LabelValue("s-1vcpu-1gb"),
|
||||||
|
"__meta_digitalocean_status": model.LabelValue("active"),
|
||||||
|
"__meta_digitalocean_features": model.LabelValue(",backups,ipv6,virtio,"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__address__": model.LabelValue("104.131.186.241:80"),
|
||||||
|
"__meta_digitalocean_droplet_id": model.LabelValue("3164494"),
|
||||||
|
"__meta_digitalocean_droplet_name": model.LabelValue("prometheus"),
|
||||||
|
"__meta_digitalocean_image": model.LabelValue("ubuntu-16-04-x64"),
|
||||||
|
"__meta_digitalocean_private_ipv4": model.LabelValue(""),
|
||||||
|
"__meta_digitalocean_public_ipv4": model.LabelValue("104.131.186.241"),
|
||||||
|
"__meta_digitalocean_public_ipv6": model.LabelValue(""),
|
||||||
|
"__meta_digitalocean_region": model.LabelValue("nyc3"),
|
||||||
|
"__meta_digitalocean_size": model.LabelValue("s-1vcpu-1gb"),
|
||||||
|
"__meta_digitalocean_status": model.LabelValue("active"),
|
||||||
|
"__meta_digitalocean_tags": model.LabelValue(",monitor,"),
|
||||||
|
"__meta_digitalocean_features": model.LabelValue(",virtio,"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__address__": model.LabelValue("167.172.111.118:80"),
|
||||||
|
"__meta_digitalocean_droplet_id": model.LabelValue("175072239"),
|
||||||
|
"__meta_digitalocean_droplet_name": model.LabelValue("prometheus-demo-old"),
|
||||||
|
"__meta_digitalocean_image": model.LabelValue("ubuntu-18-04-x64"),
|
||||||
|
"__meta_digitalocean_private_ipv4": model.LabelValue("10.135.64.211"),
|
||||||
|
"__meta_digitalocean_public_ipv4": model.LabelValue("167.172.111.118"),
|
||||||
|
"__meta_digitalocean_public_ipv6": model.LabelValue(""),
|
||||||
|
"__meta_digitalocean_region": model.LabelValue("fra1"),
|
||||||
|
"__meta_digitalocean_size": model.LabelValue("s-1vcpu-1gb"),
|
||||||
|
"__meta_digitalocean_status": model.LabelValue("off"),
|
||||||
|
"__meta_digitalocean_features": model.LabelValue(",ipv6,private_networking,"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__address__": model.LabelValue("138.65.56.69:80"),
|
||||||
|
"__meta_digitalocean_droplet_id": model.LabelValue("176011507"),
|
||||||
|
"__meta_digitalocean_droplet_name": model.LabelValue("prometheus-demo"),
|
||||||
|
"__meta_digitalocean_image": model.LabelValue("ubuntu-18-04-x64"),
|
||||||
|
"__meta_digitalocean_private_ipv4": model.LabelValue("10.135.64.212"),
|
||||||
|
"__meta_digitalocean_public_ipv4": model.LabelValue("138.65.56.69"),
|
||||||
|
"__meta_digitalocean_public_ipv6": model.LabelValue("2a03:b0c0:3:f0::cf2:4"),
|
||||||
|
"__meta_digitalocean_region": model.LabelValue("fra1"),
|
||||||
|
"__meta_digitalocean_size": model.LabelValue("s-1vcpu-1gb"),
|
||||||
|
"__meta_digitalocean_status": model.LabelValue("active"),
|
||||||
|
"__meta_digitalocean_features": model.LabelValue(",ipv6,private_networking,"),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
|
||||||
|
testutil.Equals(t, lbls, tg.Targets[i])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
644
discovery/digitalocean/mock_test.go
Normal file
644
discovery/digitalocean/mock_test.go
Normal file
|
@ -0,0 +1,644 @@
|
||||||
|
// 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 digitalocean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SDMock is the interface for the DigitalOcean 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShutdownServer creates the mock server
|
||||||
|
func (m *SDMock) ShutdownServer() {
|
||||||
|
m.Server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenID = "3c9a75a2-24fd-4508-b4f2-11f18aa97411"
|
||||||
|
|
||||||
|
// HandleDropletsList mocks droplet list.
|
||||||
|
func (m *SDMock) HandleDropletsList() {
|
||||||
|
m.Mux.HandleFunc("/v2/droplets", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Header.Get("Authorization") != fmt.Sprintf("Bearer %s", tokenID) {
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Add("content-type", "application/json; charset=utf-8")
|
||||||
|
w.Header().Add("ratelimit-limit", "1200")
|
||||||
|
w.Header().Add("ratelimit-remaining", "965")
|
||||||
|
w.Header().Add("ratelimit-reset", "1415984218")
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
|
||||||
|
page := 1
|
||||||
|
if pageQuery, ok := r.URL.Query()["page"]; ok {
|
||||||
|
var err error
|
||||||
|
page, err = strconv.Atoi(pageQuery[0])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprint(w, []string{`
|
||||||
|
{
|
||||||
|
"droplets": [
|
||||||
|
{
|
||||||
|
"id": 3164444,
|
||||||
|
"name": "example.com",
|
||||||
|
"memory": 1024,
|
||||||
|
"vcpus": 1,
|
||||||
|
"disk": 25,
|
||||||
|
"locked": false,
|
||||||
|
"status": "active",
|
||||||
|
"kernel": {
|
||||||
|
"id": 2233,
|
||||||
|
"name": "Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic",
|
||||||
|
"version": "3.13.0-37-generic"
|
||||||
|
},
|
||||||
|
"created_at": "2014-11-14T16:29:21Z",
|
||||||
|
"features": [
|
||||||
|
"backups",
|
||||||
|
"ipv6",
|
||||||
|
"virtio"
|
||||||
|
],
|
||||||
|
"backup_ids": [
|
||||||
|
7938002
|
||||||
|
],
|
||||||
|
"snapshot_ids": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"image": {
|
||||||
|
"id": 6918990,
|
||||||
|
"name": "14.04 x64",
|
||||||
|
"distribution": "Ubuntu",
|
||||||
|
"slug": "ubuntu-16-04-x64",
|
||||||
|
"public": true,
|
||||||
|
"regions": [
|
||||||
|
"nyc1",
|
||||||
|
"ams1",
|
||||||
|
"sfo1",
|
||||||
|
"nyc2",
|
||||||
|
"ams2",
|
||||||
|
"sgp1",
|
||||||
|
"lon1",
|
||||||
|
"nyc3",
|
||||||
|
"ams3",
|
||||||
|
"nyc3"
|
||||||
|
],
|
||||||
|
"created_at": "2014-10-17T20:24:33Z",
|
||||||
|
"type": "snapshot",
|
||||||
|
"min_disk_size": 20,
|
||||||
|
"size_gigabytes": 2.34
|
||||||
|
},
|
||||||
|
"volume_ids": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
},
|
||||||
|
"size_slug": "s-1vcpu-1gb",
|
||||||
|
"networks": {
|
||||||
|
"v4": [
|
||||||
|
{
|
||||||
|
"ip_address": "104.236.32.182",
|
||||||
|
"netmask": "255.255.192.0",
|
||||||
|
"gateway": "104.236.0.1",
|
||||||
|
"type": "public"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"v6": [
|
||||||
|
{
|
||||||
|
"ip_address": "2604:A880:0800:0010:0000:0000:02DD:4001",
|
||||||
|
"netmask": 64,
|
||||||
|
"gateway": "2604:A880:0800:0010:0000:0000:0000:0001",
|
||||||
|
"type": "public"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"name": "New York 3",
|
||||||
|
"slug": "nyc3",
|
||||||
|
"sizes": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"features": [
|
||||||
|
"virtio",
|
||||||
|
"private_networking",
|
||||||
|
"backups",
|
||||||
|
"ipv6",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"available": null
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"vpc_uuid": "f9b0769c-e118-42fb-a0c4-fed15ef69662"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3164494,
|
||||||
|
"name": "prometheus",
|
||||||
|
"memory": 1024,
|
||||||
|
"vcpus": 1,
|
||||||
|
"disk": 25,
|
||||||
|
"locked": false,
|
||||||
|
"status": "active",
|
||||||
|
"kernel": {
|
||||||
|
"id": 2233,
|
||||||
|
"name": "Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic",
|
||||||
|
"version": "3.13.0-37-generic"
|
||||||
|
},
|
||||||
|
"created_at": "2014-11-14T16:36:31Z",
|
||||||
|
"features": [
|
||||||
|
"virtio"
|
||||||
|
],
|
||||||
|
"backup_ids": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"snapshot_ids": [
|
||||||
|
7938206
|
||||||
|
],
|
||||||
|
"image": {
|
||||||
|
"id": 6918990,
|
||||||
|
"name": "14.04 x64",
|
||||||
|
"distribution": "Ubuntu",
|
||||||
|
"slug": "ubuntu-16-04-x64",
|
||||||
|
"public": true,
|
||||||
|
"regions": [
|
||||||
|
"nyc1",
|
||||||
|
"ams1",
|
||||||
|
"sfo1",
|
||||||
|
"nyc2",
|
||||||
|
"ams2",
|
||||||
|
"sgp1",
|
||||||
|
"lon1",
|
||||||
|
"nyc3",
|
||||||
|
"ams3",
|
||||||
|
"nyc3"
|
||||||
|
],
|
||||||
|
"created_at": "2014-10-17T20:24:33Z",
|
||||||
|
"type": "snapshot",
|
||||||
|
"min_disk_size": 20,
|
||||||
|
"size_gigabytes": 2.34
|
||||||
|
},
|
||||||
|
"volume_ids": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
},
|
||||||
|
"size_slug": "s-1vcpu-1gb",
|
||||||
|
"networks": {
|
||||||
|
"v4": [
|
||||||
|
{
|
||||||
|
"ip_address": "104.131.186.241",
|
||||||
|
"netmask": "255.255.240.0",
|
||||||
|
"gateway": "104.131.176.1",
|
||||||
|
"type": "public"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"name": "New York 3",
|
||||||
|
"slug": "nyc3",
|
||||||
|
"sizes": [
|
||||||
|
"s-1vcpu-1gb",
|
||||||
|
"s-1vcpu-2gb",
|
||||||
|
"s-1vcpu-3gb",
|
||||||
|
"s-2vcpu-2gb",
|
||||||
|
"s-3vcpu-1gb",
|
||||||
|
"s-2vcpu-4gb",
|
||||||
|
"s-4vcpu-8gb",
|
||||||
|
"s-6vcpu-16gb",
|
||||||
|
"s-8vcpu-32gb",
|
||||||
|
"s-12vcpu-48gb",
|
||||||
|
"s-16vcpu-64gb",
|
||||||
|
"s-20vcpu-96gb",
|
||||||
|
"s-24vcpu-128gb",
|
||||||
|
"s-32vcpu-192gb"
|
||||||
|
],
|
||||||
|
"features": [
|
||||||
|
"virtio",
|
||||||
|
"private_networking",
|
||||||
|
"backups",
|
||||||
|
"ipv6",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"monitor"
|
||||||
|
],
|
||||||
|
"vpc_uuid": "f9b0769c-e118-42fb-a0c4-fed15ef69662"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": {
|
||||||
|
"pages": {
|
||||||
|
"next": "https://api.digitalocean.com/v2/droplets?page=2&per_page=2",
|
||||||
|
"last": "https://api.digitalocean.com/v2/droplets?page=2&per_page=2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"total": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
{
|
||||||
|
"droplets": [
|
||||||
|
{
|
||||||
|
"id": 175072239,
|
||||||
|
"name": "prometheus-demo-old",
|
||||||
|
"memory": 1024,
|
||||||
|
"vcpus": 1,
|
||||||
|
"disk": 25,
|
||||||
|
"locked": false,
|
||||||
|
"status": "off",
|
||||||
|
"kernel": null,
|
||||||
|
"created_at": "2020-01-10T16:47:39Z",
|
||||||
|
"features": [
|
||||||
|
"ipv6",
|
||||||
|
"private_networking"
|
||||||
|
],
|
||||||
|
"backup_ids": [],
|
||||||
|
"next_backup_window": null,
|
||||||
|
"snapshot_ids": [],
|
||||||
|
"image": {
|
||||||
|
"id": 53893572,
|
||||||
|
"name": "18.04.3 (LTS) x64",
|
||||||
|
"distribution": "Ubuntu",
|
||||||
|
"slug": "ubuntu-18-04-x64",
|
||||||
|
"public": true,
|
||||||
|
"regions": [
|
||||||
|
"nyc3",
|
||||||
|
"nyc1",
|
||||||
|
"sfo1",
|
||||||
|
"nyc2",
|
||||||
|
"ams2",
|
||||||
|
"sgp1",
|
||||||
|
"lon1",
|
||||||
|
"nyc3",
|
||||||
|
"ams3",
|
||||||
|
"fra1",
|
||||||
|
"tor1",
|
||||||
|
"sfo2",
|
||||||
|
"blr1",
|
||||||
|
"sfo3"
|
||||||
|
],
|
||||||
|
"created_at": "2019-10-22T01:38:19Z",
|
||||||
|
"min_disk_size": 20,
|
||||||
|
"type": "base",
|
||||||
|
"size_gigabytes": 2.36,
|
||||||
|
"description": "Ubuntu 18.04 x64 20191022",
|
||||||
|
"tags": [],
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"volume_ids": [],
|
||||||
|
"size": {
|
||||||
|
"slug": "s-1vcpu-1gb",
|
||||||
|
"memory": 1024,
|
||||||
|
"vcpus": 1,
|
||||||
|
"disk": 25,
|
||||||
|
"transfer": 1,
|
||||||
|
"price_monthly": 5,
|
||||||
|
"price_hourly": 0.00744,
|
||||||
|
"regions": [
|
||||||
|
"ams2",
|
||||||
|
"ams3",
|
||||||
|
"blr1",
|
||||||
|
"fra1",
|
||||||
|
"lon1",
|
||||||
|
"nyc1",
|
||||||
|
"nyc2",
|
||||||
|
"nyc3",
|
||||||
|
"sfo1",
|
||||||
|
"sfo2",
|
||||||
|
"sfo3",
|
||||||
|
"sgp1",
|
||||||
|
"tor1"
|
||||||
|
],
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
"size_slug": "s-1vcpu-1gb",
|
||||||
|
"networks": {
|
||||||
|
"v4": [
|
||||||
|
{
|
||||||
|
"ip_address": "167.172.111.118",
|
||||||
|
"netmask": "255.255.240.0",
|
||||||
|
"gateway": "167.172.176.1",
|
||||||
|
"type": "public"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip_address": "10.135.64.211",
|
||||||
|
"netmask": "255.255.0.0",
|
||||||
|
"gateway": "10.135.0.1",
|
||||||
|
"type": "private"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"v6": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"name": "Frankfurt 1",
|
||||||
|
"slug": "fra1",
|
||||||
|
"features": [
|
||||||
|
"private_networking",
|
||||||
|
"backups",
|
||||||
|
"ipv6",
|
||||||
|
"metadata",
|
||||||
|
"install_agent",
|
||||||
|
"storage",
|
||||||
|
"image_transfer"
|
||||||
|
],
|
||||||
|
"available": true,
|
||||||
|
"sizes": [
|
||||||
|
"s-1vcpu-1gb",
|
||||||
|
"512mb",
|
||||||
|
"s-1vcpu-2gb",
|
||||||
|
"1gb",
|
||||||
|
"s-3vcpu-1gb",
|
||||||
|
"s-2vcpu-2gb",
|
||||||
|
"s-1vcpu-3gb",
|
||||||
|
"s-2vcpu-4gb",
|
||||||
|
"2gb",
|
||||||
|
"s-4vcpu-8gb",
|
||||||
|
"m-1vcpu-8gb",
|
||||||
|
"c-2",
|
||||||
|
"4gb",
|
||||||
|
"g-2vcpu-8gb",
|
||||||
|
"gd-2vcpu-8gb",
|
||||||
|
"m-16gb",
|
||||||
|
"s-6vcpu-16gb",
|
||||||
|
"c-4",
|
||||||
|
"8gb",
|
||||||
|
"m-2vcpu-16gb",
|
||||||
|
"m3-2vcpu-16gb",
|
||||||
|
"g-4vcpu-16gb",
|
||||||
|
"gd-4vcpu-16gb",
|
||||||
|
"m6-2vcpu-16gb",
|
||||||
|
"m-32gb",
|
||||||
|
"s-8vcpu-32gb",
|
||||||
|
"c-8",
|
||||||
|
"16gb",
|
||||||
|
"m-4vcpu-32gb",
|
||||||
|
"m3-4vcpu-32gb",
|
||||||
|
"g-8vcpu-32gb",
|
||||||
|
"s-12vcpu-48gb",
|
||||||
|
"gd-8vcpu-32gb",
|
||||||
|
"m6-4vcpu-32gb",
|
||||||
|
"m-64gb",
|
||||||
|
"s-16vcpu-64gb",
|
||||||
|
"c-16",
|
||||||
|
"32gb",
|
||||||
|
"m-8vcpu-64gb",
|
||||||
|
"m3-8vcpu-64gb",
|
||||||
|
"g-16vcpu-64gb",
|
||||||
|
"s-20vcpu-96gb",
|
||||||
|
"48gb",
|
||||||
|
"gd-16vcpu-64gb",
|
||||||
|
"m6-8vcpu-64gb",
|
||||||
|
"m-128gb",
|
||||||
|
"s-24vcpu-128gb",
|
||||||
|
"c-32",
|
||||||
|
"64gb",
|
||||||
|
"m-16vcpu-128gb",
|
||||||
|
"m3-16vcpu-128gb",
|
||||||
|
"s-32vcpu-192gb",
|
||||||
|
"m-24vcpu-192gb",
|
||||||
|
"m-224gb",
|
||||||
|
"m6-16vcpu-128gb",
|
||||||
|
"m3-24vcpu-192gb",
|
||||||
|
"m6-24vcpu-192gb"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 176011507,
|
||||||
|
"name": "prometheus-demo",
|
||||||
|
"memory": 1024,
|
||||||
|
"vcpus": 1,
|
||||||
|
"disk": 25,
|
||||||
|
"locked": false,
|
||||||
|
"status": "active",
|
||||||
|
"kernel": null,
|
||||||
|
"created_at": "2020-01-17T12:06:26Z",
|
||||||
|
"features": [
|
||||||
|
"ipv6",
|
||||||
|
"private_networking"
|
||||||
|
],
|
||||||
|
"backup_ids": [],
|
||||||
|
"next_backup_window": null,
|
||||||
|
"snapshot_ids": [],
|
||||||
|
"image": {
|
||||||
|
"id": 53893572,
|
||||||
|
"name": "18.04.3 (LTS) x64",
|
||||||
|
"distribution": "Ubuntu",
|
||||||
|
"slug": "ubuntu-18-04-x64",
|
||||||
|
"public": true,
|
||||||
|
"regions": [
|
||||||
|
"nyc3",
|
||||||
|
"nyc1",
|
||||||
|
"sfo1",
|
||||||
|
"nyc2",
|
||||||
|
"ams2",
|
||||||
|
"sgp1",
|
||||||
|
"lon1",
|
||||||
|
"nyc3",
|
||||||
|
"ams3",
|
||||||
|
"fra1",
|
||||||
|
"tor1",
|
||||||
|
"sfo2",
|
||||||
|
"blr1",
|
||||||
|
"sfo3"
|
||||||
|
],
|
||||||
|
"created_at": "2019-10-22T01:38:19Z",
|
||||||
|
"min_disk_size": 20,
|
||||||
|
"type": "base",
|
||||||
|
"size_gigabytes": 2.36,
|
||||||
|
"description": "Ubuntu 18.04 x64 20191022",
|
||||||
|
"tags": [],
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"volume_ids": [],
|
||||||
|
"size": {
|
||||||
|
"slug": "s-1vcpu-1gb",
|
||||||
|
"memory": 1024,
|
||||||
|
"vcpus": 1,
|
||||||
|
"disk": 25,
|
||||||
|
"transfer": 1,
|
||||||
|
"price_monthly": 5,
|
||||||
|
"price_hourly": 0.00744,
|
||||||
|
"regions": [
|
||||||
|
"ams2",
|
||||||
|
"ams3",
|
||||||
|
"blr1",
|
||||||
|
"fra1",
|
||||||
|
"lon1",
|
||||||
|
"nyc1",
|
||||||
|
"nyc2",
|
||||||
|
"nyc3",
|
||||||
|
"sfo1",
|
||||||
|
"sfo2",
|
||||||
|
"sfo3",
|
||||||
|
"sgp1",
|
||||||
|
"tor1"
|
||||||
|
],
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
"size_slug": "s-1vcpu-1gb",
|
||||||
|
"networks": {
|
||||||
|
"v4": [
|
||||||
|
{
|
||||||
|
"ip_address": "138.65.56.69",
|
||||||
|
"netmask": "255.255.240.0",
|
||||||
|
"gateway": "138.65.64.1",
|
||||||
|
"type": "public"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip_address": "154.245.26.111",
|
||||||
|
"netmask": "255.255.252.0",
|
||||||
|
"gateway": "154.245.24.1",
|
||||||
|
"type": "public"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip_address": "10.135.64.212",
|
||||||
|
"netmask": "255.255.0.0",
|
||||||
|
"gateway": "10.135.0.1",
|
||||||
|
"type": "private"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"v6": [
|
||||||
|
{
|
||||||
|
"ip_address": "2a03:b0c0:3:f0::cf2:4",
|
||||||
|
"netmask": 64,
|
||||||
|
"gateway": "2a03:b0c0:3:f0::1",
|
||||||
|
"type": "public"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"name": "Frankfurt 1",
|
||||||
|
"slug": "fra1",
|
||||||
|
"features": [
|
||||||
|
"private_networking",
|
||||||
|
"backups",
|
||||||
|
"ipv6",
|
||||||
|
"metadata",
|
||||||
|
"install_agent",
|
||||||
|
"storage",
|
||||||
|
"image_transfer"
|
||||||
|
],
|
||||||
|
"available": true,
|
||||||
|
"sizes": [
|
||||||
|
"s-1vcpu-1gb",
|
||||||
|
"512mb",
|
||||||
|
"s-1vcpu-2gb",
|
||||||
|
"1gb",
|
||||||
|
"s-3vcpu-1gb",
|
||||||
|
"s-2vcpu-2gb",
|
||||||
|
"s-1vcpu-3gb",
|
||||||
|
"s-2vcpu-4gb",
|
||||||
|
"2gb",
|
||||||
|
"s-4vcpu-8gb",
|
||||||
|
"m-1vcpu-8gb",
|
||||||
|
"c-2",
|
||||||
|
"4gb",
|
||||||
|
"g-2vcpu-8gb",
|
||||||
|
"gd-2vcpu-8gb",
|
||||||
|
"m-16gb",
|
||||||
|
"s-6vcpu-16gb",
|
||||||
|
"c-4",
|
||||||
|
"8gb",
|
||||||
|
"m-2vcpu-16gb",
|
||||||
|
"m3-2vcpu-16gb",
|
||||||
|
"g-4vcpu-16gb",
|
||||||
|
"gd-4vcpu-16gb",
|
||||||
|
"m6-2vcpu-16gb",
|
||||||
|
"m-32gb",
|
||||||
|
"s-8vcpu-32gb",
|
||||||
|
"c-8",
|
||||||
|
"16gb",
|
||||||
|
"m-4vcpu-32gb",
|
||||||
|
"m3-4vcpu-32gb",
|
||||||
|
"g-8vcpu-32gb",
|
||||||
|
"s-12vcpu-48gb",
|
||||||
|
"gd-8vcpu-32gb",
|
||||||
|
"m6-4vcpu-32gb",
|
||||||
|
"m-64gb",
|
||||||
|
"s-16vcpu-64gb",
|
||||||
|
"c-16",
|
||||||
|
"32gb",
|
||||||
|
"m-8vcpu-64gb",
|
||||||
|
"m3-8vcpu-64gb",
|
||||||
|
"g-16vcpu-64gb",
|
||||||
|
"s-20vcpu-96gb",
|
||||||
|
"48gb",
|
||||||
|
"gd-16vcpu-64gb",
|
||||||
|
"m6-8vcpu-64gb",
|
||||||
|
"m-128gb",
|
||||||
|
"s-24vcpu-128gb",
|
||||||
|
"c-32",
|
||||||
|
"64gb",
|
||||||
|
"m-16vcpu-128gb",
|
||||||
|
"m3-16vcpu-128gb",
|
||||||
|
"s-32vcpu-192gb",
|
||||||
|
"m-24vcpu-192gb",
|
||||||
|
"m-224gb",
|
||||||
|
"m6-16vcpu-128gb",
|
||||||
|
"m3-24vcpu-192gb",
|
||||||
|
"m6-24vcpu-192gb"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": {
|
||||||
|
"pages": {
|
||||||
|
"first": "https://api.digitalocean.com/v2/droplets?page=1&per_page=2",
|
||||||
|
"prev": "https://api.digitalocean.com/v2/droplets?page=1&per_page=2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"total": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}[page-1],
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/discovery/azure"
|
"github.com/prometheus/prometheus/discovery/azure"
|
||||||
"github.com/prometheus/prometheus/discovery/consul"
|
"github.com/prometheus/prometheus/discovery/consul"
|
||||||
|
"github.com/prometheus/prometheus/discovery/digitalocean"
|
||||||
"github.com/prometheus/prometheus/discovery/dns"
|
"github.com/prometheus/prometheus/discovery/dns"
|
||||||
"github.com/prometheus/prometheus/discovery/ec2"
|
"github.com/prometheus/prometheus/discovery/ec2"
|
||||||
"github.com/prometheus/prometheus/discovery/file"
|
"github.com/prometheus/prometheus/discovery/file"
|
||||||
|
@ -369,6 +370,11 @@ func (m *Manager) registerProviders(cfg sd_config.ServiceDiscoveryConfig, setNam
|
||||||
return consul.NewDiscovery(c, log.With(m.logger, "discovery", "consul"))
|
return consul.NewDiscovery(c, log.With(m.logger, "discovery", "consul"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
for _, c := range cfg.DigitalOceanSDConfigs {
|
||||||
|
add(c, func() (Discoverer, error) {
|
||||||
|
return digitalocean.NewDiscovery(c, log.With(m.logger, "discovery", "digitalocean"))
|
||||||
|
})
|
||||||
|
}
|
||||||
for _, c := range cfg.MarathonSDConfigs {
|
for _, c := range cfg.MarathonSDConfigs {
|
||||||
add(c, func() (Discoverer, error) {
|
add(c, func() (Discoverer, error) {
|
||||||
return marathon.NewDiscovery(*c, log.With(m.logger, "discovery", "marathon"))
|
return marathon.NewDiscovery(*c, log.With(m.logger, "discovery", "marathon"))
|
||||||
|
|
|
@ -190,6 +190,10 @@ azure_sd_configs:
|
||||||
consul_sd_configs:
|
consul_sd_configs:
|
||||||
[ - <consul_sd_config> ... ]
|
[ - <consul_sd_config> ... ]
|
||||||
|
|
||||||
|
# List of DigitalOcean service discovery configurations.
|
||||||
|
digitalocean_sd_configs:
|
||||||
|
[ - <digitalocean_sd_config> ... ]
|
||||||
|
|
||||||
# List of DNS service discovery configurations.
|
# List of DNS service discovery configurations.
|
||||||
dns_sd_configs:
|
dns_sd_configs:
|
||||||
[ - <dns_sd_config> ... ]
|
[ - <dns_sd_config> ... ]
|
||||||
|
@ -386,6 +390,60 @@ users with thousands of services it can be more efficient to use the Consul API
|
||||||
directly which has basic support for filtering nodes (currently by node
|
directly which has basic support for filtering nodes (currently by node
|
||||||
metadata and a single tag).
|
metadata and a single tag).
|
||||||
|
|
||||||
|
### `<digitalocean_sd_config>`
|
||||||
|
|
||||||
|
DigitalOcean SD configurations allow retrieving scrape targets from [DigitalOcean's](https://www.digitalocean.com/)
|
||||||
|
Droplets API.
|
||||||
|
This service discovery uses the public IPv4 address by default, by that can be
|
||||||
|
changed with relabelling, as demonstrated in [the Prometheus digitalocean-sd
|
||||||
|
configuration file](/documentation/examples/prometheus-digitalocean.yml).
|
||||||
|
|
||||||
|
The following meta labels are available on targets during [relabeling](#relabel_config):
|
||||||
|
|
||||||
|
* `__meta_digitalocean_droplet_id`: the id of the droplet
|
||||||
|
* `__meta_digitalocean_droplet_name`: the name of the droplet
|
||||||
|
* `__meta_digitalocean_image`: the image name of the droplet
|
||||||
|
* `__meta_digitalocean_private_ipv4`: the private IPv4 of the droplet
|
||||||
|
* `__meta_digitalocean_public_ipv4`: the public IPv4 of the droplet
|
||||||
|
* `__meta_digitalocean_public_ipv6`: the public IPv6 of the droplet
|
||||||
|
* `__meta_digitalocean_region`: the region of the droplet
|
||||||
|
* `__meta_digitalocean_size`: the size of the droplet
|
||||||
|
* `__meta_digitalocean_status`: the status of the droplet
|
||||||
|
* `__meta_digitalocean_features`: the comma-separated list of features of the droplet
|
||||||
|
* `__meta_digitalocean_tags`: the comma-separated list of tags of the droplet
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 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, not currently supported by DigitalOcean.
|
||||||
|
basic_auth:
|
||||||
|
[ username: <string> ]
|
||||||
|
[ password: <secret> ]
|
||||||
|
[ password_file: <string> ]
|
||||||
|
|
||||||
|
# Optional bearer token authentication information.
|
||||||
|
[ 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 droplets are refreshed.
|
||||||
|
[ refresh_interval: <duration> | default = 60s ]
|
||||||
|
```
|
||||||
|
|
||||||
### `<dns_sd_config>`
|
### `<dns_sd_config>`
|
||||||
|
|
||||||
A DNS-based service discovery configuration allows specifying a set of DNS
|
A DNS-based service discovery configuration allows specifying a set of DNS
|
||||||
|
|
25
documentation/examples/prometheus-digitalocean.yml
Normal file
25
documentation/examples/prometheus-digitalocean.yml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# A example scrape configuration for running Prometheus with
|
||||||
|
# DigitalOcean.
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
digitalocean_sd_configs:
|
||||||
|
- bearer_token: "<replace with a Personal Access Token>"
|
||||||
|
relabel_configs:
|
||||||
|
# Only scrape targets that have a tag 'monitoring'.
|
||||||
|
- source_labels: [__meta_digitalocean_tags]
|
||||||
|
regex: '.*,monitoring,.*'
|
||||||
|
action: keep
|
||||||
|
|
||||||
|
# Use the public IPv6 address and port 9100 to scrape the target.
|
||||||
|
- source_labels: [__meta_digitalocean_public_ipv6]
|
||||||
|
target_label: __address__
|
||||||
|
replacement: '[$1]:9100'
|
1
go.mod
1
go.mod
|
@ -14,6 +14,7 @@ require (
|
||||||
github.com/cespare/xxhash v1.1.0
|
github.com/cespare/xxhash v1.1.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b
|
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b
|
||||||
|
github.com/digitalocean/godo v1.37.0
|
||||||
github.com/edsrzf/mmap-go v1.0.0
|
github.com/edsrzf/mmap-go v1.0.0
|
||||||
github.com/go-kit/kit v0.10.0
|
github.com/go-kit/kit v0.10.0
|
||||||
github.com/go-logfmt/logfmt v0.5.0
|
github.com/go-logfmt/logfmt v0.5.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -150,6 +150,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
||||||
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
|
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b h1:Yqiad0+sloMPdd/0Fg22actpFx0dekpzt1xJmVNVkU0=
|
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b h1:Yqiad0+sloMPdd/0Fg22actpFx0dekpzt1xJmVNVkU0=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/digitalocean/godo v1.37.0 h1:NEj5ne2cvLBHo1GJY1DNN/iEt9ipa72CMwwAjKEA530=
|
||||||
|
github.com/digitalocean/godo v1.37.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
|
@ -362,6 +364,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-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||||
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=
|
||||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||||
|
|
1
vendor/github.com/digitalocean/godo/.gitignore
generated
vendored
Normal file
1
vendor/github.com/digitalocean/godo/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
vendor/
|
8
vendor/github.com/digitalocean/godo/.whitesource
generated
vendored
Normal file
8
vendor/github.com/digitalocean/godo/.whitesource
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"checkRunSettings": {
|
||||||
|
"vulnerableCheckRunConclusionLevel": "failure"
|
||||||
|
},
|
||||||
|
"issueSettings": {
|
||||||
|
"minSeverityLevel": "LOW"
|
||||||
|
}
|
||||||
|
}
|
52
vendor/github.com/digitalocean/godo/1-click.go
generated
vendored
Normal file
52
vendor/github.com/digitalocean/godo/1-click.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const oneClickBasePath = "v2/1-clicks"
|
||||||
|
|
||||||
|
// OneClickService is an interface for interacting with 1-clicks with the
|
||||||
|
// DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#1-click-applications
|
||||||
|
type OneClickService interface {
|
||||||
|
List(context.Context, string) ([]*OneClick, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ OneClickService = &OneClickServiceOp{}
|
||||||
|
|
||||||
|
// OneClickServiceOp interfaces with 1-click endpoints in the DigitalOcean API.
|
||||||
|
type OneClickServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// OneClick is the structure of a 1-click
|
||||||
|
type OneClick struct {
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OneClicksRoot is the root of the json payload that contains a list of 1-clicks
|
||||||
|
type OneClicksRoot struct {
|
||||||
|
List []*OneClick `json:"1_clicks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of the available 1-click applications.
|
||||||
|
func (ocs *OneClickServiceOp) List(ctx context.Context, oneClickType string) ([]*OneClick, *Response, error) {
|
||||||
|
path := fmt.Sprintf(`%s?type=%s`, oneClickBasePath, oneClickType)
|
||||||
|
|
||||||
|
req, err := ocs.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(OneClicksRoot)
|
||||||
|
resp, err := ocs.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.List, resp, nil
|
||||||
|
}
|
255
vendor/github.com/digitalocean/godo/CHANGELOG.md
generated
vendored
Normal file
255
vendor/github.com/digitalocean/godo/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
# Change Log
|
||||||
|
|
||||||
|
## [v1.37.0] - 2020-06-01
|
||||||
|
|
||||||
|
- #336 registry: URL encode repository names when building URLs. @adamwg
|
||||||
|
- #335 Add 1-click service and request. @scottcrawford03
|
||||||
|
|
||||||
|
## [v1.36.0] - 2020-05-12
|
||||||
|
|
||||||
|
- #331 Expose expiry_seconds for Registry.DockerCredentials. @andrewsomething
|
||||||
|
|
||||||
|
## [v1.35.1] - 2020-04-21
|
||||||
|
|
||||||
|
- #328 Update vulnerable x/crypto dependency - @bentranter
|
||||||
|
|
||||||
|
## [v1.35.0] - 2020-04-20
|
||||||
|
|
||||||
|
- #326 Add TagCount field to registry/Repository - @nicktate
|
||||||
|
- #325 Add DOCR EA routes - @nicktate
|
||||||
|
- #324 Upgrade godo to Go 1.14 - @bentranter
|
||||||
|
|
||||||
|
## [v1.34.0] - 2020-03-30
|
||||||
|
|
||||||
|
- #320 Add VPC v3 attributes - @viola
|
||||||
|
|
||||||
|
## [v1.33.1] - 2020-03-23
|
||||||
|
|
||||||
|
- #318 upgrade github.com/stretchr/objx past 0.1.1 - @hilary
|
||||||
|
|
||||||
|
## [v1.33.0] - 2020-03-20
|
||||||
|
|
||||||
|
- #310 Add BillingHistory service and List endpoint - @rbutler
|
||||||
|
- #316 load balancers: add new enable_backend_keepalive field - @anitgandhi
|
||||||
|
|
||||||
|
## [v1.32.0] - 2020-03-04
|
||||||
|
|
||||||
|
- #311 Add reset database user auth method - @zbarahal-do
|
||||||
|
|
||||||
|
## [v1.31.0] - 2020-02-28
|
||||||
|
|
||||||
|
- #305 invoices: GetPDF and GetCSV methods - @rbutler
|
||||||
|
- #304 Add NewFromToken convenience method to init client - @bentranter
|
||||||
|
- #301 invoices: Get, Summary, and List methods - @rbutler
|
||||||
|
- #299 Fix param expiry_seconds for kubernetes.GetCredentials request - @velp
|
||||||
|
|
||||||
|
## [v1.30.0] - 2020-02-03
|
||||||
|
|
||||||
|
- #295 registry: support the created_at field - @adamwg
|
||||||
|
- #293 doks: node pool labels - @snormore
|
||||||
|
|
||||||
|
## [v1.29.0] - 2019-12-13
|
||||||
|
|
||||||
|
- #288 Add Balance Get method - @rbutler
|
||||||
|
- #286,#289 Deserialize meta field - @timoreimann
|
||||||
|
|
||||||
|
## [v1.28.0] - 2019-12-04
|
||||||
|
|
||||||
|
- #282 Add valid Redis eviction policy constants - @bentranter
|
||||||
|
- #281 Remove databases info from top-level godoc string - @bentranter
|
||||||
|
- #280 Fix VolumeSnapshotResourceType value volumesnapshot -> volume_snapshot - @aqche
|
||||||
|
|
||||||
|
## [v1.27.0] - 2019-11-18
|
||||||
|
|
||||||
|
- #278 add mysql user auth settings for database users - @gregmankes
|
||||||
|
|
||||||
|
## [v1.26.0] - 2019-11-13
|
||||||
|
|
||||||
|
- #272 dbaas: get and set mysql sql mode - @mikejholly
|
||||||
|
|
||||||
|
## [v1.25.0] - 2019-11-13
|
||||||
|
|
||||||
|
- #275 registry/docker-credentials: add support for the read/write parameter - @kamaln7
|
||||||
|
- #273 implement the registry/docker-credentials endpoint - @kamaln7
|
||||||
|
- #271 Add registry resource - @snormore
|
||||||
|
|
||||||
|
## [v1.24.1] - 2019-11-04
|
||||||
|
|
||||||
|
- #264 Update isLast to check p.Next - @aqche
|
||||||
|
|
||||||
|
## [v1.24.0] - 2019-10-30
|
||||||
|
|
||||||
|
- #267 Return []DatabaseFirewallRule in addition to raw response. - @andrewsomething
|
||||||
|
|
||||||
|
## [v1.23.1] - 2019-10-30
|
||||||
|
|
||||||
|
- #265 add support for getting/setting firewall rules - @gregmankes
|
||||||
|
- #262 remove ResolveReference call - @mdanzinger
|
||||||
|
- #261 Update CONTRIBUTING.md - @mdanzinger
|
||||||
|
|
||||||
|
## [v1.22.0] - 2019-09-24
|
||||||
|
|
||||||
|
- #259 Add Kubernetes GetCredentials method - @snormore
|
||||||
|
|
||||||
|
## [v1.21.1] - 2019-09-19
|
||||||
|
|
||||||
|
- #257 Upgrade to Go 1.13 - @bentranter
|
||||||
|
|
||||||
|
## [v1.21.0] - 2019-09-16
|
||||||
|
|
||||||
|
- #255 Add DropletID to Kubernetes Node instance - @snormore
|
||||||
|
- #254 Add tags to Database, DatabaseReplica - @Zyqsempai
|
||||||
|
|
||||||
|
## [v1.20.0] - 2019-09-06
|
||||||
|
|
||||||
|
- #252 Add Kubernetes autoscale config fields - @snormore
|
||||||
|
- #251 Support unset fields on Kubernetes cluster and node pool updates - @snormore
|
||||||
|
- #250 Add Kubernetes GetUser method - @snormore
|
||||||
|
|
||||||
|
## [v1.19.0] - 2019-07-19
|
||||||
|
|
||||||
|
- #244 dbaas: add private-network-uuid field to create request
|
||||||
|
|
||||||
|
## [v1.18.0] - 2019-07-17
|
||||||
|
|
||||||
|
- #241 Databases: support for custom VPC UUID on migrate @mikejholly
|
||||||
|
- #240 Add the ability to get URN for a Database @stack72
|
||||||
|
- #236 Fix omitempty typos in JSON struct tags @amccarthy1
|
||||||
|
|
||||||
|
## [v1.17.0] - 2019-06-21
|
||||||
|
|
||||||
|
- #238 Add support for Redis eviction policy in Databases @mikejholly
|
||||||
|
|
||||||
|
## [v1.16.0] - 2019-06-04
|
||||||
|
|
||||||
|
- #233 Add Kubernetes DeleteNode method, deprecate RecycleNodePoolNodes @bouk
|
||||||
|
|
||||||
|
## [v1.15.0] - 2019-05-13
|
||||||
|
|
||||||
|
- #231 Add private connection fields to Databases - @mikejholly
|
||||||
|
- #223 Introduce Go modules - @andreiavrammsd
|
||||||
|
|
||||||
|
## [v1.14.0] - 2019-05-13
|
||||||
|
|
||||||
|
- #229 Add support for upgrading Kubernetes clusters - @adamwg
|
||||||
|
|
||||||
|
## [v1.13.0] - 2019-04-19
|
||||||
|
|
||||||
|
- #213 Add tagging support for volume snapshots - @jcodybaker
|
||||||
|
|
||||||
|
## [v1.12.0] - 2019-04-18
|
||||||
|
|
||||||
|
- #224 Add maintenance window support for Kubernetes- @fatih
|
||||||
|
|
||||||
|
## [v1.11.1] - 2019-04-04
|
||||||
|
|
||||||
|
- #222 Fix Create Database Pools json fields - @sunny-b
|
||||||
|
|
||||||
|
## [v1.11.0] - 2019-04-03
|
||||||
|
|
||||||
|
- #220 roll out vpc functionality - @jheimann
|
||||||
|
|
||||||
|
## [v1.10.1] - 2019-03-27
|
||||||
|
|
||||||
|
- #219 Fix Database Pools json field - @sunny-b
|
||||||
|
|
||||||
|
## [v1.10.0] - 2019-03-20
|
||||||
|
|
||||||
|
- #215 Add support for Databases - @mikejholly
|
||||||
|
|
||||||
|
## [v1.9.0] - 2019-03-18
|
||||||
|
|
||||||
|
- #214 add support for enable_proxy_protocol. - @mregmi
|
||||||
|
|
||||||
|
## [v1.8.0] - 2019-03-13
|
||||||
|
|
||||||
|
- #210 Expose tags on storage volume create/list/get. - @jcodybaker
|
||||||
|
|
||||||
|
## [v1.7.5] - 2019-03-04
|
||||||
|
|
||||||
|
- #207 Add support for custom subdomains for Spaces CDN [beta] - @xornivore
|
||||||
|
|
||||||
|
## [v1.7.4] - 2019-02-08
|
||||||
|
|
||||||
|
- #202 Allow tagging volumes - @mchitten
|
||||||
|
|
||||||
|
## [v1.7.3] - 2018-12-18
|
||||||
|
|
||||||
|
- #196 Expose tag support for creating Load Balancers.
|
||||||
|
|
||||||
|
## [v1.7.2] - 2018-12-04
|
||||||
|
|
||||||
|
- #192 Exposes more options for Kubernetes clusters.
|
||||||
|
|
||||||
|
## [v1.7.1] - 2018-11-27
|
||||||
|
|
||||||
|
- #190 Expose constants for the state of Kubernetes clusters.
|
||||||
|
|
||||||
|
## [v1.7.0] - 2018-11-13
|
||||||
|
|
||||||
|
- #188 Kubernetes support [beta] - @aybabtme
|
||||||
|
|
||||||
|
## [v1.6.0] - 2018-10-16
|
||||||
|
|
||||||
|
- #185 Projects support [beta] - @mchitten
|
||||||
|
|
||||||
|
## [v1.5.0] - 2018-10-01
|
||||||
|
|
||||||
|
- #181 Adding tagging images support - @hugocorbucci
|
||||||
|
|
||||||
|
## [v1.4.2] - 2018-08-30
|
||||||
|
|
||||||
|
- #178 Allowing creating domain records with weight of 0 - @TFaga
|
||||||
|
- #177 Adding `VolumeLimit` to account - @lxfontes
|
||||||
|
|
||||||
|
## [v1.4.1] - 2018-08-23
|
||||||
|
|
||||||
|
- #176 Fix cdn flush cache API endpoint - @sunny-b
|
||||||
|
|
||||||
|
## [v1.4.0] - 2018-08-22
|
||||||
|
|
||||||
|
- #175 Add support for Spaces CDN - @sunny-b
|
||||||
|
|
||||||
|
## [v1.3.0] - 2018-05-24
|
||||||
|
|
||||||
|
- #170 Add support for volume formatting - @adamwg
|
||||||
|
|
||||||
|
## [v1.2.0] - 2018-05-08
|
||||||
|
|
||||||
|
- #166 Remove support for Go 1.6 - @iheanyi
|
||||||
|
- #165 Add support for Let's Encrypt Certificates - @viola
|
||||||
|
|
||||||
|
## [v1.1.3] - 2018-03-07
|
||||||
|
|
||||||
|
- #156 Handle non-json errors from the API - @aknuds1
|
||||||
|
- #158 Update droplet example to use latest instance type - @dan-v
|
||||||
|
|
||||||
|
## [v1.1.2] - 2018-03-06
|
||||||
|
|
||||||
|
- #157 storage: list volumes should handle only name or only region params - @andrewsykim
|
||||||
|
- #154 docs: replace first example with fully-runnable example - @xmudrii
|
||||||
|
- #152 Handle flags & tag properties of domain record - @jaymecd
|
||||||
|
|
||||||
|
## [v1.1.1] - 2017-09-29
|
||||||
|
|
||||||
|
- #151 Following user agent field recommendations - @joonas
|
||||||
|
- #148 AsRequest method to create load balancers requests - @lukegb
|
||||||
|
|
||||||
|
## [v1.1.0] - 2017-06-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- #145 Add FirewallsService for managing Firewalls with the DigitalOcean API. - @viola
|
||||||
|
- #139 Add TTL field to the Domains. - @xmudrii
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- #143 Fix oauth2.NoContext depreciation. - @jbowens
|
||||||
|
- #141 Fix DropletActions on tagged resources. - @xmudrii
|
||||||
|
|
||||||
|
## [v1.0.0] - 2017-03-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- #130 Add Convert to ImageActionsService. - @xmudrii
|
||||||
|
- #126 Add CertificatesService for managing certificates with the DigitalOcean API. - @viola
|
||||||
|
- #125 Add LoadBalancersService for managing load balancers with the DigitalOcean API. - @viola
|
||||||
|
- #122 Add GetVolumeByName to StorageService. - @protochron
|
||||||
|
- #113 Add context.Context to all calls. - @aybabtme
|
54
vendor/github.com/digitalocean/godo/CONTRIBUTING.md
generated
vendored
Normal file
54
vendor/github.com/digitalocean/godo/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
We love contributions! You are welcome to open a pull request, but it's a good idea to
|
||||||
|
open an issue and discuss your idea with us first.
|
||||||
|
|
||||||
|
Once you are ready to open a PR, please keep the following guidelines in mind:
|
||||||
|
|
||||||
|
1. Code should be `go fmt` compliant.
|
||||||
|
1. Types, structs and funcs should be documented.
|
||||||
|
1. Tests pass.
|
||||||
|
|
||||||
|
## Getting set up
|
||||||
|
|
||||||
|
`godo` uses go modules. Just fork this repo, clone your fork and off you go!
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
When working on code in this repository, tests can be run via:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go test -mod=vendor .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Godo follows [semver](https://www.semver.org) versioning semantics.
|
||||||
|
New functionality should be accompanied by increment to the minor
|
||||||
|
version number. Any code merged to master is subject to release.
|
||||||
|
|
||||||
|
## Releasing
|
||||||
|
|
||||||
|
Releasing a new version of godo is currently a manual process.
|
||||||
|
|
||||||
|
Submit a separate pull request for the version change from the pull
|
||||||
|
request with your changes.
|
||||||
|
|
||||||
|
1. Update the `CHANGELOG.md` with your changes. If a version header
|
||||||
|
for the next (unreleased) version does not exist, create one.
|
||||||
|
Include one bullet point for each piece of new functionality in the
|
||||||
|
release, including the pull request ID, description, and author(s).
|
||||||
|
|
||||||
|
```
|
||||||
|
## [v1.8.0] - 2019-03-13
|
||||||
|
|
||||||
|
- #210 Expose tags on storage volume create/list/get. - @jcodybaker
|
||||||
|
- #123 Update test dependencies - @digitalocean
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update the `libraryVersion` number in `godo.go`.
|
||||||
|
3. Make a pull request with these changes. This PR should be separate from the PR containing the godo changes.
|
||||||
|
4. Once the pull request has been merged, [draft a new release](https://github.com/digitalocean/godo/releases/new).
|
||||||
|
5. Update the `Tag version` and `Release title` field with the new godo version. Be sure the version has a `v` prefixed in both places. Ex `v1.8.0`.
|
||||||
|
6. Copy the changelog bullet points to the description field.
|
||||||
|
7. Publish the release.
|
55
vendor/github.com/digitalocean/godo/LICENSE.txt
generated
vendored
Normal file
55
vendor/github.com/digitalocean/godo/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
Copyright (c) 2014-2016 The godo AUTHORS. All rights reserved.
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
======================
|
||||||
|
Portions of the client are based on code at:
|
||||||
|
https://github.com/google/go-github/
|
||||||
|
|
||||||
|
Copyright (c) 2013 The go-github AUTHORS. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
139
vendor/github.com/digitalocean/godo/README.md
generated
vendored
Normal file
139
vendor/github.com/digitalocean/godo/README.md
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
# Godo
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/digitalocean/godo.svg)](https://travis-ci.org/digitalocean/godo)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/digitalocean/godo?status.svg)](https://godoc.org/github.com/digitalocean/godo)
|
||||||
|
|
||||||
|
Godo is a Go client library for accessing the DigitalOcean V2 API.
|
||||||
|
|
||||||
|
You can view the client API docs here: [http://godoc.org/github.com/digitalocean/godo](http://godoc.org/github.com/digitalocean/godo)
|
||||||
|
|
||||||
|
You can view DigitalOcean API docs here: [https://developers.digitalocean.com/documentation/v2/](https://developers.digitalocean.com/documentation/v2/)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
```sh
|
||||||
|
go get github.com/digitalocean/godo@vX.Y.Z
|
||||||
|
```
|
||||||
|
|
||||||
|
where X.Y.Z is the [version](https://github.com/digitalocean/godo/releases) you need.
|
||||||
|
|
||||||
|
or
|
||||||
|
```sh
|
||||||
|
go get github.com/digitalocean/godo
|
||||||
|
```
|
||||||
|
for non Go modules usage or latest version.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/digitalocean/godo"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new DigitalOcean client, then use the exposed services to
|
||||||
|
access different parts of the DigitalOcean API.
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
Currently, Personal Access Token (PAT) is the only method of
|
||||||
|
authenticating with the API. You can manage your tokens
|
||||||
|
at the DigitalOcean Control Panel [Applications Page](https://cloud.digitalocean.com/settings/applications).
|
||||||
|
|
||||||
|
You can then use your token to create a new client:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/digitalocean/godo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client := godo.NewFromToken("my-digitalocean-api-token")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to provide a `context.Context` to your new client, you should use [`godo.NewClient`](https://godoc.org/github.com/digitalocean/godo#NewClient) to manually construct a client instead.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
|
||||||
|
To create a new Droplet:
|
||||||
|
|
||||||
|
```go
|
||||||
|
dropletName := "super-cool-droplet"
|
||||||
|
|
||||||
|
createRequest := &godo.DropletCreateRequest{
|
||||||
|
Name: dropletName,
|
||||||
|
Region: "nyc3",
|
||||||
|
Size: "s-1vcpu-1gb",
|
||||||
|
Image: godo.DropletCreateImage{
|
||||||
|
Slug: "ubuntu-14-04-x64",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
newDroplet, _, err := client.Droplets.Create(ctx, createRequest)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Something bad happened: %s\n\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
|
||||||
|
If a list of items is paginated by the API, you must request pages individually. For example, to fetch all Droplets:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func DropletList(ctx context.Context, client *godo.Client) ([]godo.Droplet, error) {
|
||||||
|
// create a list to hold our droplets
|
||||||
|
list := []godo.Droplet{}
|
||||||
|
|
||||||
|
// create options. initially, these will be blank
|
||||||
|
opt := &godo.ListOptions{}
|
||||||
|
for {
|
||||||
|
droplets, resp, err := client.Droplets.List(ctx, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// append the current page's droplets to our list
|
||||||
|
for _, d := range droplets {
|
||||||
|
list = append(list, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are at the last page, break out the for loop
|
||||||
|
if resp.Links == nil || resp.Links.IsLastPage() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
page, err := resp.Links.CurrentPage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the page we want for the next request
|
||||||
|
opt.Page = page + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Each version of the client is tagged and the version is updated accordingly.
|
||||||
|
|
||||||
|
To see the list of past versions, run `git tag`.
|
||||||
|
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For a comprehensive list of examples, check out the [API documentation](https://developers.digitalocean.com/documentation/v2/).
|
||||||
|
|
||||||
|
For details on all the functionality in this library, see the [GoDoc](http://godoc.org/github.com/digitalocean/godo) documentation.
|
||||||
|
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We love pull requests! Please see the [contribution guidelines](CONTRIBUTING.md).
|
60
vendor/github.com/digitalocean/godo/account.go
generated
vendored
Normal file
60
vendor/github.com/digitalocean/godo/account.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccountService is an interface for interfacing with the Account
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#account
|
||||||
|
type AccountService interface {
|
||||||
|
Get(context.Context) (*Account, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountServiceOp handles communication with the Account related methods of
|
||||||
|
// the DigitalOcean API.
|
||||||
|
type AccountServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ AccountService = &AccountServiceOp{}
|
||||||
|
|
||||||
|
// Account represents a DigitalOcean Account
|
||||||
|
type Account struct {
|
||||||
|
DropletLimit int `json:"droplet_limit,omitempty"`
|
||||||
|
FloatingIPLimit int `json:"floating_ip_limit,omitempty"`
|
||||||
|
VolumeLimit int `json:"volume_limit,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
UUID string `json:"uuid,omitempty"`
|
||||||
|
EmailVerified bool `json:"email_verified,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
StatusMessage string `json:"status_message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type accountRoot struct {
|
||||||
|
Account *Account `json:"account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Account) String() string {
|
||||||
|
return Stringify(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DigitalOcean account info
|
||||||
|
func (s *AccountServiceOp) Get(ctx context.Context) (*Account, *Response, error) {
|
||||||
|
|
||||||
|
path := "v2/account"
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(accountRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Account, resp, err
|
||||||
|
}
|
108
vendor/github.com/digitalocean/godo/action.go
generated
vendored
Normal file
108
vendor/github.com/digitalocean/godo/action.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
actionsBasePath = "v2/actions"
|
||||||
|
|
||||||
|
// ActionInProgress is an in progress action status
|
||||||
|
ActionInProgress = "in-progress"
|
||||||
|
|
||||||
|
//ActionCompleted is a completed action status
|
||||||
|
ActionCompleted = "completed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ActionsService handles communction with action related methods of the
|
||||||
|
// DigitalOcean API: https://developers.digitalocean.com/documentation/v2#actions
|
||||||
|
type ActionsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Action, *Response, error)
|
||||||
|
Get(context.Context, int) (*Action, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionsServiceOp handles communition with the image action related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type ActionsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ActionsService = &ActionsServiceOp{}
|
||||||
|
|
||||||
|
type actionsRoot struct {
|
||||||
|
Actions []Action `json:"actions"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type actionRoot struct {
|
||||||
|
Event *Action `json:"action"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action represents a DigitalOcean Action
|
||||||
|
type Action struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
StartedAt *Timestamp `json:"started_at"`
|
||||||
|
CompletedAt *Timestamp `json:"completed_at"`
|
||||||
|
ResourceID int `json:"resource_id"`
|
||||||
|
ResourceType string `json:"resource_type"`
|
||||||
|
Region *Region `json:"region,omitempty"`
|
||||||
|
RegionSlug string `json:"region_slug,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all actions
|
||||||
|
func (s *ActionsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Action, *Response, error) {
|
||||||
|
path := actionsBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Actions, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an action by ID.
|
||||||
|
func (s *ActionsServiceOp) Get(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
if id < 1 {
|
||||||
|
return nil, nil, NewArgError("id", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", actionsBasePath, id)
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Action) String() string {
|
||||||
|
return Stringify(a)
|
||||||
|
}
|
52
vendor/github.com/digitalocean/godo/balance.go
generated
vendored
Normal file
52
vendor/github.com/digitalocean/godo/balance.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BalanceService is an interface for interfacing with the Balance
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#balance
|
||||||
|
type BalanceService interface {
|
||||||
|
Get(context.Context) (*Balance, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceServiceOp handles communication with the Balance related methods of
|
||||||
|
// the DigitalOcean API.
|
||||||
|
type BalanceServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BalanceService = &BalanceServiceOp{}
|
||||||
|
|
||||||
|
// Balance represents a DigitalOcean Balance
|
||||||
|
type Balance struct {
|
||||||
|
MonthToDateBalance string `json:"month_to_date_balance"`
|
||||||
|
AccountBalance string `json:"account_balance"`
|
||||||
|
MonthToDateUsage string `json:"month_to_date_usage"`
|
||||||
|
GeneratedAt time.Time `json:"generated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Balance) String() string {
|
||||||
|
return Stringify(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DigitalOcean balance info
|
||||||
|
func (s *BalanceServiceOp) Get(ctx context.Context) (*Balance, *Response, error) {
|
||||||
|
path := "v2/customers/my/balance"
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(Balance)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root, resp, err
|
||||||
|
}
|
72
vendor/github.com/digitalocean/godo/billing_history.go
generated
vendored
Normal file
72
vendor/github.com/digitalocean/godo/billing_history.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const billingHistoryBasePath = "v2/customers/my/billing_history"
|
||||||
|
|
||||||
|
// BillingHistoryService is an interface for interfacing with the BillingHistory
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#billing_history
|
||||||
|
type BillingHistoryService interface {
|
||||||
|
List(context.Context, *ListOptions) (*BillingHistory, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BillingHistoryServiceOp handles communication with the BillingHistory related methods of
|
||||||
|
// the DigitalOcean API.
|
||||||
|
type BillingHistoryServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BillingHistoryService = &BillingHistoryServiceOp{}
|
||||||
|
|
||||||
|
// BillingHistory represents a DigitalOcean Billing History
|
||||||
|
type BillingHistory struct {
|
||||||
|
BillingHistory []BillingHistoryEntry `json:"billing_history"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BillingHistoryEntry represents an entry in a customer's Billing History
|
||||||
|
type BillingHistoryEntry struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
InvoiceID *string `json:"invoice_id"`
|
||||||
|
InvoiceUUID *string `json:"invoice_uuid"`
|
||||||
|
Date time.Time `json:"date"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BillingHistory) String() string {
|
||||||
|
return Stringify(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List the Billing History for a customer
|
||||||
|
func (s *BillingHistoryServiceOp) List(ctx context.Context, opt *ListOptions) (*BillingHistory, *Response, error) {
|
||||||
|
path, err := addOptions(billingHistoryBasePath, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(BillingHistory)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root, resp, err
|
||||||
|
}
|
218
vendor/github.com/digitalocean/godo/cdn.go
generated
vendored
Normal file
218
vendor/github.com/digitalocean/godo/cdn.go
generated
vendored
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const cdnBasePath = "v2/cdn/endpoints"
|
||||||
|
|
||||||
|
// CDNService is an interface for managing Spaces CDN with the DigitalOcean API.
|
||||||
|
type CDNService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]CDN, *Response, error)
|
||||||
|
Get(context.Context, string) (*CDN, *Response, error)
|
||||||
|
Create(context.Context, *CDNCreateRequest) (*CDN, *Response, error)
|
||||||
|
UpdateTTL(context.Context, string, *CDNUpdateTTLRequest) (*CDN, *Response, error)
|
||||||
|
UpdateCustomDomain(context.Context, string, *CDNUpdateCustomDomainRequest) (*CDN, *Response, error)
|
||||||
|
FlushCache(context.Context, string, *CDNFlushCacheRequest) (*Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CDNServiceOp handles communication with the CDN related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type CDNServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ CDNService = &CDNServiceOp{}
|
||||||
|
|
||||||
|
// CDN represents a DigitalOcean CDN
|
||||||
|
type CDN struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Origin string `json:"origin"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
TTL uint32 `json:"ttl"`
|
||||||
|
CertificateID string `json:"certificate_id,omitempty"`
|
||||||
|
CustomDomain string `json:"custom_domain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CDNRoot represents a response from the DigitalOcean API
|
||||||
|
type cdnRoot struct {
|
||||||
|
Endpoint *CDN `json:"endpoint"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type cdnsRoot struct {
|
||||||
|
Endpoints []CDN `json:"endpoints"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CDNCreateRequest represents a request to create a CDN.
|
||||||
|
type CDNCreateRequest struct {
|
||||||
|
Origin string `json:"origin"`
|
||||||
|
TTL uint32 `json:"ttl"`
|
||||||
|
CustomDomain string `json:"custom_domain,omitempty"`
|
||||||
|
CertificateID string `json:"certificate_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CDNUpdateTTLRequest represents a request to update the ttl of a CDN.
|
||||||
|
type CDNUpdateTTLRequest struct {
|
||||||
|
TTL uint32 `json:"ttl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CDNUpdateCustomDomainRequest represents a request to update the custom domain of a CDN.
|
||||||
|
type CDNUpdateCustomDomainRequest struct {
|
||||||
|
CustomDomain string `json:"custom_domain"`
|
||||||
|
CertificateID string `json:"certificate_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CDNFlushCacheRequest represents a request to flush cache of a CDN.
|
||||||
|
type CDNFlushCacheRequest struct {
|
||||||
|
Files []string `json:"files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all CDN endpoints
|
||||||
|
func (c CDNServiceOp) List(ctx context.Context, opt *ListOptions) ([]CDN, *Response, error) {
|
||||||
|
path, err := addOptions(cdnBasePath, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(cdnsRoot)
|
||||||
|
resp, err := c.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Endpoints, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get individual CDN. It requires a non-empty cdn id.
|
||||||
|
func (c CDNServiceOp) Get(ctx context.Context, id string) (*CDN, *Response, error) {
|
||||||
|
if len(id) == 0 {
|
||||||
|
return nil, nil, NewArgError("id", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", cdnBasePath, id)
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(cdnRoot)
|
||||||
|
resp, err := c.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Endpoint, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new CDN
|
||||||
|
func (c CDNServiceOp) Create(ctx context.Context, createRequest *CDNCreateRequest) (*CDN, *Response, error) {
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodPost, cdnBasePath, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(cdnRoot)
|
||||||
|
resp, err := c.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Endpoint, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateTTL updates the ttl of an individual CDN
|
||||||
|
func (c CDNServiceOp) UpdateTTL(ctx context.Context, id string, updateRequest *CDNUpdateTTLRequest) (*CDN, *Response, error) {
|
||||||
|
return c.update(ctx, id, updateRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateCustomDomain sets or removes the custom domain of an individual CDN
|
||||||
|
func (c CDNServiceOp) UpdateCustomDomain(ctx context.Context, id string, updateRequest *CDNUpdateCustomDomainRequest) (*CDN, *Response, error) {
|
||||||
|
return c.update(ctx, id, updateRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CDNServiceOp) update(ctx context.Context, id string, updateRequest interface{}) (*CDN, *Response, error) {
|
||||||
|
if updateRequest == nil {
|
||||||
|
return nil, nil, NewArgError("updateRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(id) == 0 {
|
||||||
|
return nil, nil, NewArgError("id", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
path := fmt.Sprintf("%s/%s", cdnBasePath, id)
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodPut, path, updateRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(cdnRoot)
|
||||||
|
resp, err := c.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Endpoint, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushCache flushes the cache of an individual CDN. Requires a non-empty slice of file paths and/or wildcards
|
||||||
|
func (c CDNServiceOp) FlushCache(ctx context.Context, id string, flushCacheRequest *CDNFlushCacheRequest) (*Response, error) {
|
||||||
|
if flushCacheRequest == nil {
|
||||||
|
return nil, NewArgError("flushCacheRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(id) == 0 {
|
||||||
|
return nil, NewArgError("id", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/cache", cdnBasePath, id)
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodDelete, path, flushCacheRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete an individual CDN
|
||||||
|
func (c CDNServiceOp) Delete(ctx context.Context, id string) (*Response, error) {
|
||||||
|
if len(id) == 0 {
|
||||||
|
return nil, NewArgError("id", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", cdnBasePath, id)
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
130
vendor/github.com/digitalocean/godo/certificates.go
generated
vendored
Normal file
130
vendor/github.com/digitalocean/godo/certificates.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
const certificatesBasePath = "/v2/certificates"
|
||||||
|
|
||||||
|
// CertificatesService is an interface for managing certificates with the DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#certificates
|
||||||
|
type CertificatesService interface {
|
||||||
|
Get(context.Context, string) (*Certificate, *Response, error)
|
||||||
|
List(context.Context, *ListOptions) ([]Certificate, *Response, error)
|
||||||
|
Create(context.Context, *CertificateRequest) (*Certificate, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate represents a DigitalOcean certificate configuration.
|
||||||
|
type Certificate struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
DNSNames []string `json:"dns_names,omitempty"`
|
||||||
|
NotAfter string `json:"not_after,omitempty"`
|
||||||
|
SHA1Fingerprint string `json:"sha1_fingerprint,omitempty"`
|
||||||
|
Created string `json:"created_at,omitempty"`
|
||||||
|
State string `json:"state,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateRequest represents configuration for a new certificate.
|
||||||
|
type CertificateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
DNSNames []string `json:"dns_names,omitempty"`
|
||||||
|
PrivateKey string `json:"private_key,omitempty"`
|
||||||
|
LeafCertificate string `json:"leaf_certificate,omitempty"`
|
||||||
|
CertificateChain string `json:"certificate_chain,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type certificateRoot struct {
|
||||||
|
Certificate *Certificate `json:"certificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type certificatesRoot struct {
|
||||||
|
Certificates []Certificate `json:"certificates"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificatesServiceOp handles communication with certificates methods of the DigitalOcean API.
|
||||||
|
type CertificatesServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ CertificatesService = &CertificatesServiceOp{}
|
||||||
|
|
||||||
|
// Get an existing certificate by its identifier.
|
||||||
|
func (c *CertificatesServiceOp) Get(ctx context.Context, cID string) (*Certificate, *Response, error) {
|
||||||
|
urlStr := path.Join(certificatesBasePath, cID)
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodGet, urlStr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(certificateRoot)
|
||||||
|
resp, err := c.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Certificate, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all certificates.
|
||||||
|
func (c *CertificatesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Certificate, *Response, error) {
|
||||||
|
urlStr, err := addOptions(certificatesBasePath, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodGet, urlStr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(certificatesRoot)
|
||||||
|
resp, err := c.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Certificates, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new certificate with provided configuration.
|
||||||
|
func (c *CertificatesServiceOp) Create(ctx context.Context, cr *CertificateRequest) (*Certificate, *Response, error) {
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodPost, certificatesBasePath, cr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(certificateRoot)
|
||||||
|
resp, err := c.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Certificate, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a certificate by its identifier.
|
||||||
|
func (c *CertificatesServiceOp) Delete(ctx context.Context, cID string) (*Response, error) {
|
||||||
|
urlStr := path.Join(certificatesBasePath, cID)
|
||||||
|
|
||||||
|
req, err := c.client.NewRequest(ctx, http.MethodDelete, urlStr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.client.Do(ctx, req, nil)
|
||||||
|
}
|
845
vendor/github.com/digitalocean/godo/databases.go
generated
vendored
Normal file
845
vendor/github.com/digitalocean/godo/databases.go
generated
vendored
Normal file
|
@ -0,0 +1,845 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
databaseBasePath = "/v2/databases"
|
||||||
|
databaseSinglePath = databaseBasePath + "/%s"
|
||||||
|
databaseResizePath = databaseBasePath + "/%s/resize"
|
||||||
|
databaseMigratePath = databaseBasePath + "/%s/migrate"
|
||||||
|
databaseMaintenancePath = databaseBasePath + "/%s/maintenance"
|
||||||
|
databaseBackupsPath = databaseBasePath + "/%s/backups"
|
||||||
|
databaseUsersPath = databaseBasePath + "/%s/users"
|
||||||
|
databaseUserPath = databaseBasePath + "/%s/users/%s"
|
||||||
|
databaseResetUserAuthPath = databaseUserPath + "/reset_auth"
|
||||||
|
databaseDBPath = databaseBasePath + "/%s/dbs/%s"
|
||||||
|
databaseDBsPath = databaseBasePath + "/%s/dbs"
|
||||||
|
databasePoolPath = databaseBasePath + "/%s/pools/%s"
|
||||||
|
databasePoolsPath = databaseBasePath + "/%s/pools"
|
||||||
|
databaseReplicaPath = databaseBasePath + "/%s/replicas/%s"
|
||||||
|
databaseReplicasPath = databaseBasePath + "/%s/replicas"
|
||||||
|
databaseEvictionPolicyPath = databaseBasePath + "/%s/eviction_policy"
|
||||||
|
databaseSQLModePath = databaseBasePath + "/%s/sql_mode"
|
||||||
|
databaseFirewallRulesPath = databaseBasePath + "/%s/firewall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SQL Mode constants allow for MySQL-specific SQL flavor configuration.
|
||||||
|
const (
|
||||||
|
SQLModeAllowInvalidDates = "ALLOW_INVALID_DATES"
|
||||||
|
SQLModeANSIQuotes = "ANSI_QUOTES"
|
||||||
|
SQLModeHighNotPrecedence = "HIGH_NOT_PRECEDENCE"
|
||||||
|
SQLModeIgnoreSpace = "IGNORE_SPACE"
|
||||||
|
SQLModeNoAuthCreateUser = "NO_AUTO_CREATE_USER"
|
||||||
|
SQLModeNoAutoValueOnZero = "NO_AUTO_VALUE_ON_ZERO"
|
||||||
|
SQLModeNoBackslashEscapes = "NO_BACKSLASH_ESCAPES"
|
||||||
|
SQLModeNoDirInCreate = "NO_DIR_IN_CREATE"
|
||||||
|
SQLModeNoEngineSubstitution = "NO_ENGINE_SUBSTITUTION"
|
||||||
|
SQLModeNoFieldOptions = "NO_FIELD_OPTIONS"
|
||||||
|
SQLModeNoKeyOptions = "NO_KEY_OPTIONS"
|
||||||
|
SQLModeNoTableOptions = "NO_TABLE_OPTIONS"
|
||||||
|
SQLModeNoUnsignedSubtraction = "NO_UNSIGNED_SUBTRACTION"
|
||||||
|
SQLModeNoZeroDate = "NO_ZERO_DATE"
|
||||||
|
SQLModeNoZeroInDate = "NO_ZERO_IN_DATE"
|
||||||
|
SQLModeOnlyFullGroupBy = "ONLY_FULL_GROUP_BY"
|
||||||
|
SQLModePadCharToFullLength = "PAD_CHAR_TO_FULL_LENGTH"
|
||||||
|
SQLModePipesAsConcat = "PIPES_AS_CONCAT"
|
||||||
|
SQLModeRealAsFloat = "REAL_AS_FLOAT"
|
||||||
|
SQLModeStrictAllTables = "STRICT_ALL_TABLES"
|
||||||
|
SQLModeStrictTransTables = "STRICT_TRANS_TABLES"
|
||||||
|
SQLModeANSI = "ANSI"
|
||||||
|
SQLModeDB2 = "DB2"
|
||||||
|
SQLModeMaxDB = "MAXDB"
|
||||||
|
SQLModeMSSQL = "MSSQL"
|
||||||
|
SQLModeMYSQL323 = "MYSQL323"
|
||||||
|
SQLModeMYSQL40 = "MYSQL40"
|
||||||
|
SQLModeOracle = "ORACLE"
|
||||||
|
SQLModePostgreSQL = "POSTGRESQL"
|
||||||
|
SQLModeTraditional = "TRADITIONAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SQL Auth constants allow for MySQL-specific user auth plugins
|
||||||
|
const (
|
||||||
|
SQLAuthPluginNative = "mysql_native_password"
|
||||||
|
SQLAuthPluginCachingSHA2 = "caching_sha2_password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Redis eviction policies supported by the managed Redis product.
|
||||||
|
const (
|
||||||
|
EvictionPolicyNoEviction = "noeviction"
|
||||||
|
EvictionPolicyAllKeysLRU = "allkeys_lru"
|
||||||
|
EvictionPolicyAllKeysRandom = "allkeys_random"
|
||||||
|
EvictionPolicyVolatileLRU = "volatile_lru"
|
||||||
|
EvictionPolicyVolatileRandom = "volatile_random"
|
||||||
|
EvictionPolicyVolatileTTL = "volatile_ttl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The DatabasesService provides access to the DigitalOcean managed database
|
||||||
|
// suite of products through the public API. Customers can create new database
|
||||||
|
// clusters, migrate them between regions, create replicas and interact with
|
||||||
|
// their configurations. Each database service is refered to as a Database. A
|
||||||
|
// SQL database service can have multiple databases residing in the system. To
|
||||||
|
// help make these entities distinct from Databases in godo, we refer to them
|
||||||
|
// here as DatabaseDBs.
|
||||||
|
//
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#databases
|
||||||
|
type DatabasesService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Database, *Response, error)
|
||||||
|
Get(context.Context, string) (*Database, *Response, error)
|
||||||
|
Create(context.Context, *DatabaseCreateRequest) (*Database, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
Resize(context.Context, string, *DatabaseResizeRequest) (*Response, error)
|
||||||
|
Migrate(context.Context, string, *DatabaseMigrateRequest) (*Response, error)
|
||||||
|
UpdateMaintenance(context.Context, string, *DatabaseUpdateMaintenanceRequest) (*Response, error)
|
||||||
|
ListBackups(context.Context, string, *ListOptions) ([]DatabaseBackup, *Response, error)
|
||||||
|
GetUser(context.Context, string, string) (*DatabaseUser, *Response, error)
|
||||||
|
ListUsers(context.Context, string, *ListOptions) ([]DatabaseUser, *Response, error)
|
||||||
|
CreateUser(context.Context, string, *DatabaseCreateUserRequest) (*DatabaseUser, *Response, error)
|
||||||
|
DeleteUser(context.Context, string, string) (*Response, error)
|
||||||
|
ResetUserAuth(context.Context, string, string, *DatabaseResetUserAuthRequest) (*DatabaseUser, *Response, error)
|
||||||
|
ListDBs(context.Context, string, *ListOptions) ([]DatabaseDB, *Response, error)
|
||||||
|
CreateDB(context.Context, string, *DatabaseCreateDBRequest) (*DatabaseDB, *Response, error)
|
||||||
|
GetDB(context.Context, string, string) (*DatabaseDB, *Response, error)
|
||||||
|
DeleteDB(context.Context, string, string) (*Response, error)
|
||||||
|
ListPools(context.Context, string, *ListOptions) ([]DatabasePool, *Response, error)
|
||||||
|
CreatePool(context.Context, string, *DatabaseCreatePoolRequest) (*DatabasePool, *Response, error)
|
||||||
|
GetPool(context.Context, string, string) (*DatabasePool, *Response, error)
|
||||||
|
DeletePool(context.Context, string, string) (*Response, error)
|
||||||
|
GetReplica(context.Context, string, string) (*DatabaseReplica, *Response, error)
|
||||||
|
ListReplicas(context.Context, string, *ListOptions) ([]DatabaseReplica, *Response, error)
|
||||||
|
CreateReplica(context.Context, string, *DatabaseCreateReplicaRequest) (*DatabaseReplica, *Response, error)
|
||||||
|
DeleteReplica(context.Context, string, string) (*Response, error)
|
||||||
|
GetEvictionPolicy(context.Context, string) (string, *Response, error)
|
||||||
|
SetEvictionPolicy(context.Context, string, string) (*Response, error)
|
||||||
|
GetSQLMode(context.Context, string) (string, *Response, error)
|
||||||
|
SetSQLMode(context.Context, string, ...string) (*Response, error)
|
||||||
|
GetFirewallRules(context.Context, string) ([]DatabaseFirewallRule, *Response, error)
|
||||||
|
UpdateFirewallRules(context.Context, string, *DatabaseUpdateFirewallRulesRequest) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabasesServiceOp handles communication with the Databases related methods
|
||||||
|
// of the DigitalOcean API.
|
||||||
|
type DatabasesServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DatabasesService = &DatabasesServiceOp{}
|
||||||
|
|
||||||
|
// Database represents a DigitalOcean managed database product. These managed databases
|
||||||
|
// are usually comprised of a cluster of database nodes, a primary and 0 or more replicas.
|
||||||
|
// The EngineSlug is a string which indicates the type of database service. Some examples are
|
||||||
|
// "pg", "mysql" or "redis". A Database also includes connection information and other
|
||||||
|
// properties of the service like region, size and current status.
|
||||||
|
type Database struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
EngineSlug string `json:"engine,omitempty"`
|
||||||
|
VersionSlug string `json:"version,omitempty"`
|
||||||
|
Connection *DatabaseConnection `json:"connection,omitempty"`
|
||||||
|
PrivateConnection *DatabaseConnection `json:"private_connection,omitempty"`
|
||||||
|
Users []DatabaseUser `json:"users,omitempty"`
|
||||||
|
NumNodes int `json:"num_nodes,omitempty"`
|
||||||
|
SizeSlug string `json:"size,omitempty"`
|
||||||
|
DBNames []string `json:"db_names,omitempty"`
|
||||||
|
RegionSlug string `json:"region,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
MaintenanceWindow *DatabaseMaintenanceWindow `json:"maintenance_window,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
|
PrivateNetworkUUID string `json:"private_network_uuid,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseConnection represents a database connection
|
||||||
|
type DatabaseConnection struct {
|
||||||
|
URI string `json:"uri,omitempty"`
|
||||||
|
Database string `json:"database,omitempty"`
|
||||||
|
Host string `json:"host,omitempty"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
SSL bool `json:"ssl,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseUser represents a user in the database
|
||||||
|
type DatabaseUser struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Role string `json:"role,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
MySQLSettings *DatabaseMySQLUserSettings `json:"mysql_settings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseMySQLUserSettings contains MySQL-specific user settings
|
||||||
|
type DatabaseMySQLUserSettings struct {
|
||||||
|
AuthPlugin string `json:"auth_plugin"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseMaintenanceWindow represents the maintenance_window of a database
|
||||||
|
// cluster
|
||||||
|
type DatabaseMaintenanceWindow struct {
|
||||||
|
Day string `json:"day,omitempty"`
|
||||||
|
Hour string `json:"hour,omitempty"`
|
||||||
|
Pending bool `json:"pending,omitempty"`
|
||||||
|
Description []string `json:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseBackup represents a database backup.
|
||||||
|
type DatabaseBackup struct {
|
||||||
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
|
SizeGigabytes float64 `json:"size_gigabytes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseCreateRequest represents a request to create a database cluster
|
||||||
|
type DatabaseCreateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
EngineSlug string `json:"engine,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
SizeSlug string `json:"size,omitempty"`
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
NumNodes int `json:"num_nodes,omitempty"`
|
||||||
|
PrivateNetworkUUID string `json:"private_network_uuid"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseResizeRequest can be used to initiate a database resize operation.
|
||||||
|
type DatabaseResizeRequest struct {
|
||||||
|
SizeSlug string `json:"size,omitempty"`
|
||||||
|
NumNodes int `json:"num_nodes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseMigrateRequest can be used to initiate a database migrate operation.
|
||||||
|
type DatabaseMigrateRequest struct {
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
PrivateNetworkUUID string `json:"private_network_uuid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseUpdateMaintenanceRequest can be used to update the database's maintenance window.
|
||||||
|
type DatabaseUpdateMaintenanceRequest struct {
|
||||||
|
Day string `json:"day,omitempty"`
|
||||||
|
Hour string `json:"hour,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseDB represents an engine-specific database created within a database cluster. For SQL
|
||||||
|
// databases like PostgreSQL or MySQL, a "DB" refers to a database created on the RDBMS. For instance,
|
||||||
|
// a PostgreSQL database server can contain many database schemas, each with it's own settings, access
|
||||||
|
// permissions and data. ListDBs will return all databases present on the server.
|
||||||
|
type DatabaseDB struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseReplica represents a read-only replica of a particular database
|
||||||
|
type DatabaseReplica struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Connection *DatabaseConnection `json:"connection"`
|
||||||
|
PrivateConnection *DatabaseConnection `json:"private_connection,omitempty"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
PrivateNetworkUUID string `json:"private_network_uuid,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabasePool represents a database connection pool
|
||||||
|
type DatabasePool struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Database string `json:"db"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
Connection *DatabaseConnection `json:"connection"`
|
||||||
|
PrivateConnection *DatabaseConnection `json:"private_connection,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseCreatePoolRequest is used to create a new database connection pool
|
||||||
|
type DatabaseCreatePoolRequest struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Database string `json:"db"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseCreateUserRequest is used to create a new database user
|
||||||
|
type DatabaseCreateUserRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MySQLSettings *DatabaseMySQLUserSettings `json:"mysql_settings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseResetUserAuth request is used to reset a users DB auth
|
||||||
|
type DatabaseResetUserAuthRequest struct {
|
||||||
|
MySQLSettings *DatabaseMySQLUserSettings `json:"mysql_settings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseCreateDBRequest is used to create a new engine-specific database within the cluster
|
||||||
|
type DatabaseCreateDBRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseCreateReplicaRequest is used to create a new read-only replica
|
||||||
|
type DatabaseCreateReplicaRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
PrivateNetworkUUID string `json:"private_network_uuid"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseUpdateFirewallRulesRequest is used to set the firewall rules for a database
|
||||||
|
type DatabaseUpdateFirewallRulesRequest struct {
|
||||||
|
Rules []*DatabaseFirewallRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseFirewallRule is a rule describing an inbound source to a database
|
||||||
|
type DatabaseFirewallRule struct {
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
ClusterUUID string `json:"cluster_uuid"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseUserRoot struct {
|
||||||
|
User *DatabaseUser `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseUsersRoot struct {
|
||||||
|
Users []DatabaseUser `json:"users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseDBRoot struct {
|
||||||
|
DB *DatabaseDB `json:"db"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseDBsRoot struct {
|
||||||
|
DBs []DatabaseDB `json:"dbs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databasesRoot struct {
|
||||||
|
Databases []Database `json:"databases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseRoot struct {
|
||||||
|
Database *Database `json:"database"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseBackupsRoot struct {
|
||||||
|
Backups []DatabaseBackup `json:"backups"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databasePoolRoot struct {
|
||||||
|
Pool *DatabasePool `json:"pool"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databasePoolsRoot struct {
|
||||||
|
Pools []DatabasePool `json:"pools"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseReplicaRoot struct {
|
||||||
|
Replica *DatabaseReplica `json:"replica"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseReplicasRoot struct {
|
||||||
|
Replicas []DatabaseReplica `json:"replicas"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type evictionPolicyRoot struct {
|
||||||
|
EvictionPolicy string `json:"eviction_policy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlModeRoot struct {
|
||||||
|
SQLMode string `json:"sql_mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type databaseFirewallRuleRoot struct {
|
||||||
|
Rules []DatabaseFirewallRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// URN returns a URN identifier for the database
|
||||||
|
func (d Database) URN() string {
|
||||||
|
return ToURN("dbaas", d.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of the Databases visible with the caller's API token
|
||||||
|
func (svc *DatabasesServiceOp) List(ctx context.Context, opts *ListOptions) ([]Database, *Response, error) {
|
||||||
|
path := databaseBasePath
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databasesRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Databases, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the details of a database cluster
|
||||||
|
func (svc *DatabasesServiceOp) Get(ctx context.Context, databaseID string) (*Database, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseSinglePath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Database, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a database cluster
|
||||||
|
func (svc *DatabasesServiceOp) Create(ctx context.Context, create *DatabaseCreateRequest) (*Database, *Response, error) {
|
||||||
|
path := databaseBasePath
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, create)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Database, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a database cluster. There is no way to recover a cluster once
|
||||||
|
// it has been destroyed.
|
||||||
|
func (svc *DatabasesServiceOp) Delete(ctx context.Context, databaseID string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", databaseBasePath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize resizes a database cluster by number of nodes or size
|
||||||
|
func (svc *DatabasesServiceOp) Resize(ctx context.Context, databaseID string, resize *DatabaseResizeRequest) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseResizePath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, resize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate migrates a database cluster to a new region
|
||||||
|
func (svc *DatabasesServiceOp) Migrate(ctx context.Context, databaseID string, migrate *DatabaseMigrateRequest) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseMigratePath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, migrate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMaintenance updates the maintenance window on a cluster
|
||||||
|
func (svc *DatabasesServiceOp) UpdateMaintenance(ctx context.Context, databaseID string, maintenance *DatabaseUpdateMaintenanceRequest) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseMaintenancePath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, maintenance)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListBackups returns a list of the current backups of a database
|
||||||
|
func (svc *DatabasesServiceOp) ListBackups(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseBackup, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseBackupsPath, databaseID)
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseBackupsRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Backups, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUser returns the database user identified by userID
|
||||||
|
func (svc *DatabasesServiceOp) GetUser(ctx context.Context, databaseID, userID string) (*DatabaseUser, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseUserPath, databaseID, userID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseUserRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.User, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUsers returns all database users for the database
|
||||||
|
func (svc *DatabasesServiceOp) ListUsers(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseUser, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseUsersPath, databaseID)
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseUsersRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Users, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser will create a new database user
|
||||||
|
func (svc *DatabasesServiceOp) CreateUser(ctx context.Context, databaseID string, createUser *DatabaseCreateUserRequest) (*DatabaseUser, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseUsersPath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createUser)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseUserRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.User, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *DatabasesServiceOp) ResetUserAuth(ctx context.Context, databaseID, userID string, resetAuth *DatabaseResetUserAuthRequest) (*DatabaseUser, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseResetUserAuthPath, databaseID, userID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, resetAuth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseUserRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.User, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUser will delete an existing database user
|
||||||
|
func (svc *DatabasesServiceOp) DeleteUser(ctx context.Context, databaseID, userID string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseUserPath, databaseID, userID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDBs returns all databases for a given database cluster
|
||||||
|
func (svc *DatabasesServiceOp) ListDBs(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseDB, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseDBsPath, databaseID)
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseDBsRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.DBs, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDB returns a single database by name
|
||||||
|
func (svc *DatabasesServiceOp) GetDB(ctx context.Context, databaseID, name string) (*DatabaseDB, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseDBPath, databaseID, name)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseDBRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.DB, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDB will create a new database
|
||||||
|
func (svc *DatabasesServiceOp) CreateDB(ctx context.Context, databaseID string, createDB *DatabaseCreateDBRequest) (*DatabaseDB, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseDBsPath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseDBRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.DB, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDB will delete an existing database
|
||||||
|
func (svc *DatabasesServiceOp) DeleteDB(ctx context.Context, databaseID, name string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseDBPath, databaseID, name)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPools returns all connection pools for a given database cluster
|
||||||
|
func (svc *DatabasesServiceOp) ListPools(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabasePool, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databasePoolsPath, databaseID)
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databasePoolsRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Pools, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPool returns a single database connection pool by name
|
||||||
|
func (svc *DatabasesServiceOp) GetPool(ctx context.Context, databaseID, name string) (*DatabasePool, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databasePoolPath, databaseID, name)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databasePoolRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Pool, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePool will create a new database connection pool
|
||||||
|
func (svc *DatabasesServiceOp) CreatePool(ctx context.Context, databaseID string, createPool *DatabaseCreatePoolRequest) (*DatabasePool, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databasePoolsPath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createPool)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databasePoolRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Pool, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePool will delete an existing database connection pool
|
||||||
|
func (svc *DatabasesServiceOp) DeletePool(ctx context.Context, databaseID, name string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databasePoolPath, databaseID, name)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReplica returns a single database replica
|
||||||
|
func (svc *DatabasesServiceOp) GetReplica(ctx context.Context, databaseID, name string) (*DatabaseReplica, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseReplicaPath, databaseID, name)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseReplicaRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Replica, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListReplicas returns all read-only replicas for a given database cluster
|
||||||
|
func (svc *DatabasesServiceOp) ListReplicas(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseReplica, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseReplicasPath, databaseID)
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseReplicasRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Replicas, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateReplica will create a new database connection pool
|
||||||
|
func (svc *DatabasesServiceOp) CreateReplica(ctx context.Context, databaseID string, createReplica *DatabaseCreateReplicaRequest) (*DatabaseReplica, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseReplicasPath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createReplica)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(databaseReplicaRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Replica, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteReplica will delete an existing database replica
|
||||||
|
func (svc *DatabasesServiceOp) DeleteReplica(ctx context.Context, databaseID, name string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseReplicaPath, databaseID, name)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEvictionPolicy loads the eviction policy for a given Redis cluster.
|
||||||
|
func (svc *DatabasesServiceOp) GetEvictionPolicy(ctx context.Context, databaseID string) (string, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseEvictionPolicyPath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
root := new(evictionPolicyRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return "", resp, err
|
||||||
|
}
|
||||||
|
return root.EvictionPolicy, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEvictionPolicy updates the eviction policy for a given Redis cluster.
|
||||||
|
//
|
||||||
|
// The valid eviction policies are documented by the exported string constants
|
||||||
|
// with the prefix `EvictionPolicy`.
|
||||||
|
func (svc *DatabasesServiceOp) SetEvictionPolicy(ctx context.Context, databaseID, policy string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseEvictionPolicyPath, databaseID)
|
||||||
|
root := &evictionPolicyRoot{EvictionPolicy: policy}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSQLMode loads the SQL Mode settings for a given MySQL cluster.
|
||||||
|
func (svc *DatabasesServiceOp) GetSQLMode(ctx context.Context, databaseID string) (string, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseSQLModePath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
root := &sqlModeRoot{}
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return "", resp, err
|
||||||
|
}
|
||||||
|
return root.SQLMode, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSQLMode updates the SQL Mode settings for a given MySQL cluster.
|
||||||
|
func (svc *DatabasesServiceOp) SetSQLMode(ctx context.Context, databaseID string, sqlModes ...string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseSQLModePath, databaseID)
|
||||||
|
root := &sqlModeRoot{SQLMode: strings.Join(sqlModes, ",")}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFirewallRules loads the inbound sources for a given cluster.
|
||||||
|
func (svc *DatabasesServiceOp) GetFirewallRules(ctx context.Context, databaseID string) ([]DatabaseFirewallRule, *Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseFirewallRulesPath, databaseID)
|
||||||
|
root := new(databaseFirewallRuleRoot)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Rules, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateFirewallRules sets the inbound sources for a given cluster.
|
||||||
|
func (svc *DatabasesServiceOp) UpdateFirewallRules(ctx context.Context, databaseID string, firewallRulesReq *DatabaseUpdateFirewallRulesRequest) (*Response, error) {
|
||||||
|
path := fmt.Sprintf(databaseFirewallRulesPath, databaseID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, firewallRulesReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return svc.client.Do(ctx, req, nil)
|
||||||
|
}
|
2
vendor/github.com/digitalocean/godo/doc.go
generated
vendored
Normal file
2
vendor/github.com/digitalocean/godo/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Package godo is the DigtalOcean API v2 client for Go.
|
||||||
|
package godo
|
341
vendor/github.com/digitalocean/godo/domains.go
generated
vendored
Normal file
341
vendor/github.com/digitalocean/godo/domains.go
generated
vendored
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const domainsBasePath = "v2/domains"
|
||||||
|
|
||||||
|
// DomainsService is an interface for managing DNS with the DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#domains and
|
||||||
|
// https://developers.digitalocean.com/documentation/v2#domain-records
|
||||||
|
type DomainsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Domain, *Response, error)
|
||||||
|
Get(context.Context, string) (*Domain, *Response, error)
|
||||||
|
Create(context.Context, *DomainCreateRequest) (*Domain, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
|
||||||
|
Records(context.Context, string, *ListOptions) ([]DomainRecord, *Response, error)
|
||||||
|
Record(context.Context, string, int) (*DomainRecord, *Response, error)
|
||||||
|
DeleteRecord(context.Context, string, int) (*Response, error)
|
||||||
|
EditRecord(context.Context, string, int, *DomainRecordEditRequest) (*DomainRecord, *Response, error)
|
||||||
|
CreateRecord(context.Context, string, *DomainRecordEditRequest) (*DomainRecord, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainsServiceOp handles communication with the domain related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type DomainsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DomainsService = &DomainsServiceOp{}
|
||||||
|
|
||||||
|
// Domain represents a DigitalOcean domain
|
||||||
|
type Domain struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
ZoneFile string `json:"zone_file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// domainRoot represents a response from the DigitalOcean API
|
||||||
|
type domainRoot struct {
|
||||||
|
Domain *Domain `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type domainsRoot struct {
|
||||||
|
Domains []Domain `json:"domains"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainCreateRequest respresents a request to create a domain.
|
||||||
|
type DomainCreateRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
IPAddress string `json:"ip_address,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainRecordRoot is the root of an individual Domain Record response
|
||||||
|
type domainRecordRoot struct {
|
||||||
|
DomainRecord *DomainRecord `json:"domain_record"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainRecordsRoot is the root of a group of Domain Record responses
|
||||||
|
type domainRecordsRoot struct {
|
||||||
|
DomainRecords []DomainRecord `json:"domain_records"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainRecord represents a DigitalOcean DomainRecord
|
||||||
|
type DomainRecord struct {
|
||||||
|
ID int `json:"id,float64,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Data string `json:"data,omitempty"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
TTL int `json:"ttl,omitempty"`
|
||||||
|
Weight int `json:"weight"`
|
||||||
|
Flags int `json:"flags"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainRecordEditRequest represents a request to update a domain record.
|
||||||
|
type DomainRecordEditRequest struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Data string `json:"data,omitempty"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
TTL int `json:"ttl,omitempty"`
|
||||||
|
Weight int `json:"weight"`
|
||||||
|
Flags int `json:"flags"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Domain) String() string {
|
||||||
|
return Stringify(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Domain) URN() string {
|
||||||
|
return ToURN("Domain", d.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all domains.
|
||||||
|
func (s DomainsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Domain, *Response, error) {
|
||||||
|
path := domainsBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Domains, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get individual domain. It requires a non-empty domain name.
|
||||||
|
func (s *DomainsServiceOp) Get(ctx context.Context, name string) (*Domain, *Response, error) {
|
||||||
|
if len(name) < 1 {
|
||||||
|
return nil, nil, NewArgError("name", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", domainsBasePath, name)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Domain, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new domain
|
||||||
|
func (s *DomainsServiceOp) Create(ctx context.Context, createRequest *DomainCreateRequest) (*Domain, *Response, error) {
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := domainsBasePath
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Domain, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete domain
|
||||||
|
func (s *DomainsServiceOp) Delete(ctx context.Context, name string) (*Response, error) {
|
||||||
|
if len(name) < 1 {
|
||||||
|
return nil, NewArgError("name", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", domainsBasePath, name)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a DomainRecord to a string.
|
||||||
|
func (d DomainRecord) String() string {
|
||||||
|
return Stringify(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a DomainRecordEditRequest to a string.
|
||||||
|
func (d DomainRecordEditRequest) String() string {
|
||||||
|
return Stringify(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Records returns a slice of DomainRecords for a domain
|
||||||
|
func (s *DomainsServiceOp) Records(ctx context.Context, domain string, opt *ListOptions) ([]DomainRecord, *Response, error) {
|
||||||
|
if len(domain) < 1 {
|
||||||
|
return nil, nil, NewArgError("domain", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/records", domainsBasePath, domain)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainRecordsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.DomainRecords, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record returns the record id from a domain
|
||||||
|
func (s *DomainsServiceOp) Record(ctx context.Context, domain string, id int) (*DomainRecord, *Response, error) {
|
||||||
|
if len(domain) < 1 {
|
||||||
|
return nil, nil, NewArgError("domain", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if id < 1 {
|
||||||
|
return nil, nil, NewArgError("id", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
record := new(domainRecordRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, record)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return record.DomainRecord, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRecord deletes a record from a domain identified by id
|
||||||
|
func (s *DomainsServiceOp) DeleteRecord(ctx context.Context, domain string, id int) (*Response, error) {
|
||||||
|
if len(domain) < 1 {
|
||||||
|
return nil, NewArgError("domain", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if id < 1 {
|
||||||
|
return nil, NewArgError("id", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditRecord edits a record using a DomainRecordEditRequest
|
||||||
|
func (s *DomainsServiceOp) EditRecord(ctx context.Context,
|
||||||
|
domain string,
|
||||||
|
id int,
|
||||||
|
editRequest *DomainRecordEditRequest,
|
||||||
|
) (*DomainRecord, *Response, error) {
|
||||||
|
if len(domain) < 1 {
|
||||||
|
return nil, nil, NewArgError("domain", "cannot be an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if id < 1 {
|
||||||
|
return nil, nil, NewArgError("id", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if editRequest == nil {
|
||||||
|
return nil, nil, NewArgError("editRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPut, path, editRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(domainRecordRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.DomainRecord, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRecord creates a record using a DomainRecordEditRequest
|
||||||
|
func (s *DomainsServiceOp) CreateRecord(ctx context.Context,
|
||||||
|
domain string,
|
||||||
|
createRequest *DomainRecordEditRequest) (*DomainRecord, *Response, error) {
|
||||||
|
if len(domain) < 1 {
|
||||||
|
return nil, nil, NewArgError("domain", "cannot be empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/records", domainsBasePath, domain)
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := new(domainRecordRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.DomainRecord, resp, err
|
||||||
|
}
|
329
vendor/github.com/digitalocean/godo/droplet_actions.go
generated
vendored
Normal file
329
vendor/github.com/digitalocean/godo/droplet_actions.go
generated
vendored
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ActionRequest reprents DigitalOcean Action Request
|
||||||
|
type ActionRequest map[string]interface{}
|
||||||
|
|
||||||
|
// DropletActionsService is an interface for interfacing with the Droplet actions
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#droplet-actions
|
||||||
|
type DropletActionsService interface {
|
||||||
|
Shutdown(context.Context, int) (*Action, *Response, error)
|
||||||
|
ShutdownByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
PowerOff(context.Context, int) (*Action, *Response, error)
|
||||||
|
PowerOffByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
PowerOn(context.Context, int) (*Action, *Response, error)
|
||||||
|
PowerOnByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
PowerCycle(context.Context, int) (*Action, *Response, error)
|
||||||
|
PowerCycleByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
Reboot(context.Context, int) (*Action, *Response, error)
|
||||||
|
Restore(context.Context, int, int) (*Action, *Response, error)
|
||||||
|
Resize(context.Context, int, string, bool) (*Action, *Response, error)
|
||||||
|
Rename(context.Context, int, string) (*Action, *Response, error)
|
||||||
|
Snapshot(context.Context, int, string) (*Action, *Response, error)
|
||||||
|
SnapshotByTag(context.Context, string, string) ([]Action, *Response, error)
|
||||||
|
EnableBackups(context.Context, int) (*Action, *Response, error)
|
||||||
|
EnableBackupsByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
DisableBackups(context.Context, int) (*Action, *Response, error)
|
||||||
|
DisableBackupsByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
PasswordReset(context.Context, int) (*Action, *Response, error)
|
||||||
|
RebuildByImageID(context.Context, int, int) (*Action, *Response, error)
|
||||||
|
RebuildByImageSlug(context.Context, int, string) (*Action, *Response, error)
|
||||||
|
ChangeKernel(context.Context, int, int) (*Action, *Response, error)
|
||||||
|
EnableIPv6(context.Context, int) (*Action, *Response, error)
|
||||||
|
EnableIPv6ByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
EnablePrivateNetworking(context.Context, int) (*Action, *Response, error)
|
||||||
|
EnablePrivateNetworkingByTag(context.Context, string) ([]Action, *Response, error)
|
||||||
|
Get(context.Context, int, int) (*Action, *Response, error)
|
||||||
|
GetByURI(context.Context, string) (*Action, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletActionsServiceOp handles communication with the Droplet action related
|
||||||
|
// methods of the DigitalOcean API.
|
||||||
|
type DropletActionsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DropletActionsService = &DropletActionsServiceOp{}
|
||||||
|
|
||||||
|
// Shutdown a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) Shutdown(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "shutdown"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShutdownByTag shuts down Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) ShutdownByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "shutdown"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PowerOff a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) PowerOff(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "power_off"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PowerOffByTag powers off Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) PowerOffByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "power_off"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PowerOn a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) PowerOn(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "power_on"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PowerOnByTag powers on Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) PowerOnByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "power_on"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PowerCycle a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) PowerCycle(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "power_cycle"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PowerCycleByTag power cycles Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) PowerCycleByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "power_cycle"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reboot a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) Reboot(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "reboot"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore an image to a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) Restore(ctx context.Context, id, imageID int) (*Action, *Response, error) {
|
||||||
|
requestType := "restore"
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": requestType,
|
||||||
|
"image": float64(imageID),
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) Resize(ctx context.Context, id int, sizeSlug string, resizeDisk bool) (*Action, *Response, error) {
|
||||||
|
requestType := "resize"
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": requestType,
|
||||||
|
"size": sizeSlug,
|
||||||
|
"disk": resizeDisk,
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename a Droplet
|
||||||
|
func (s *DropletActionsServiceOp) Rename(ctx context.Context, id int, name string) (*Action, *Response, error) {
|
||||||
|
requestType := "rename"
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": requestType,
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot a Droplet.
|
||||||
|
func (s *DropletActionsServiceOp) Snapshot(ctx context.Context, id int, name string) (*Action, *Response, error) {
|
||||||
|
requestType := "snapshot"
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": requestType,
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotByTag snapshots Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) SnapshotByTag(ctx context.Context, tag string, name string) ([]Action, *Response, error) {
|
||||||
|
requestType := "snapshot"
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": requestType,
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableBackups enables backups for a Droplet.
|
||||||
|
func (s *DropletActionsServiceOp) EnableBackups(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "enable_backups"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableBackupsByTag enables backups for Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) EnableBackupsByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "enable_backups"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableBackups disables backups for a Droplet.
|
||||||
|
func (s *DropletActionsServiceOp) DisableBackups(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "disable_backups"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableBackupsByTag disables backups for Droplet matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) DisableBackupsByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "disable_backups"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordReset resets the password for a Droplet.
|
||||||
|
func (s *DropletActionsServiceOp) PasswordReset(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "password_reset"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebuildByImageID rebuilds a Droplet from an image with a given id.
|
||||||
|
func (s *DropletActionsServiceOp) RebuildByImageID(ctx context.Context, id, imageID int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "rebuild", "image": imageID}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RebuildByImageSlug rebuilds a Droplet from an Image matched by a given Slug.
|
||||||
|
func (s *DropletActionsServiceOp) RebuildByImageSlug(ctx context.Context, id int, slug string) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "rebuild", "image": slug}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeKernel changes the kernel for a Droplet.
|
||||||
|
func (s *DropletActionsServiceOp) ChangeKernel(ctx context.Context, id, kernelID int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "change_kernel", "kernel": kernelID}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableIPv6 enables IPv6 for a Droplet.
|
||||||
|
func (s *DropletActionsServiceOp) EnableIPv6(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "enable_ipv6"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableIPv6ByTag enables IPv6 for Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) EnableIPv6ByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "enable_ipv6"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnablePrivateNetworking enables private networking for a Droplet.
|
||||||
|
func (s *DropletActionsServiceOp) EnablePrivateNetworking(ctx context.Context, id int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "enable_private_networking"}
|
||||||
|
return s.doAction(ctx, id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnablePrivateNetworkingByTag enables private networking for Droplets matched by a Tag.
|
||||||
|
func (s *DropletActionsServiceOp) EnablePrivateNetworkingByTag(ctx context.Context, tag string) ([]Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "enable_private_networking"}
|
||||||
|
return s.doActionByTag(ctx, tag, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DropletActionsServiceOp) doAction(ctx context.Context, id int, request *ActionRequest) (*Action, *Response, error) {
|
||||||
|
if id < 1 {
|
||||||
|
return nil, nil, NewArgError("id", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if request == nil {
|
||||||
|
return nil, nil, NewArgError("request", "request can't be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := dropletActionPath(id)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DropletActionsServiceOp) doActionByTag(ctx context.Context, tag string, request *ActionRequest) ([]Action, *Response, error) {
|
||||||
|
if tag == "" {
|
||||||
|
return nil, nil, NewArgError("tag", "cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if request == nil {
|
||||||
|
return nil, nil, NewArgError("request", "request can't be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := dropletActionPathByTag(tag)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Actions, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an action for a particular Droplet by id.
|
||||||
|
func (s *DropletActionsServiceOp) Get(ctx context.Context, dropletID, actionID int) (*Action, *Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if actionID < 1 {
|
||||||
|
return nil, nil, NewArgError("actionID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", dropletActionPath(dropletID), actionID)
|
||||||
|
return s.get(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByURI gets an action for a particular Droplet by id.
|
||||||
|
func (s *DropletActionsServiceOp) GetByURI(ctx context.Context, rawurl string) (*Action, *Response, error) {
|
||||||
|
u, err := url.Parse(rawurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.get(ctx, u.Path)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DropletActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func dropletActionPath(dropletID int) string {
|
||||||
|
return fmt.Sprintf("v2/droplets/%d/actions", dropletID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dropletActionPathByTag(tag string) string {
|
||||||
|
return fmt.Sprintf("v2/droplets/actions?tag_name=%s", tag)
|
||||||
|
}
|
592
vendor/github.com/digitalocean/godo/droplets.go
generated
vendored
Normal file
592
vendor/github.com/digitalocean/godo/droplets.go
generated
vendored
Normal file
|
@ -0,0 +1,592 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const dropletBasePath = "v2/droplets"
|
||||||
|
|
||||||
|
var errNoNetworks = errors.New("no networks have been defined")
|
||||||
|
|
||||||
|
// DropletsService is an interface for interfacing with the Droplet
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#droplets
|
||||||
|
type DropletsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Droplet, *Response, error)
|
||||||
|
ListByTag(context.Context, string, *ListOptions) ([]Droplet, *Response, error)
|
||||||
|
Get(context.Context, int) (*Droplet, *Response, error)
|
||||||
|
Create(context.Context, *DropletCreateRequest) (*Droplet, *Response, error)
|
||||||
|
CreateMultiple(context.Context, *DropletMultiCreateRequest) ([]Droplet, *Response, error)
|
||||||
|
Delete(context.Context, int) (*Response, error)
|
||||||
|
DeleteByTag(context.Context, string) (*Response, error)
|
||||||
|
Kernels(context.Context, int, *ListOptions) ([]Kernel, *Response, error)
|
||||||
|
Snapshots(context.Context, int, *ListOptions) ([]Image, *Response, error)
|
||||||
|
Backups(context.Context, int, *ListOptions) ([]Image, *Response, error)
|
||||||
|
Actions(context.Context, int, *ListOptions) ([]Action, *Response, error)
|
||||||
|
Neighbors(context.Context, int) ([]Droplet, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletsServiceOp handles communication with the Droplet related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type DropletsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ DropletsService = &DropletsServiceOp{}
|
||||||
|
|
||||||
|
// Droplet represents a DigitalOcean Droplet
|
||||||
|
type Droplet struct {
|
||||||
|
ID int `json:"id,float64,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Memory int `json:"memory,omitempty"`
|
||||||
|
Vcpus int `json:"vcpus,omitempty"`
|
||||||
|
Disk int `json:"disk,omitempty"`
|
||||||
|
Region *Region `json:"region,omitempty"`
|
||||||
|
Image *Image `json:"image,omitempty"`
|
||||||
|
Size *Size `json:"size,omitempty"`
|
||||||
|
SizeSlug string `json:"size_slug,omitempty"`
|
||||||
|
BackupIDs []int `json:"backup_ids,omitempty"`
|
||||||
|
NextBackupWindow *BackupWindow `json:"next_backup_window,omitempty"`
|
||||||
|
SnapshotIDs []int `json:"snapshot_ids,omitempty"`
|
||||||
|
Features []string `json:"features,omitempty"`
|
||||||
|
Locked bool `json:"locked,bool,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Networks *Networks `json:"networks,omitempty"`
|
||||||
|
Created string `json:"created_at,omitempty"`
|
||||||
|
Kernel *Kernel `json:"kernel,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
VolumeIDs []string `json:"volume_ids"`
|
||||||
|
VPCUUID string `json:"vpc_uuid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicIPv4 returns the public IPv4 address for the Droplet.
|
||||||
|
func (d *Droplet) PublicIPv4() (string, error) {
|
||||||
|
if d.Networks == nil {
|
||||||
|
return "", errNoNetworks
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v4 := range d.Networks.V4 {
|
||||||
|
if v4.Type == "public" {
|
||||||
|
return v4.IPAddress, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateIPv4 returns the private IPv4 address for the Droplet.
|
||||||
|
func (d *Droplet) PrivateIPv4() (string, error) {
|
||||||
|
if d.Networks == nil {
|
||||||
|
return "", errNoNetworks
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v4 := range d.Networks.V4 {
|
||||||
|
if v4.Type == "private" {
|
||||||
|
return v4.IPAddress, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicIPv6 returns the public IPv6 address for the Droplet.
|
||||||
|
func (d *Droplet) PublicIPv6() (string, error) {
|
||||||
|
if d.Networks == nil {
|
||||||
|
return "", errNoNetworks
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v6 := range d.Networks.V6 {
|
||||||
|
if v6.Type == "public" {
|
||||||
|
return v6.IPAddress, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kernel object
|
||||||
|
type Kernel struct {
|
||||||
|
ID int `json:"id,float64,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupWindow object
|
||||||
|
type BackupWindow struct {
|
||||||
|
Start *Timestamp `json:"start,omitempty"`
|
||||||
|
End *Timestamp `json:"end,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Droplet to a string
|
||||||
|
func (d Droplet) String() string {
|
||||||
|
return Stringify(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Droplet) URN() string {
|
||||||
|
return ToURN("Droplet", d.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletRoot represents a Droplet root
|
||||||
|
type dropletRoot struct {
|
||||||
|
Droplet *Droplet `json:"droplet"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type dropletsRoot struct {
|
||||||
|
Droplets []Droplet `json:"droplets"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type kernelsRoot struct {
|
||||||
|
Kernels []Kernel `json:"kernels,omitempty"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type dropletSnapshotsRoot struct {
|
||||||
|
Snapshots []Image `json:"snapshots,omitempty"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type backupsRoot struct {
|
||||||
|
Backups []Image `json:"backups,omitempty"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletCreateImage identifies an image for the create request. It prefers slug over ID.
|
||||||
|
type DropletCreateImage struct {
|
||||||
|
ID int
|
||||||
|
Slug string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns either the slug or id of the image. It returns the id
|
||||||
|
// if the slug is empty.
|
||||||
|
func (d DropletCreateImage) MarshalJSON() ([]byte, error) {
|
||||||
|
if d.Slug != "" {
|
||||||
|
return json.Marshal(d.Slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(d.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletCreateVolume identifies a volume to attach for the create request. It
|
||||||
|
// prefers Name over ID,
|
||||||
|
type DropletCreateVolume struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns an object with either the name or id of the volume. It
|
||||||
|
// returns the id if the name is empty.
|
||||||
|
func (d DropletCreateVolume) MarshalJSON() ([]byte, error) {
|
||||||
|
if d.Name != "" {
|
||||||
|
return json.Marshal(struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}{Name: d.Name})
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}{ID: d.ID})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletCreateSSHKey identifies a SSH Key for the create request. It prefers fingerprint over ID.
|
||||||
|
type DropletCreateSSHKey struct {
|
||||||
|
ID int
|
||||||
|
Fingerprint string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns either the fingerprint or id of the ssh key. It returns
|
||||||
|
// the id if the fingerprint is empty.
|
||||||
|
func (d DropletCreateSSHKey) MarshalJSON() ([]byte, error) {
|
||||||
|
if d.Fingerprint != "" {
|
||||||
|
return json.Marshal(d.Fingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(d.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletCreateRequest represents a request to create a Droplet.
|
||||||
|
type DropletCreateRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
Image DropletCreateImage `json:"image"`
|
||||||
|
SSHKeys []DropletCreateSSHKey `json:"ssh_keys"`
|
||||||
|
Backups bool `json:"backups"`
|
||||||
|
IPv6 bool `json:"ipv6"`
|
||||||
|
PrivateNetworking bool `json:"private_networking"`
|
||||||
|
Monitoring bool `json:"monitoring"`
|
||||||
|
UserData string `json:"user_data,omitempty"`
|
||||||
|
Volumes []DropletCreateVolume `json:"volumes,omitempty"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
VPCUUID string `json:"vpc_uuid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropletMultiCreateRequest is a request to create multiple Droplets.
|
||||||
|
type DropletMultiCreateRequest struct {
|
||||||
|
Names []string `json:"names"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
Image DropletCreateImage `json:"image"`
|
||||||
|
SSHKeys []DropletCreateSSHKey `json:"ssh_keys"`
|
||||||
|
Backups bool `json:"backups"`
|
||||||
|
IPv6 bool `json:"ipv6"`
|
||||||
|
PrivateNetworking bool `json:"private_networking"`
|
||||||
|
Monitoring bool `json:"monitoring"`
|
||||||
|
UserData string `json:"user_data,omitempty"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
VPCUUID string `json:"vpc_uuid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DropletCreateRequest) String() string {
|
||||||
|
return Stringify(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d DropletMultiCreateRequest) String() string {
|
||||||
|
return Stringify(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Networks represents the Droplet's Networks.
|
||||||
|
type Networks struct {
|
||||||
|
V4 []NetworkV4 `json:"v4,omitempty"`
|
||||||
|
V6 []NetworkV6 `json:"v6,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkV4 represents a DigitalOcean IPv4 Network.
|
||||||
|
type NetworkV4 struct {
|
||||||
|
IPAddress string `json:"ip_address,omitempty"`
|
||||||
|
Netmask string `json:"netmask,omitempty"`
|
||||||
|
Gateway string `json:"gateway,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NetworkV4) String() string {
|
||||||
|
return Stringify(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkV6 represents a DigitalOcean IPv6 network.
|
||||||
|
type NetworkV6 struct {
|
||||||
|
IPAddress string `json:"ip_address,omitempty"`
|
||||||
|
Netmask int `json:"netmask,omitempty"`
|
||||||
|
Gateway string `json:"gateway,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NetworkV6) String() string {
|
||||||
|
return Stringify(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performs a list request given a path.
|
||||||
|
func (s *DropletsServiceOp) list(ctx context.Context, path string) ([]Droplet, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(dropletsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Droplets, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all Droplets.
|
||||||
|
func (s *DropletsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Droplet, *Response, error) {
|
||||||
|
path := dropletBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.list(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByTag lists all Droplets matched by a Tag.
|
||||||
|
func (s *DropletsServiceOp) ListByTag(ctx context.Context, tag string, opt *ListOptions) ([]Droplet, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s?tag_name=%s", dropletBasePath, tag)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.list(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get individual Droplet.
|
||||||
|
func (s *DropletsServiceOp) Get(ctx context.Context, dropletID int) (*Droplet, *Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(dropletRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Droplet, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Droplet
|
||||||
|
func (s *DropletsServiceOp) Create(ctx context.Context, createRequest *DropletCreateRequest) (*Droplet, *Response, error) {
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := dropletBasePath
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(dropletRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Droplet, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMultiple creates multiple Droplets.
|
||||||
|
func (s *DropletsServiceOp) CreateMultiple(ctx context.Context, createRequest *DropletMultiCreateRequest) ([]Droplet, *Response, error) {
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := dropletBasePath
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(dropletsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Droplets, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performs a delete request given a path
|
||||||
|
func (s *DropletsServiceOp) delete(ctx context.Context, path string) (*Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Droplet.
|
||||||
|
func (s *DropletsServiceOp) Delete(ctx context.Context, dropletID int) (*Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID)
|
||||||
|
|
||||||
|
return s.delete(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteByTag deletes Droplets matched by a Tag.
|
||||||
|
func (s *DropletsServiceOp) DeleteByTag(ctx context.Context, tag string) (*Response, error) {
|
||||||
|
if tag == "" {
|
||||||
|
return nil, NewArgError("tag", "cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s?tag_name=%s", dropletBasePath, tag)
|
||||||
|
|
||||||
|
return s.delete(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kernels lists kernels available for a Droplet.
|
||||||
|
func (s *DropletsServiceOp) Kernels(ctx context.Context, dropletID int, opt *ListOptions) ([]Kernel, *Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d/kernels", dropletBasePath, dropletID)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(kernelsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Kernels, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions lists the actions for a Droplet.
|
||||||
|
func (s *DropletsServiceOp) Actions(ctx context.Context, dropletID int, opt *ListOptions) ([]Action, *Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d/actions", dropletBasePath, dropletID)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Actions, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backups lists the backups for a Droplet.
|
||||||
|
func (s *DropletsServiceOp) Backups(ctx context.Context, dropletID int, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d/backups", dropletBasePath, dropletID)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(backupsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Backups, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshots lists the snapshots available for a Droplet.
|
||||||
|
func (s *DropletsServiceOp) Snapshots(ctx context.Context, dropletID int, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d/snapshots", dropletBasePath, dropletID)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(dropletSnapshotsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Snapshots, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neighbors lists the neighbors for a Droplet.
|
||||||
|
func (s *DropletsServiceOp) Neighbors(ctx context.Context, dropletID int) ([]Droplet, *Response, error) {
|
||||||
|
if dropletID < 1 {
|
||||||
|
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d/neighbors", dropletBasePath, dropletID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(dropletsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Droplets, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DropletsServiceOp) dropletActionStatus(ctx context.Context, uri string) (string, error) {
|
||||||
|
action, _, err := s.client.DropletActions.GetByURI(ctx, uri)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return action.Status, nil
|
||||||
|
}
|
24
vendor/github.com/digitalocean/godo/errors.go
generated
vendored
Normal file
24
vendor/github.com/digitalocean/godo/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ArgError is an error that represents an error with an input to godo. It
|
||||||
|
// identifies the argument and the cause (if possible).
|
||||||
|
type ArgError struct {
|
||||||
|
arg string
|
||||||
|
reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = &ArgError{}
|
||||||
|
|
||||||
|
// NewArgError creates an InputError.
|
||||||
|
func NewArgError(arg, reason string) *ArgError {
|
||||||
|
return &ArgError{
|
||||||
|
arg: arg,
|
||||||
|
reason: reason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ArgError) Error() string {
|
||||||
|
return fmt.Sprintf("%s is invalid because %s", e.arg, e.reason)
|
||||||
|
}
|
271
vendor/github.com/digitalocean/godo/firewalls.go
generated
vendored
Normal file
271
vendor/github.com/digitalocean/godo/firewalls.go
generated
vendored
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const firewallsBasePath = "/v2/firewalls"
|
||||||
|
|
||||||
|
// FirewallsService is an interface for managing Firewalls with the DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#firewalls
|
||||||
|
type FirewallsService interface {
|
||||||
|
Get(context.Context, string) (*Firewall, *Response, error)
|
||||||
|
Create(context.Context, *FirewallRequest) (*Firewall, *Response, error)
|
||||||
|
Update(context.Context, string, *FirewallRequest) (*Firewall, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
List(context.Context, *ListOptions) ([]Firewall, *Response, error)
|
||||||
|
ListByDroplet(context.Context, int, *ListOptions) ([]Firewall, *Response, error)
|
||||||
|
AddDroplets(context.Context, string, ...int) (*Response, error)
|
||||||
|
RemoveDroplets(context.Context, string, ...int) (*Response, error)
|
||||||
|
AddTags(context.Context, string, ...string) (*Response, error)
|
||||||
|
RemoveTags(context.Context, string, ...string) (*Response, error)
|
||||||
|
AddRules(context.Context, string, *FirewallRulesRequest) (*Response, error)
|
||||||
|
RemoveRules(context.Context, string, *FirewallRulesRequest) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirewallsServiceOp handles communication with Firewalls methods of the DigitalOcean API.
|
||||||
|
type FirewallsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Firewall represents a DigitalOcean Firewall configuration.
|
||||||
|
type Firewall struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
InboundRules []InboundRule `json:"inbound_rules"`
|
||||||
|
OutboundRules []OutboundRule `json:"outbound_rules"`
|
||||||
|
DropletIDs []int `json:"droplet_ids"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Created string `json:"created_at"`
|
||||||
|
PendingChanges []PendingChange `json:"pending_changes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a human-readable description of a Firewall.
|
||||||
|
func (fw Firewall) String() string {
|
||||||
|
return Stringify(fw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fw Firewall) URN() string {
|
||||||
|
return ToURN("Firewall", fw.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirewallRequest represents the configuration to be applied to an existing or a new Firewall.
|
||||||
|
type FirewallRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
InboundRules []InboundRule `json:"inbound_rules"`
|
||||||
|
OutboundRules []OutboundRule `json:"outbound_rules"`
|
||||||
|
DropletIDs []int `json:"droplet_ids"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirewallRulesRequest represents rules configuration to be applied to an existing Firewall.
|
||||||
|
type FirewallRulesRequest struct {
|
||||||
|
InboundRules []InboundRule `json:"inbound_rules"`
|
||||||
|
OutboundRules []OutboundRule `json:"outbound_rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InboundRule represents a DigitalOcean Firewall inbound rule.
|
||||||
|
type InboundRule struct {
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
PortRange string `json:"ports,omitempty"`
|
||||||
|
Sources *Sources `json:"sources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutboundRule represents a DigitalOcean Firewall outbound rule.
|
||||||
|
type OutboundRule struct {
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
PortRange string `json:"ports,omitempty"`
|
||||||
|
Destinations *Destinations `json:"destinations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sources represents a DigitalOcean Firewall InboundRule sources.
|
||||||
|
type Sources struct {
|
||||||
|
Addresses []string `json:"addresses,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
DropletIDs []int `json:"droplet_ids,omitempty"`
|
||||||
|
LoadBalancerUIDs []string `json:"load_balancer_uids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingChange represents a DigitalOcean Firewall status details.
|
||||||
|
type PendingChange struct {
|
||||||
|
DropletID int `json:"droplet_id,omitempty"`
|
||||||
|
Removing bool `json:"removing,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destinations represents a DigitalOcean Firewall OutboundRule destinations.
|
||||||
|
type Destinations struct {
|
||||||
|
Addresses []string `json:"addresses,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
DropletIDs []int `json:"droplet_ids,omitempty"`
|
||||||
|
LoadBalancerUIDs []string `json:"load_balancer_uids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ FirewallsService = &FirewallsServiceOp{}
|
||||||
|
|
||||||
|
// Get an existing Firewall by its identifier.
|
||||||
|
func (fw *FirewallsServiceOp) Get(ctx context.Context, fID string) (*Firewall, *Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID)
|
||||||
|
|
||||||
|
req, err := fw.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(firewallRoot)
|
||||||
|
resp, err := fw.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Firewall, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new Firewall with a given configuration.
|
||||||
|
func (fw *FirewallsServiceOp) Create(ctx context.Context, fr *FirewallRequest) (*Firewall, *Response, error) {
|
||||||
|
req, err := fw.client.NewRequest(ctx, http.MethodPost, firewallsBasePath, fr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(firewallRoot)
|
||||||
|
resp, err := fw.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Firewall, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update an existing Firewall with new configuration.
|
||||||
|
func (fw *FirewallsServiceOp) Update(ctx context.Context, fID string, fr *FirewallRequest) (*Firewall, *Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID)
|
||||||
|
|
||||||
|
req, err := fw.client.NewRequest(ctx, "PUT", path, fr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(firewallRoot)
|
||||||
|
resp, err := fw.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Firewall, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a Firewall by its identifier.
|
||||||
|
func (fw *FirewallsServiceOp) Delete(ctx context.Context, fID string) (*Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID)
|
||||||
|
return fw.createAndDoReq(ctx, http.MethodDelete, path, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Firewalls.
|
||||||
|
func (fw *FirewallsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Firewall, *Response, error) {
|
||||||
|
path, err := addOptions(firewallsBasePath, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fw.listHelper(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByDroplet Firewalls.
|
||||||
|
func (fw *FirewallsServiceOp) ListByDroplet(ctx context.Context, dID int, opt *ListOptions) ([]Firewall, *Response, error) {
|
||||||
|
basePath := path.Join(dropletBasePath, strconv.Itoa(dID), "firewalls")
|
||||||
|
path, err := addOptions(basePath, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fw.listHelper(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDroplets to a Firewall.
|
||||||
|
func (fw *FirewallsServiceOp) AddDroplets(ctx context.Context, fID string, dropletIDs ...int) (*Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID, "droplets")
|
||||||
|
return fw.createAndDoReq(ctx, http.MethodPost, path, &dropletsRequest{IDs: dropletIDs})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveDroplets from a Firewall.
|
||||||
|
func (fw *FirewallsServiceOp) RemoveDroplets(ctx context.Context, fID string, dropletIDs ...int) (*Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID, "droplets")
|
||||||
|
return fw.createAndDoReq(ctx, http.MethodDelete, path, &dropletsRequest{IDs: dropletIDs})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTags to a Firewall.
|
||||||
|
func (fw *FirewallsServiceOp) AddTags(ctx context.Context, fID string, tags ...string) (*Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID, "tags")
|
||||||
|
return fw.createAndDoReq(ctx, http.MethodPost, path, &tagsRequest{Tags: tags})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveTags from a Firewall.
|
||||||
|
func (fw *FirewallsServiceOp) RemoveTags(ctx context.Context, fID string, tags ...string) (*Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID, "tags")
|
||||||
|
return fw.createAndDoReq(ctx, http.MethodDelete, path, &tagsRequest{Tags: tags})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRules to a Firewall.
|
||||||
|
func (fw *FirewallsServiceOp) AddRules(ctx context.Context, fID string, rr *FirewallRulesRequest) (*Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID, "rules")
|
||||||
|
return fw.createAndDoReq(ctx, http.MethodPost, path, rr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRules from a Firewall.
|
||||||
|
func (fw *FirewallsServiceOp) RemoveRules(ctx context.Context, fID string, rr *FirewallRulesRequest) (*Response, error) {
|
||||||
|
path := path.Join(firewallsBasePath, fID, "rules")
|
||||||
|
return fw.createAndDoReq(ctx, http.MethodDelete, path, rr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dropletsRequest struct {
|
||||||
|
IDs []int `json:"droplet_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagsRequest struct {
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type firewallRoot struct {
|
||||||
|
Firewall *Firewall `json:"firewall"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type firewallsRoot struct {
|
||||||
|
Firewalls []Firewall `json:"firewalls"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fw *FirewallsServiceOp) createAndDoReq(ctx context.Context, method, path string, v interface{}) (*Response, error) {
|
||||||
|
req, err := fw.client.NewRequest(ctx, method, path, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fw.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fw *FirewallsServiceOp) listHelper(ctx context.Context, path string) ([]Firewall, *Response, error) {
|
||||||
|
req, err := fw.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(firewallsRoot)
|
||||||
|
resp, err := fw.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Firewalls, resp, err
|
||||||
|
}
|
143
vendor/github.com/digitalocean/godo/floating_ips.go
generated
vendored
Normal file
143
vendor/github.com/digitalocean/godo/floating_ips.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const floatingBasePath = "v2/floating_ips"
|
||||||
|
|
||||||
|
// FloatingIPsService is an interface for interfacing with the floating IPs
|
||||||
|
// endpoints of the Digital Ocean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#floating-ips
|
||||||
|
type FloatingIPsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]FloatingIP, *Response, error)
|
||||||
|
Get(context.Context, string) (*FloatingIP, *Response, error)
|
||||||
|
Create(context.Context, *FloatingIPCreateRequest) (*FloatingIP, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloatingIPsServiceOp handles communication with the floating IPs related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type FloatingIPsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ FloatingIPsService = &FloatingIPsServiceOp{}
|
||||||
|
|
||||||
|
// FloatingIP represents a Digital Ocean floating IP.
|
||||||
|
type FloatingIP struct {
|
||||||
|
Region *Region `json:"region"`
|
||||||
|
Droplet *Droplet `json:"droplet"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FloatingIP) String() string {
|
||||||
|
return Stringify(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FloatingIP) URN() string {
|
||||||
|
return ToURN("FloatingIP", f.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
type floatingIPsRoot struct {
|
||||||
|
FloatingIPs []FloatingIP `json:"floating_ips"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type floatingIPRoot struct {
|
||||||
|
FloatingIP *FloatingIP `json:"floating_ip"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloatingIPCreateRequest represents a request to create a floating IP.
|
||||||
|
// If DropletID is not empty, the floating IP will be assigned to the
|
||||||
|
// droplet.
|
||||||
|
type FloatingIPCreateRequest struct {
|
||||||
|
Region string `json:"region"`
|
||||||
|
DropletID int `json:"droplet_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all floating IPs.
|
||||||
|
func (f *FloatingIPsServiceOp) List(ctx context.Context, opt *ListOptions) ([]FloatingIP, *Response, error) {
|
||||||
|
path := floatingBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := f.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(floatingIPsRoot)
|
||||||
|
resp, err := f.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.FloatingIPs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an individual floating IP.
|
||||||
|
func (f *FloatingIPsServiceOp) Get(ctx context.Context, ip string) (*FloatingIP, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", floatingBasePath, ip)
|
||||||
|
|
||||||
|
req, err := f.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(floatingIPRoot)
|
||||||
|
resp, err := f.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.FloatingIP, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a floating IP. If the DropletID field of the request is not empty,
|
||||||
|
// the floating IP will also be assigned to the droplet.
|
||||||
|
func (f *FloatingIPsServiceOp) Create(ctx context.Context, createRequest *FloatingIPCreateRequest) (*FloatingIP, *Response, error) {
|
||||||
|
path := floatingBasePath
|
||||||
|
|
||||||
|
req, err := f.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(floatingIPRoot)
|
||||||
|
resp, err := f.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.FloatingIP, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a floating IP.
|
||||||
|
func (f *FloatingIPsServiceOp) Delete(ctx context.Context, ip string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", floatingBasePath, ip)
|
||||||
|
|
||||||
|
req, err := f.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := f.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
109
vendor/github.com/digitalocean/godo/floating_ips_actions.go
generated
vendored
Normal file
109
vendor/github.com/digitalocean/godo/floating_ips_actions.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FloatingIPActionsService is an interface for interfacing with the
|
||||||
|
// floating IPs actions endpoints of the Digital Ocean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#floating-ips-action
|
||||||
|
type FloatingIPActionsService interface {
|
||||||
|
Assign(ctx context.Context, ip string, dropletID int) (*Action, *Response, error)
|
||||||
|
Unassign(ctx context.Context, ip string) (*Action, *Response, error)
|
||||||
|
Get(ctx context.Context, ip string, actionID int) (*Action, *Response, error)
|
||||||
|
List(ctx context.Context, ip string, opt *ListOptions) ([]Action, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloatingIPActionsServiceOp handles communication with the floating IPs
|
||||||
|
// action related methods of the DigitalOcean API.
|
||||||
|
type FloatingIPActionsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign a floating IP to a droplet.
|
||||||
|
func (s *FloatingIPActionsServiceOp) Assign(ctx context.Context, ip string, dropletID int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": "assign",
|
||||||
|
"droplet_id": dropletID,
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, ip, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unassign a floating IP from the droplet it is currently assigned to.
|
||||||
|
func (s *FloatingIPActionsServiceOp) Unassign(ctx context.Context, ip string) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{"type": "unassign"}
|
||||||
|
return s.doAction(ctx, ip, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an action for a particular floating IP by id.
|
||||||
|
func (s *FloatingIPActionsServiceOp) Get(ctx context.Context, ip string, actionID int) (*Action, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%d", floatingIPActionPath(ip), actionID)
|
||||||
|
return s.get(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List the actions for a particular floating IP.
|
||||||
|
func (s *FloatingIPActionsServiceOp) List(ctx context.Context, ip string, opt *ListOptions) ([]Action, *Response, error) {
|
||||||
|
path := floatingIPActionPath(ip)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.list(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FloatingIPActionsServiceOp) doAction(ctx context.Context, ip string, request *ActionRequest) (*Action, *Response, error) {
|
||||||
|
path := floatingIPActionPath(ip)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FloatingIPActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FloatingIPActionsServiceOp) list(ctx context.Context, path string) ([]Action, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Actions, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func floatingIPActionPath(ip string) string {
|
||||||
|
return fmt.Sprintf("%s/%s/actions", floatingBasePath, ip)
|
||||||
|
}
|
16
vendor/github.com/digitalocean/godo/go.mod
generated
vendored
Normal file
16
vendor/github.com/digitalocean/godo/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module github.com/digitalocean/godo
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/protobuf v1.3.5 // indirect
|
||||||
|
github.com/google/go-querystring v1.0.0
|
||||||
|
github.com/stretchr/testify v1.4.0
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/stretchr/objx => github.com/stretchr/objx v0.2.0
|
||||||
|
|
||||||
|
replace golang.org/x/crypto => golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a
|
42
vendor/github.com/digitalocean/godo/go.sum
generated
vendored
Normal file
42
vendor/github.com/digitalocean/godo/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
432
vendor/github.com/digitalocean/godo/godo.go
generated
vendored
Normal file
432
vendor/github.com/digitalocean/godo/godo.go
generated
vendored
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-querystring/query"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
libraryVersion = "1.37.0"
|
||||||
|
defaultBaseURL = "https://api.digitalocean.com/"
|
||||||
|
userAgent = "godo/" + libraryVersion
|
||||||
|
mediaType = "application/json"
|
||||||
|
|
||||||
|
headerRateLimit = "RateLimit-Limit"
|
||||||
|
headerRateRemaining = "RateLimit-Remaining"
|
||||||
|
headerRateReset = "RateLimit-Reset"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client manages communication with DigitalOcean V2 API.
|
||||||
|
type Client struct {
|
||||||
|
// HTTP client used to communicate with the DO API.
|
||||||
|
client *http.Client
|
||||||
|
|
||||||
|
// Base URL for API requests.
|
||||||
|
BaseURL *url.URL
|
||||||
|
|
||||||
|
// User agent for client
|
||||||
|
UserAgent string
|
||||||
|
|
||||||
|
// Rate contains the current rate limit for the client as determined by the most recent
|
||||||
|
// API call.
|
||||||
|
Rate Rate
|
||||||
|
|
||||||
|
// Services used for communicating with the API
|
||||||
|
Account AccountService
|
||||||
|
Actions ActionsService
|
||||||
|
Balance BalanceService
|
||||||
|
BillingHistory BillingHistoryService
|
||||||
|
CDNs CDNService
|
||||||
|
Domains DomainsService
|
||||||
|
Droplets DropletsService
|
||||||
|
DropletActions DropletActionsService
|
||||||
|
Images ImagesService
|
||||||
|
ImageActions ImageActionsService
|
||||||
|
Invoices InvoicesService
|
||||||
|
Keys KeysService
|
||||||
|
Regions RegionsService
|
||||||
|
Sizes SizesService
|
||||||
|
FloatingIPs FloatingIPsService
|
||||||
|
FloatingIPActions FloatingIPActionsService
|
||||||
|
Snapshots SnapshotsService
|
||||||
|
Storage StorageService
|
||||||
|
StorageActions StorageActionsService
|
||||||
|
Tags TagsService
|
||||||
|
LoadBalancers LoadBalancersService
|
||||||
|
Certificates CertificatesService
|
||||||
|
Firewalls FirewallsService
|
||||||
|
Projects ProjectsService
|
||||||
|
Kubernetes KubernetesService
|
||||||
|
Registry RegistryService
|
||||||
|
Databases DatabasesService
|
||||||
|
VPCs VPCsService
|
||||||
|
OneClick OneClickService
|
||||||
|
|
||||||
|
// Optional function called after every successful request made to the DO APIs
|
||||||
|
onRequestCompleted RequestCompletionCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestCompletionCallback defines the type of the request callback function
|
||||||
|
type RequestCompletionCallback func(*http.Request, *http.Response)
|
||||||
|
|
||||||
|
// ListOptions specifies the optional parameters to various List methods that
|
||||||
|
// support pagination.
|
||||||
|
type ListOptions struct {
|
||||||
|
// For paginated result sets, page of results to retrieve.
|
||||||
|
Page int `url:"page,omitempty"`
|
||||||
|
|
||||||
|
// For paginated result sets, the number of results to include per page.
|
||||||
|
PerPage int `url:"per_page,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response is a DigitalOcean response. This wraps the standard http.Response returned from DigitalOcean.
|
||||||
|
type Response struct {
|
||||||
|
*http.Response
|
||||||
|
|
||||||
|
// Links that were returned with the response. These are parsed from
|
||||||
|
// request body and not the header.
|
||||||
|
Links *Links
|
||||||
|
|
||||||
|
// Meta describes generic information about the response.
|
||||||
|
Meta *Meta
|
||||||
|
|
||||||
|
// Monitoring URI
|
||||||
|
Monitor string
|
||||||
|
|
||||||
|
Rate
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ErrorResponse reports the error caused by an API request
|
||||||
|
type ErrorResponse struct {
|
||||||
|
// HTTP response that caused this error
|
||||||
|
Response *http.Response
|
||||||
|
|
||||||
|
// Error message
|
||||||
|
Message string `json:"message"`
|
||||||
|
|
||||||
|
// RequestID returned from the API, useful to contact support.
|
||||||
|
RequestID string `json:"request_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rate contains the rate limit for the current client.
|
||||||
|
type Rate struct {
|
||||||
|
// The number of request per hour the client is currently limited to.
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
|
||||||
|
// The number of remaining requests the client can make this hour.
|
||||||
|
Remaining int `json:"remaining"`
|
||||||
|
|
||||||
|
// The time at which the current rate limit will reset.
|
||||||
|
Reset Timestamp `json:"reset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func addOptions(s string, opt interface{}) (string, error) {
|
||||||
|
v := reflect.ValueOf(opt)
|
||||||
|
|
||||||
|
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
origURL, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
origValues := origURL.Query()
|
||||||
|
|
||||||
|
newValues, err := query.Values(opt)
|
||||||
|
if err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range newValues {
|
||||||
|
origValues[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
origURL.RawQuery = origValues.Encode()
|
||||||
|
return origURL.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromToken returns a new DigitalOcean API client with the given API
|
||||||
|
// token.
|
||||||
|
func NewFromToken(token string) *Client {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config := &oauth2.Config{}
|
||||||
|
ts := config.TokenSource(ctx, &oauth2.Token{AccessToken: token})
|
||||||
|
|
||||||
|
return NewClient(oauth2.NewClient(ctx, ts))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new DigitalOcean API client, using the given
|
||||||
|
// http.Client to perform all requests.
|
||||||
|
//
|
||||||
|
// Users who wish to pass their own http.Client should use this method. If
|
||||||
|
// you're in need of further customization, the godo.New method allows more
|
||||||
|
// options, such as setting a custom URL or a custom user agent string.
|
||||||
|
func NewClient(httpClient *http.Client) *Client {
|
||||||
|
if httpClient == nil {
|
||||||
|
httpClient = http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
baseURL, _ := url.Parse(defaultBaseURL)
|
||||||
|
|
||||||
|
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent}
|
||||||
|
c.Account = &AccountServiceOp{client: c}
|
||||||
|
c.Actions = &ActionsServiceOp{client: c}
|
||||||
|
c.Balance = &BalanceServiceOp{client: c}
|
||||||
|
c.BillingHistory = &BillingHistoryServiceOp{client: c}
|
||||||
|
c.CDNs = &CDNServiceOp{client: c}
|
||||||
|
c.Certificates = &CertificatesServiceOp{client: c}
|
||||||
|
c.Domains = &DomainsServiceOp{client: c}
|
||||||
|
c.Droplets = &DropletsServiceOp{client: c}
|
||||||
|
c.DropletActions = &DropletActionsServiceOp{client: c}
|
||||||
|
c.Firewalls = &FirewallsServiceOp{client: c}
|
||||||
|
c.FloatingIPs = &FloatingIPsServiceOp{client: c}
|
||||||
|
c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c}
|
||||||
|
c.Images = &ImagesServiceOp{client: c}
|
||||||
|
c.ImageActions = &ImageActionsServiceOp{client: c}
|
||||||
|
c.Invoices = &InvoicesServiceOp{client: c}
|
||||||
|
c.Keys = &KeysServiceOp{client: c}
|
||||||
|
c.LoadBalancers = &LoadBalancersServiceOp{client: c}
|
||||||
|
c.Projects = &ProjectsServiceOp{client: c}
|
||||||
|
c.Regions = &RegionsServiceOp{client: c}
|
||||||
|
c.Sizes = &SizesServiceOp{client: c}
|
||||||
|
c.Snapshots = &SnapshotsServiceOp{client: c}
|
||||||
|
c.Storage = &StorageServiceOp{client: c}
|
||||||
|
c.StorageActions = &StorageActionsServiceOp{client: c}
|
||||||
|
c.Tags = &TagsServiceOp{client: c}
|
||||||
|
c.Kubernetes = &KubernetesServiceOp{client: c}
|
||||||
|
c.Registry = &RegistryServiceOp{client: c}
|
||||||
|
c.Databases = &DatabasesServiceOp{client: c}
|
||||||
|
c.VPCs = &VPCsServiceOp{client: c}
|
||||||
|
c.OneClick = &OneClickServiceOp{client: c}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientOpt are options for New.
|
||||||
|
type ClientOpt func(*Client) error
|
||||||
|
|
||||||
|
// New returns a new DigitalOcean API client instance.
|
||||||
|
func New(httpClient *http.Client, opts ...ClientOpt) (*Client, error) {
|
||||||
|
c := NewClient(httpClient)
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBaseURL is a client option for setting the base URL.
|
||||||
|
func SetBaseURL(bu string) ClientOpt {
|
||||||
|
return func(c *Client) error {
|
||||||
|
u, err := url.Parse(bu)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.BaseURL = u
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserAgent is a client option for setting the user agent.
|
||||||
|
func SetUserAgent(ua string) ClientOpt {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.UserAgent = fmt.Sprintf("%s %s", ua, c.UserAgent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest creates an API request. A relative URL can be provided in urlStr, which will be resolved to the
|
||||||
|
// BaseURL of the Client. Relative URLS should always be specified without a preceding slash. If specified, the
|
||||||
|
// value pointed to by body is JSON encoded and included in as the request body.
|
||||||
|
func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body interface{}) (*http.Request, error) {
|
||||||
|
u, err := c.BaseURL.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
if body != nil {
|
||||||
|
err = json.NewEncoder(buf).Encode(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(method, u.String(), buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Content-Type", mediaType)
|
||||||
|
req.Header.Add("Accept", mediaType)
|
||||||
|
req.Header.Add("User-Agent", c.UserAgent)
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRequestCompleted sets the DO API request completion callback
|
||||||
|
func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) {
|
||||||
|
c.onRequestCompleted = rc
|
||||||
|
}
|
||||||
|
|
||||||
|
// newResponse creates a new Response for the provided http.Response
|
||||||
|
func newResponse(r *http.Response) *Response {
|
||||||
|
response := Response{Response: r}
|
||||||
|
response.populateRate()
|
||||||
|
|
||||||
|
return &response
|
||||||
|
}
|
||||||
|
|
||||||
|
// populateRate parses the rate related headers and populates the response Rate.
|
||||||
|
func (r *Response) populateRate() {
|
||||||
|
if limit := r.Header.Get(headerRateLimit); limit != "" {
|
||||||
|
r.Rate.Limit, _ = strconv.Atoi(limit)
|
||||||
|
}
|
||||||
|
if remaining := r.Header.Get(headerRateRemaining); remaining != "" {
|
||||||
|
r.Rate.Remaining, _ = strconv.Atoi(remaining)
|
||||||
|
}
|
||||||
|
if reset := r.Header.Get(headerRateReset); reset != "" {
|
||||||
|
if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 {
|
||||||
|
r.Rate.Reset = Timestamp{time.Unix(v, 0)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value
|
||||||
|
// pointed to by v, or returned as an error if an API error has occurred. If v implements the io.Writer interface,
|
||||||
|
// the raw response will be written to v, without attempting to decode it.
|
||||||
|
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
|
||||||
|
resp, err := DoRequestWithClient(ctx, c.client, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c.onRequestCompleted != nil {
|
||||||
|
c.onRequestCompleted(req, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if rerr := resp.Body.Close(); err == nil {
|
||||||
|
err = rerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
response := newResponse(resp)
|
||||||
|
c.Rate = response.Rate
|
||||||
|
|
||||||
|
err = CheckResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if v != nil {
|
||||||
|
if w, ok := v.(io.Writer); ok {
|
||||||
|
_, err = io.Copy(w, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoRequest submits an HTTP request.
|
||||||
|
func DoRequest(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||||
|
return DoRequestWithClient(ctx, http.DefaultClient, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoRequestWithClient submits an HTTP request using the specified client.
|
||||||
|
func DoRequestWithClient(
|
||||||
|
ctx context.Context,
|
||||||
|
client *http.Client,
|
||||||
|
req *http.Request) (*http.Response, error) {
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
return client.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ErrorResponse) Error() string {
|
||||||
|
if r.RequestID != "" {
|
||||||
|
return fmt.Sprintf("%v %v: %d (request %q) %v",
|
||||||
|
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.RequestID, r.Message)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v %v: %d %v",
|
||||||
|
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckResponse checks the API response for errors, and returns them if present. A response is considered an
|
||||||
|
// error if it has a status code outside the 200 range. API error responses are expected to have either no response
|
||||||
|
// body, or a JSON response body that maps to ErrorResponse. Any other response body will be silently ignored.
|
||||||
|
func CheckResponse(r *http.Response) error {
|
||||||
|
if c := r.StatusCode; c >= 200 && c <= 299 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
errorResponse := &ErrorResponse{Response: r}
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err == nil && len(data) > 0 {
|
||||||
|
err := json.Unmarshal(data, errorResponse)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse.Message = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Rate) String() string {
|
||||||
|
return Stringify(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a helper routine that allocates a new string value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func String(v string) *string {
|
||||||
|
p := new(string)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int is a helper routine that allocates a new int32 value
|
||||||
|
// to store v and returns a pointer to it, but unlike Int32
|
||||||
|
// its argument value is an int.
|
||||||
|
func Int(v int) *int {
|
||||||
|
p := new(int)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool is a helper routine that allocates a new bool value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Bool(v bool) *bool {
|
||||||
|
p := new(bool)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamToString converts a reader to a string
|
||||||
|
func StreamToString(stream io.Reader) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, _ = buf.ReadFrom(stream)
|
||||||
|
return buf.String()
|
||||||
|
}
|
102
vendor/github.com/digitalocean/godo/image_actions.go
generated
vendored
Normal file
102
vendor/github.com/digitalocean/godo/image_actions.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImageActionsService is an interface for interfacing with the image actions
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#image-actions
|
||||||
|
type ImageActionsService interface {
|
||||||
|
Get(context.Context, int, int) (*Action, *Response, error)
|
||||||
|
Transfer(context.Context, int, *ActionRequest) (*Action, *Response, error)
|
||||||
|
Convert(context.Context, int) (*Action, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageActionsServiceOp handles communition with the image action related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type ImageActionsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ImageActionsService = &ImageActionsServiceOp{}
|
||||||
|
|
||||||
|
// Transfer an image
|
||||||
|
func (i *ImageActionsServiceOp) Transfer(ctx context.Context, imageID int, transferRequest *ActionRequest) (*Action, *Response, error) {
|
||||||
|
if imageID < 1 {
|
||||||
|
return nil, nil, NewArgError("imageID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if transferRequest == nil {
|
||||||
|
return nil, nil, NewArgError("transferRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("v2/images/%d/actions", imageID)
|
||||||
|
|
||||||
|
req, err := i.client.NewRequest(ctx, http.MethodPost, path, transferRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := i.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an image to a snapshot
|
||||||
|
func (i *ImageActionsServiceOp) Convert(ctx context.Context, imageID int) (*Action, *Response, error) {
|
||||||
|
if imageID < 1 {
|
||||||
|
return nil, nil, NewArgError("imageID", "cannont be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("v2/images/%d/actions", imageID)
|
||||||
|
|
||||||
|
convertRequest := &ActionRequest{
|
||||||
|
"type": "convert",
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := i.client.NewRequest(ctx, http.MethodPost, path, convertRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := i.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an action for a particular image by id.
|
||||||
|
func (i *ImageActionsServiceOp) Get(ctx context.Context, imageID, actionID int) (*Action, *Response, error) {
|
||||||
|
if imageID < 1 {
|
||||||
|
return nil, nil, NewArgError("imageID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if actionID < 1 {
|
||||||
|
return nil, nil, NewArgError("actionID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("v2/images/%d/actions/%d", imageID, actionID)
|
||||||
|
|
||||||
|
req, err := i.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := i.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
245
vendor/github.com/digitalocean/godo/images.go
generated
vendored
Normal file
245
vendor/github.com/digitalocean/godo/images.go
generated
vendored
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const imageBasePath = "v2/images"
|
||||||
|
|
||||||
|
// ImagesService is an interface for interfacing with the images
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#images
|
||||||
|
type ImagesService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Image, *Response, error)
|
||||||
|
ListDistribution(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||||
|
ListApplication(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||||
|
ListUser(ctx context.Context, opt *ListOptions) ([]Image, *Response, error)
|
||||||
|
ListByTag(ctx context.Context, tag string, opt *ListOptions) ([]Image, *Response, error)
|
||||||
|
GetByID(context.Context, int) (*Image, *Response, error)
|
||||||
|
GetBySlug(context.Context, string) (*Image, *Response, error)
|
||||||
|
Create(context.Context, *CustomImageCreateRequest) (*Image, *Response, error)
|
||||||
|
Update(context.Context, int, *ImageUpdateRequest) (*Image, *Response, error)
|
||||||
|
Delete(context.Context, int) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImagesServiceOp handles communication with the image related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type ImagesServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ImagesService = &ImagesServiceOp{}
|
||||||
|
|
||||||
|
// Image represents a DigitalOcean Image
|
||||||
|
type Image struct {
|
||||||
|
ID int `json:"id,float64,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Distribution string `json:"distribution,omitempty"`
|
||||||
|
Slug string `json:"slug,omitempty"`
|
||||||
|
Public bool `json:"public,omitempty"`
|
||||||
|
Regions []string `json:"regions,omitempty"`
|
||||||
|
MinDiskSize int `json:"min_disk_size,omitempty"`
|
||||||
|
SizeGigaBytes float64 `json:"size_gigabytes,omitempty"`
|
||||||
|
Created string `json:"created_at,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
ErrorMessage string `json:"error_message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageUpdateRequest represents a request to update an image.
|
||||||
|
type ImageUpdateRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomImageCreateRequest represents a request to create a custom image.
|
||||||
|
type CustomImageCreateRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
Distribution string `json:"distribution,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type imageRoot struct {
|
||||||
|
Image *Image
|
||||||
|
}
|
||||||
|
|
||||||
|
type imagesRoot struct {
|
||||||
|
Images []Image
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type listImageOptions struct {
|
||||||
|
Private bool `url:"private,omitempty"`
|
||||||
|
Type string `url:"type,omitempty"`
|
||||||
|
Tag string `url:"tag_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Image) String() string {
|
||||||
|
return Stringify(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all the images available.
|
||||||
|
func (s *ImagesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
|
return s.list(ctx, opt, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDistribution lists all the distribution images.
|
||||||
|
func (s *ImagesServiceOp) ListDistribution(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
|
listOpt := listImageOptions{Type: "distribution"}
|
||||||
|
return s.list(ctx, opt, &listOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListApplication lists all the application images.
|
||||||
|
func (s *ImagesServiceOp) ListApplication(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
|
listOpt := listImageOptions{Type: "application"}
|
||||||
|
return s.list(ctx, opt, &listOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUser lists all the user images.
|
||||||
|
func (s *ImagesServiceOp) ListUser(ctx context.Context, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
|
listOpt := listImageOptions{Private: true}
|
||||||
|
return s.list(ctx, opt, &listOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByTag lists all images with a specific tag applied.
|
||||||
|
func (s *ImagesServiceOp) ListByTag(ctx context.Context, tag string, opt *ListOptions) ([]Image, *Response, error) {
|
||||||
|
listOpt := listImageOptions{Tag: tag}
|
||||||
|
return s.list(ctx, opt, &listOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByID retrieves an image by id.
|
||||||
|
func (s *ImagesServiceOp) GetByID(ctx context.Context, imageID int) (*Image, *Response, error) {
|
||||||
|
if imageID < 1 {
|
||||||
|
return nil, nil, NewArgError("imageID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.get(ctx, interface{}(imageID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBySlug retrieves an image by slug.
|
||||||
|
func (s *ImagesServiceOp) GetBySlug(ctx context.Context, slug string) (*Image, *Response, error) {
|
||||||
|
if len(slug) < 1 {
|
||||||
|
return nil, nil, NewArgError("slug", "cannot be blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.get(ctx, interface{}(slug))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ImagesServiceOp) Create(ctx context.Context, createRequest *CustomImageCreateRequest) (*Image, *Response, error) {
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, imageBasePath, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(imageRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Image, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update an image name.
|
||||||
|
func (s *ImagesServiceOp) Update(ctx context.Context, imageID int, updateRequest *ImageUpdateRequest) (*Image, *Response, error) {
|
||||||
|
if imageID < 1 {
|
||||||
|
return nil, nil, NewArgError("imageID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if updateRequest == nil {
|
||||||
|
return nil, nil, NewArgError("updateRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPut, path, updateRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(imageRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Image, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete an image.
|
||||||
|
func (s *ImagesServiceOp) Delete(ctx context.Context, imageID int) (*Response, error) {
|
||||||
|
if imageID < 1 {
|
||||||
|
return nil, NewArgError("imageID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method for getting an individual image
|
||||||
|
func (s *ImagesServiceOp) get(ctx context.Context, ID interface{}) (*Image, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%v", imageBasePath, ID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(imageRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Image, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method for listing images
|
||||||
|
func (s *ImagesServiceOp) list(ctx context.Context, opt *ListOptions, listOpt *listImageOptions) ([]Image, *Response, error) {
|
||||||
|
path := imageBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
path, err = addOptions(path, listOpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(imagesRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Images, resp, err
|
||||||
|
}
|
225
vendor/github.com/digitalocean/godo/invoices.go
generated
vendored
Normal file
225
vendor/github.com/digitalocean/godo/invoices.go
generated
vendored
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const invoicesBasePath = "v2/customers/my/invoices"
|
||||||
|
|
||||||
|
// InvoicesService is an interface for interfacing with the Invoice
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#invoices
|
||||||
|
type InvoicesService interface {
|
||||||
|
Get(context.Context, string, *ListOptions) (*Invoice, *Response, error)
|
||||||
|
GetPDF(context.Context, string) ([]byte, *Response, error)
|
||||||
|
GetCSV(context.Context, string) ([]byte, *Response, error)
|
||||||
|
List(context.Context, *ListOptions) (*InvoiceList, *Response, error)
|
||||||
|
GetSummary(context.Context, string) (*InvoiceSummary, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoicesServiceOp handles communication with the Invoice related methods of
|
||||||
|
// the DigitalOcean API.
|
||||||
|
type InvoicesServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ InvoicesService = &InvoicesServiceOp{}
|
||||||
|
|
||||||
|
// Invoice represents a DigitalOcean Invoice
|
||||||
|
type Invoice struct {
|
||||||
|
InvoiceItems []InvoiceItem `json:"invoice_items"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceItem represents a line-item on a DigitalOcean Invoice
|
||||||
|
type InvoiceItem struct {
|
||||||
|
Product string `json:"product"`
|
||||||
|
ResourceID string `json:"resource_id"`
|
||||||
|
ResourceUUID string `json:"resource_uuid"`
|
||||||
|
GroupDescription string `json:"group_description"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Duration string `json:"duration"`
|
||||||
|
DurationUnit string `json:"duration_unit"`
|
||||||
|
StartTime time.Time `json:"start_time"`
|
||||||
|
EndTime time.Time `json:"end_time"`
|
||||||
|
ProjectName string `json:"project_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceList contains a paginated list of all of a customer's invoices.
|
||||||
|
// The InvoicePreview is the month-to-date usage generated by DigitalOcean.
|
||||||
|
type InvoiceList struct {
|
||||||
|
Invoices []InvoiceListItem `json:"invoices"`
|
||||||
|
InvoicePreview InvoiceListItem `json:"invoice_preview"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceListItem contains a small list of information about a customer's invoice.
|
||||||
|
// More information can be found in the Invoice or InvoiceSummary
|
||||||
|
type InvoiceListItem struct {
|
||||||
|
InvoiceUUID string `json:"invoice_uuid"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
InvoicePeriod string `json:"invoice_period"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceSummary contains metadata and summarized usage for an invoice generated by DigitalOcean
|
||||||
|
type InvoiceSummary struct {
|
||||||
|
InvoiceUUID string `json:"invoice_uuid"`
|
||||||
|
BillingPeriod string `json:"billing_period"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
UserBillingAddress Address `json:"user_billing_address"`
|
||||||
|
UserCompany string `json:"user_company"`
|
||||||
|
UserEmail string `json:"user_email"`
|
||||||
|
ProductCharges InvoiceSummaryBreakdown `json:"product_charges"`
|
||||||
|
Overages InvoiceSummaryBreakdown `json:"overages"`
|
||||||
|
Taxes InvoiceSummaryBreakdown `json:"taxes"`
|
||||||
|
CreditsAndAdjustments InvoiceSummaryBreakdown `json:"credits_and_adjustments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address represents the billing address of a customer
|
||||||
|
type Address struct {
|
||||||
|
AddressLine1 string `json:"address_line1"`
|
||||||
|
AddressLine2 string `json:"address_line2"`
|
||||||
|
City string `json:"city"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
PostalCode string `json:"postal_code"`
|
||||||
|
CountryISO2Code string `json:"country_iso2_code"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceSummaryBreakdown is a grouped set of InvoiceItems from an invoice
|
||||||
|
type InvoiceSummaryBreakdown struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Items []InvoiceSummaryBreakdownItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvoiceSummaryBreakdownItem further breaks down the InvoiceSummary by product
|
||||||
|
type InvoiceSummaryBreakdownItem struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Count string `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Invoice) String() string {
|
||||||
|
return Stringify(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get detailed invoice items for an Invoice
|
||||||
|
func (s *InvoicesServiceOp) Get(ctx context.Context, invoiceUUID string, opt *ListOptions) (*Invoice, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", invoicesBasePath, invoiceUUID)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(Invoice)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List invoices for a customer
|
||||||
|
func (s *InvoicesServiceOp) List(ctx context.Context, opt *ListOptions) (*InvoiceList, *Response, error) {
|
||||||
|
path := invoicesBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(InvoiceList)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a summary of metadata and summarized usage for an Invoice
|
||||||
|
func (s *InvoicesServiceOp) GetSummary(ctx context.Context, invoiceUUID string) (*InvoiceSummary, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/summary", invoicesBasePath, invoiceUUID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(InvoiceSummary)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the pdf for an Invoice
|
||||||
|
func (s *InvoicesServiceOp) GetPDF(ctx context.Context, invoiceUUID string) ([]byte, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/pdf", invoicesBasePath, invoiceUUID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var root bytes.Buffer
|
||||||
|
resp, err := s.client.Do(ctx, req, &root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Bytes(), resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the csv for an Invoice
|
||||||
|
func (s *InvoicesServiceOp) GetCSV(ctx context.Context, invoiceUUID string) ([]byte, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/csv", invoicesBasePath, invoiceUUID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var root bytes.Buffer
|
||||||
|
resp, err := s.client.Do(ctx, req, &root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Bytes(), resp, err
|
||||||
|
}
|
230
vendor/github.com/digitalocean/godo/keys.go
generated
vendored
Normal file
230
vendor/github.com/digitalocean/godo/keys.go
generated
vendored
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const keysBasePath = "v2/account/keys"
|
||||||
|
|
||||||
|
// KeysService is an interface for interfacing with the keys
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#keys
|
||||||
|
type KeysService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Key, *Response, error)
|
||||||
|
GetByID(context.Context, int) (*Key, *Response, error)
|
||||||
|
GetByFingerprint(context.Context, string) (*Key, *Response, error)
|
||||||
|
Create(context.Context, *KeyCreateRequest) (*Key, *Response, error)
|
||||||
|
UpdateByID(context.Context, int, *KeyUpdateRequest) (*Key, *Response, error)
|
||||||
|
UpdateByFingerprint(context.Context, string, *KeyUpdateRequest) (*Key, *Response, error)
|
||||||
|
DeleteByID(context.Context, int) (*Response, error)
|
||||||
|
DeleteByFingerprint(context.Context, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysServiceOp handles communication with key related method of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type KeysServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ KeysService = &KeysServiceOp{}
|
||||||
|
|
||||||
|
// Key represents a DigitalOcean Key.
|
||||||
|
type Key struct {
|
||||||
|
ID int `json:"id,float64,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Fingerprint string `json:"fingerprint,omitempty"`
|
||||||
|
PublicKey string `json:"public_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyUpdateRequest represents a request to update a DigitalOcean key.
|
||||||
|
type KeyUpdateRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type keysRoot struct {
|
||||||
|
SSHKeys []Key `json:"ssh_keys"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyRoot struct {
|
||||||
|
SSHKey *Key `json:"ssh_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Key) String() string {
|
||||||
|
return Stringify(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyCreateRequest represents a request to create a new key.
|
||||||
|
type KeyCreateRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
PublicKey string `json:"public_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all keys
|
||||||
|
func (s *KeysServiceOp) List(ctx context.Context, opt *ListOptions) ([]Key, *Response, error) {
|
||||||
|
path := keysBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(keysRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.SSHKeys, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performs a get given a path
|
||||||
|
func (s *KeysServiceOp) get(ctx context.Context, path string) (*Key, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(keyRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.SSHKey, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByID gets a Key by id
|
||||||
|
func (s *KeysServiceOp) GetByID(ctx context.Context, keyID int) (*Key, *Response, error) {
|
||||||
|
if keyID < 1 {
|
||||||
|
return nil, nil, NewArgError("keyID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
|
||||||
|
return s.get(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByFingerprint gets a Key by by fingerprint
|
||||||
|
func (s *KeysServiceOp) GetByFingerprint(ctx context.Context, fingerprint string) (*Key, *Response, error) {
|
||||||
|
if len(fingerprint) < 1 {
|
||||||
|
return nil, nil, NewArgError("fingerprint", "cannot not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
|
||||||
|
return s.get(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a key using a KeyCreateRequest
|
||||||
|
func (s *KeysServiceOp) Create(ctx context.Context, createRequest *KeyCreateRequest) (*Key, *Response, error) {
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, keysBasePath, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(keyRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.SSHKey, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateByID updates a key name by ID.
|
||||||
|
func (s *KeysServiceOp) UpdateByID(ctx context.Context, keyID int, updateRequest *KeyUpdateRequest) (*Key, *Response, error) {
|
||||||
|
if keyID < 1 {
|
||||||
|
return nil, nil, NewArgError("keyID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if updateRequest == nil {
|
||||||
|
return nil, nil, NewArgError("updateRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
|
||||||
|
req, err := s.client.NewRequest(ctx, "PUT", path, updateRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(keyRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.SSHKey, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateByFingerprint updates a key name by fingerprint.
|
||||||
|
func (s *KeysServiceOp) UpdateByFingerprint(ctx context.Context, fingerprint string, updateRequest *KeyUpdateRequest) (*Key, *Response, error) {
|
||||||
|
if len(fingerprint) < 1 {
|
||||||
|
return nil, nil, NewArgError("fingerprint", "cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if updateRequest == nil {
|
||||||
|
return nil, nil, NewArgError("updateRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
|
||||||
|
req, err := s.client.NewRequest(ctx, "PUT", path, updateRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(keyRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.SSHKey, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete key using a path
|
||||||
|
func (s *KeysServiceOp) delete(ctx context.Context, path string) (*Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteByID deletes a key by its id
|
||||||
|
func (s *KeysServiceOp) DeleteByID(ctx context.Context, keyID int) (*Response, error) {
|
||||||
|
if keyID < 1 {
|
||||||
|
return nil, NewArgError("keyID", "cannot be less than 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
|
||||||
|
return s.delete(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteByFingerprint deletes a key by its fingerprint
|
||||||
|
func (s *KeysServiceOp) DeleteByFingerprint(ctx context.Context, fingerprint string) (*Response, error) {
|
||||||
|
if len(fingerprint) < 1 {
|
||||||
|
return nil, NewArgError("fingerprint", "cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
|
||||||
|
return s.delete(ctx, path)
|
||||||
|
}
|
697
vendor/github.com/digitalocean/godo/kubernetes.go
generated
vendored
Normal file
697
vendor/github.com/digitalocean/godo/kubernetes.go
generated
vendored
Normal file
|
@ -0,0 +1,697 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
kubernetesBasePath = "/v2/kubernetes"
|
||||||
|
kubernetesClustersPath = kubernetesBasePath + "/clusters"
|
||||||
|
kubernetesOptionsPath = kubernetesBasePath + "/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KubernetesService is an interface for interfacing with the Kubernetes endpoints
|
||||||
|
// of the DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#kubernetes
|
||||||
|
type KubernetesService interface {
|
||||||
|
Create(context.Context, *KubernetesClusterCreateRequest) (*KubernetesCluster, *Response, error)
|
||||||
|
Get(context.Context, string) (*KubernetesCluster, *Response, error)
|
||||||
|
GetUser(context.Context, string) (*KubernetesClusterUser, *Response, error)
|
||||||
|
GetUpgrades(context.Context, string) ([]*KubernetesVersion, *Response, error)
|
||||||
|
GetKubeConfig(context.Context, string) (*KubernetesClusterConfig, *Response, error)
|
||||||
|
GetCredentials(context.Context, string, *KubernetesClusterCredentialsGetRequest) (*KubernetesClusterCredentials, *Response, error)
|
||||||
|
List(context.Context, *ListOptions) ([]*KubernetesCluster, *Response, error)
|
||||||
|
Update(context.Context, string, *KubernetesClusterUpdateRequest) (*KubernetesCluster, *Response, error)
|
||||||
|
Upgrade(context.Context, string, *KubernetesClusterUpgradeRequest) (*Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
|
||||||
|
CreateNodePool(ctx context.Context, clusterID string, req *KubernetesNodePoolCreateRequest) (*KubernetesNodePool, *Response, error)
|
||||||
|
GetNodePool(ctx context.Context, clusterID, poolID string) (*KubernetesNodePool, *Response, error)
|
||||||
|
ListNodePools(ctx context.Context, clusterID string, opts *ListOptions) ([]*KubernetesNodePool, *Response, error)
|
||||||
|
UpdateNodePool(ctx context.Context, clusterID, poolID string, req *KubernetesNodePoolUpdateRequest) (*KubernetesNodePool, *Response, error)
|
||||||
|
// RecycleNodePoolNodes is DEPRECATED please use DeleteNode
|
||||||
|
// The method will be removed in godo 2.0.
|
||||||
|
RecycleNodePoolNodes(ctx context.Context, clusterID, poolID string, req *KubernetesNodePoolRecycleNodesRequest) (*Response, error)
|
||||||
|
DeleteNodePool(ctx context.Context, clusterID, poolID string) (*Response, error)
|
||||||
|
DeleteNode(ctx context.Context, clusterID, poolID, nodeID string, req *KubernetesNodeDeleteRequest) (*Response, error)
|
||||||
|
|
||||||
|
GetOptions(context.Context) (*KubernetesOptions, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ KubernetesService = &KubernetesServiceOp{}
|
||||||
|
|
||||||
|
// KubernetesServiceOp handles communication with Kubernetes methods of the DigitalOcean API.
|
||||||
|
type KubernetesServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterCreateRequest represents a request to create a Kubernetes cluster.
|
||||||
|
type KubernetesClusterCreateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
RegionSlug string `json:"region,omitempty"`
|
||||||
|
VersionSlug string `json:"version,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
VPCUUID string `json:"vpc_uuid,omitempty"`
|
||||||
|
|
||||||
|
NodePools []*KubernetesNodePoolCreateRequest `json:"node_pools,omitempty"`
|
||||||
|
|
||||||
|
MaintenancePolicy *KubernetesMaintenancePolicy `json:"maintenance_policy"`
|
||||||
|
AutoUpgrade bool `json:"auto_upgrade"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterUpdateRequest represents a request to update a Kubernetes cluster.
|
||||||
|
type KubernetesClusterUpdateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
MaintenancePolicy *KubernetesMaintenancePolicy `json:"maintenance_policy,omitempty"`
|
||||||
|
AutoUpgrade *bool `json:"auto_upgrade,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterUpgradeRequest represents a request to upgrade a Kubernetes cluster.
|
||||||
|
type KubernetesClusterUpgradeRequest struct {
|
||||||
|
VersionSlug string `json:"version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNodePoolCreateRequest represents a request to create a node pool for a
|
||||||
|
// Kubernetes cluster.
|
||||||
|
type KubernetesNodePoolCreateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Size string `json:"size,omitempty"`
|
||||||
|
Count int `json:"count,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
AutoScale bool `json:"auto_scale,omitempty"`
|
||||||
|
MinNodes int `json:"min_nodes,omitempty"`
|
||||||
|
MaxNodes int `json:"max_nodes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNodePoolUpdateRequest represents a request to update a node pool in a
|
||||||
|
// Kubernetes cluster.
|
||||||
|
type KubernetesNodePoolUpdateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Count *int `json:"count,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
AutoScale *bool `json:"auto_scale,omitempty"`
|
||||||
|
MinNodes *int `json:"min_nodes,omitempty"`
|
||||||
|
MaxNodes *int `json:"max_nodes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNodePoolRecycleNodesRequest is DEPRECATED please use DeleteNode
|
||||||
|
// The type will be removed in godo 2.0.
|
||||||
|
type KubernetesNodePoolRecycleNodesRequest struct {
|
||||||
|
Nodes []string `json:"nodes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNodeDeleteRequest is a request to delete a specific node in a node pool.
|
||||||
|
type KubernetesNodeDeleteRequest struct {
|
||||||
|
// Replace will cause a new node to be created to replace the deleted node.
|
||||||
|
Replace bool `json:"replace,omitempty"`
|
||||||
|
|
||||||
|
// SkipDrain skips draining the node before deleting it.
|
||||||
|
SkipDrain bool `json:"skip_drain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterCredentialsGetRequest is a request to get cluster credentials.
|
||||||
|
type KubernetesClusterCredentialsGetRequest struct {
|
||||||
|
ExpirySeconds *int `json:"expiry_seconds,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesCluster represents a Kubernetes cluster.
|
||||||
|
type KubernetesCluster struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
RegionSlug string `json:"region,omitempty"`
|
||||||
|
VersionSlug string `json:"version,omitempty"`
|
||||||
|
ClusterSubnet string `json:"cluster_subnet,omitempty"`
|
||||||
|
ServiceSubnet string `json:"service_subnet,omitempty"`
|
||||||
|
IPv4 string `json:"ipv4,omitempty"`
|
||||||
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
VPCUUID string `json:"vpc_uuid,omitempty"`
|
||||||
|
|
||||||
|
NodePools []*KubernetesNodePool `json:"node_pools,omitempty"`
|
||||||
|
|
||||||
|
MaintenancePolicy *KubernetesMaintenancePolicy `json:"maintenance_policy,omitempty"`
|
||||||
|
AutoUpgrade bool `json:"auto_upgrade,omitempty"`
|
||||||
|
|
||||||
|
Status *KubernetesClusterStatus `json:"status,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterUser represents a Kubernetes cluster user.
|
||||||
|
type KubernetesClusterUser struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Groups []string `json:"groups,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterCredentials represents Kubernetes cluster credentials.
|
||||||
|
type KubernetesClusterCredentials struct {
|
||||||
|
Server string `json:"server"`
|
||||||
|
CertificateAuthorityData []byte `json:"certificate_authority_data"`
|
||||||
|
ClientCertificateData []byte `json:"client_certificate_data"`
|
||||||
|
ClientKeyData []byte `json:"client_key_data"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesMaintenancePolicy is a configuration to set the maintenance window
|
||||||
|
// of a cluster
|
||||||
|
type KubernetesMaintenancePolicy struct {
|
||||||
|
StartTime string `json:"start_time"`
|
||||||
|
Duration string `json:"duration"`
|
||||||
|
Day KubernetesMaintenancePolicyDay `json:"day"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesMaintenancePolicyDay represents the possible days of a maintenance
|
||||||
|
// window
|
||||||
|
type KubernetesMaintenancePolicyDay int
|
||||||
|
|
||||||
|
const (
|
||||||
|
KubernetesMaintenanceDayAny KubernetesMaintenancePolicyDay = iota
|
||||||
|
KubernetesMaintenanceDayMonday
|
||||||
|
KubernetesMaintenanceDayTuesday
|
||||||
|
KubernetesMaintenanceDayWednesday
|
||||||
|
KubernetesMaintenanceDayThursday
|
||||||
|
KubernetesMaintenanceDayFriday
|
||||||
|
KubernetesMaintenanceDaySaturday
|
||||||
|
KubernetesMaintenanceDaySunday
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
days = [...]string{
|
||||||
|
"any",
|
||||||
|
"monday",
|
||||||
|
"tuesday",
|
||||||
|
"wednesday",
|
||||||
|
"thursday",
|
||||||
|
"friday",
|
||||||
|
"saturday",
|
||||||
|
"sunday",
|
||||||
|
}
|
||||||
|
|
||||||
|
toDay = map[string]KubernetesMaintenancePolicyDay{
|
||||||
|
"any": KubernetesMaintenanceDayAny,
|
||||||
|
"monday": KubernetesMaintenanceDayMonday,
|
||||||
|
"tuesday": KubernetesMaintenanceDayTuesday,
|
||||||
|
"wednesday": KubernetesMaintenanceDayWednesday,
|
||||||
|
"thursday": KubernetesMaintenanceDayThursday,
|
||||||
|
"friday": KubernetesMaintenanceDayFriday,
|
||||||
|
"saturday": KubernetesMaintenanceDaySaturday,
|
||||||
|
"sunday": KubernetesMaintenanceDaySunday,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// KubernetesMaintenanceToDay returns the appropriate KubernetesMaintenancePolicyDay for the given string.
|
||||||
|
func KubernetesMaintenanceToDay(day string) (KubernetesMaintenancePolicyDay, error) {
|
||||||
|
d, ok := toDay[day]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unknown day: %q", day)
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KubernetesMaintenancePolicyDay) String() string {
|
||||||
|
if KubernetesMaintenanceDayAny <= k && k <= KubernetesMaintenanceDaySunday {
|
||||||
|
return days[k]
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d !Weekday", k)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KubernetesMaintenancePolicyDay) UnmarshalJSON(data []byte) error {
|
||||||
|
var val string
|
||||||
|
if err := json.Unmarshal(data, &val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := KubernetesMaintenanceToDay(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*k = parsed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KubernetesMaintenancePolicyDay) MarshalJSON() ([]byte, error) {
|
||||||
|
if KubernetesMaintenanceDayAny <= k && k <= KubernetesMaintenanceDaySunday {
|
||||||
|
return json.Marshal(days[k])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("invalid day: %d", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possible states for a cluster.
|
||||||
|
const (
|
||||||
|
KubernetesClusterStatusProvisioning = KubernetesClusterStatusState("provisioning")
|
||||||
|
KubernetesClusterStatusRunning = KubernetesClusterStatusState("running")
|
||||||
|
KubernetesClusterStatusDegraded = KubernetesClusterStatusState("degraded")
|
||||||
|
KubernetesClusterStatusError = KubernetesClusterStatusState("error")
|
||||||
|
KubernetesClusterStatusDeleted = KubernetesClusterStatusState("deleted")
|
||||||
|
KubernetesClusterStatusUpgrading = KubernetesClusterStatusState("upgrading")
|
||||||
|
KubernetesClusterStatusInvalid = KubernetesClusterStatusState("invalid")
|
||||||
|
)
|
||||||
|
|
||||||
|
// KubernetesClusterStatusState represents states for a cluster.
|
||||||
|
type KubernetesClusterStatusState string
|
||||||
|
|
||||||
|
var _ encoding.TextUnmarshaler = (*KubernetesClusterStatusState)(nil)
|
||||||
|
|
||||||
|
// UnmarshalText unmarshals the state.
|
||||||
|
func (s *KubernetesClusterStatusState) UnmarshalText(text []byte) error {
|
||||||
|
switch KubernetesClusterStatusState(strings.ToLower(string(text))) {
|
||||||
|
case KubernetesClusterStatusProvisioning:
|
||||||
|
*s = KubernetesClusterStatusProvisioning
|
||||||
|
case KubernetesClusterStatusRunning:
|
||||||
|
*s = KubernetesClusterStatusRunning
|
||||||
|
case KubernetesClusterStatusDegraded:
|
||||||
|
*s = KubernetesClusterStatusDegraded
|
||||||
|
case KubernetesClusterStatusError:
|
||||||
|
*s = KubernetesClusterStatusError
|
||||||
|
case KubernetesClusterStatusDeleted:
|
||||||
|
*s = KubernetesClusterStatusDeleted
|
||||||
|
case KubernetesClusterStatusUpgrading:
|
||||||
|
*s = KubernetesClusterStatusUpgrading
|
||||||
|
case "", KubernetesClusterStatusInvalid:
|
||||||
|
*s = KubernetesClusterStatusInvalid
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown cluster state %q", string(text))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterStatus describes the status of a cluster.
|
||||||
|
type KubernetesClusterStatus struct {
|
||||||
|
State KubernetesClusterStatusState `json:"state,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNodePool represents a node pool in a Kubernetes cluster.
|
||||||
|
type KubernetesNodePool struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Size string `json:"size,omitempty"`
|
||||||
|
Count int `json:"count,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
AutoScale bool `json:"auto_scale,omitempty"`
|
||||||
|
MinNodes int `json:"min_nodes,omitempty"`
|
||||||
|
MaxNodes int `json:"max_nodes,omitempty"`
|
||||||
|
|
||||||
|
Nodes []*KubernetesNode `json:"nodes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNode represents a Node in a node pool in a Kubernetes cluster.
|
||||||
|
type KubernetesNode struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Status *KubernetesNodeStatus `json:"status,omitempty"`
|
||||||
|
DropletID string `json:"droplet_id,omitempty"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNodeStatus represents the status of a particular Node in a Kubernetes cluster.
|
||||||
|
type KubernetesNodeStatus struct {
|
||||||
|
State string `json:"state,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesOptions represents options available for creating Kubernetes clusters.
|
||||||
|
type KubernetesOptions struct {
|
||||||
|
Versions []*KubernetesVersion `json:"versions,omitempty"`
|
||||||
|
Regions []*KubernetesRegion `json:"regions,omitempty"`
|
||||||
|
Sizes []*KubernetesNodeSize `json:"sizes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesVersion is a DigitalOcean Kubernetes release.
|
||||||
|
type KubernetesVersion struct {
|
||||||
|
Slug string `json:"slug,omitempty"`
|
||||||
|
KubernetesVersion string `json:"kubernetes_version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesNodeSize is a node sizes supported for Kubernetes clusters.
|
||||||
|
type KubernetesNodeSize struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesRegion is a region usable by Kubernetes clusters.
|
||||||
|
type KubernetesRegion struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type kubernetesClustersRoot struct {
|
||||||
|
Clusters []*KubernetesCluster `json:"kubernetes_clusters,omitempty"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type kubernetesClusterRoot struct {
|
||||||
|
Cluster *KubernetesCluster `json:"kubernetes_cluster,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type kubernetesClusterUserRoot struct {
|
||||||
|
User *KubernetesClusterUser `json:"kubernetes_cluster_user,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type kubernetesNodePoolRoot struct {
|
||||||
|
NodePool *KubernetesNodePool `json:"node_pool,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type kubernetesNodePoolsRoot struct {
|
||||||
|
NodePools []*KubernetesNodePool `json:"node_pools,omitempty"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type kubernetesUpgradesRoot struct {
|
||||||
|
AvailableUpgradeVersions []*KubernetesVersion `json:"available_upgrade_versions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the details of a Kubernetes cluster.
|
||||||
|
func (svc *KubernetesServiceOp) Get(ctx context.Context, clusterID string) (*KubernetesCluster, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesClusterRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Cluster, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUser retrieves the details of a Kubernetes cluster user.
|
||||||
|
func (svc *KubernetesServiceOp) GetUser(ctx context.Context, clusterID string) (*KubernetesClusterUser, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/user", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesClusterUserRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.User, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpgrades retrieves versions a Kubernetes cluster can be upgraded to. An
|
||||||
|
// upgrade can be requested using `Upgrade`.
|
||||||
|
func (svc *KubernetesServiceOp) GetUpgrades(ctx context.Context, clusterID string) ([]*KubernetesVersion, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/upgrades", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesUpgradesRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return root.AvailableUpgradeVersions, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a Kubernetes cluster.
|
||||||
|
func (svc *KubernetesServiceOp) Create(ctx context.Context, create *KubernetesClusterCreateRequest) (*KubernetesCluster, *Response, error) {
|
||||||
|
path := kubernetesClustersPath
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, create)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesClusterRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Cluster, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a Kubernetes cluster. There is no way to recover a cluster
|
||||||
|
// once it has been destroyed.
|
||||||
|
func (svc *KubernetesServiceOp) Delete(ctx context.Context, clusterID string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of the Kubernetes clusters visible with the caller's API token.
|
||||||
|
func (svc *KubernetesServiceOp) List(ctx context.Context, opts *ListOptions) ([]*KubernetesCluster, *Response, error) {
|
||||||
|
path := kubernetesClustersPath
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesClustersRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Clusters, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClusterConfig is the content of a Kubernetes config file, which can be
|
||||||
|
// used to interact with your Kubernetes cluster using `kubectl`.
|
||||||
|
// See: https://kubernetes.io/docs/tasks/tools/install-kubectl/
|
||||||
|
type KubernetesClusterConfig struct {
|
||||||
|
KubeconfigYAML []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKubeConfig returns a Kubernetes config file for the specified cluster.
|
||||||
|
func (svc *KubernetesServiceOp) GetKubeConfig(ctx context.Context, clusterID string) (*KubernetesClusterConfig, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/kubeconfig", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
configBytes := bytes.NewBuffer(nil)
|
||||||
|
resp, err := svc.client.Do(ctx, req, configBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
res := &KubernetesClusterConfig{
|
||||||
|
KubeconfigYAML: configBytes.Bytes(),
|
||||||
|
}
|
||||||
|
return res, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCredentials returns a Kubernetes API server credentials for the specified cluster.
|
||||||
|
func (svc *KubernetesServiceOp) GetCredentials(ctx context.Context, clusterID string, get *KubernetesClusterCredentialsGetRequest) (*KubernetesClusterCredentials, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/credentials", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
q := req.URL.Query()
|
||||||
|
if get.ExpirySeconds != nil {
|
||||||
|
q.Add("expiry_seconds", strconv.Itoa(*get.ExpirySeconds))
|
||||||
|
}
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
credentials := new(KubernetesClusterCredentials)
|
||||||
|
resp, err := svc.client.Do(ctx, req, credentials)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return credentials, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates a Kubernetes cluster's properties.
|
||||||
|
func (svc *KubernetesServiceOp) Update(ctx context.Context, clusterID string, update *KubernetesClusterUpdateRequest) (*KubernetesCluster, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, update)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesClusterRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Cluster, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade upgrades a Kubernetes cluster to a new version. Valid upgrade
|
||||||
|
// versions for a given cluster can be retrieved with `GetUpgrades`.
|
||||||
|
func (svc *KubernetesServiceOp) Upgrade(ctx context.Context, clusterID string, upgrade *KubernetesClusterUpgradeRequest) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/upgrade", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, upgrade)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return svc.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateNodePool creates a new node pool in an existing Kubernetes cluster.
|
||||||
|
func (svc *KubernetesServiceOp) CreateNodePool(ctx context.Context, clusterID string, create *KubernetesNodePoolCreateRequest) (*KubernetesNodePool, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/node_pools", kubernetesClustersPath, clusterID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, create)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesNodePoolRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.NodePool, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNodePool retrieves an existing node pool in a Kubernetes cluster.
|
||||||
|
func (svc *KubernetesServiceOp) GetNodePool(ctx context.Context, clusterID, poolID string) (*KubernetesNodePool, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/node_pools/%s", kubernetesClustersPath, clusterID, poolID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesNodePoolRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.NodePool, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNodePools lists all the node pools found in a Kubernetes cluster.
|
||||||
|
func (svc *KubernetesServiceOp) ListNodePools(ctx context.Context, clusterID string, opts *ListOptions) ([]*KubernetesNodePool, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/node_pools", kubernetesClustersPath, clusterID)
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesNodePoolsRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.NodePools, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateNodePool updates the details of an existing node pool.
|
||||||
|
func (svc *KubernetesServiceOp) UpdateNodePool(ctx context.Context, clusterID, poolID string, update *KubernetesNodePoolUpdateRequest) (*KubernetesNodePool, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/node_pools/%s", kubernetesClustersPath, clusterID, poolID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPut, path, update)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesNodePoolRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.NodePool, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecycleNodePoolNodes is DEPRECATED please use DeleteNode
|
||||||
|
// The method will be removed in godo 2.0.
|
||||||
|
func (svc *KubernetesServiceOp) RecycleNodePoolNodes(ctx context.Context, clusterID, poolID string, recycle *KubernetesNodePoolRecycleNodesRequest) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/node_pools/%s/recycle", kubernetesClustersPath, clusterID, poolID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, recycle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteNodePool deletes a node pool, and subsequently all the nodes in that pool.
|
||||||
|
func (svc *KubernetesServiceOp) DeleteNodePool(ctx context.Context, clusterID, poolID string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/node_pools/%s", kubernetesClustersPath, clusterID, poolID)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteNode deletes a specific node in a node pool.
|
||||||
|
func (svc *KubernetesServiceOp) DeleteNode(ctx context.Context, clusterID, poolID, nodeID string, deleteReq *KubernetesNodeDeleteRequest) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/node_pools/%s/nodes/%s", kubernetesClustersPath, clusterID, poolID, nodeID)
|
||||||
|
if deleteReq != nil {
|
||||||
|
v := make(url.Values)
|
||||||
|
if deleteReq.SkipDrain {
|
||||||
|
v.Set("skip_drain", "1")
|
||||||
|
}
|
||||||
|
if deleteReq.Replace {
|
||||||
|
v.Set("replace", "1")
|
||||||
|
}
|
||||||
|
if query := v.Encode(); query != "" {
|
||||||
|
path = path + "?" + query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type kubernetesOptionsRoot struct {
|
||||||
|
Options *KubernetesOptions `json:"options,omitempty"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOptions returns options about the Kubernetes service, such as the versions available for
|
||||||
|
// cluster creation.
|
||||||
|
func (svc *KubernetesServiceOp) GetOptions(ctx context.Context) (*KubernetesOptions, *Response, error) {
|
||||||
|
path := kubernetesOptionsPath
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(kubernetesOptionsRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Options, resp, nil
|
||||||
|
}
|
83
vendor/github.com/digitalocean/godo/links.go
generated
vendored
Normal file
83
vendor/github.com/digitalocean/godo/links.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Links manages links that are returned along with a List
|
||||||
|
type Links struct {
|
||||||
|
Pages *Pages `json:"pages,omitempty"`
|
||||||
|
Actions []LinkAction `json:"actions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pages are pages specified in Links
|
||||||
|
type Pages struct {
|
||||||
|
First string `json:"first,omitempty"`
|
||||||
|
Prev string `json:"prev,omitempty"`
|
||||||
|
Last string `json:"last,omitempty"`
|
||||||
|
Next string `json:"next,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkAction is a pointer to an action
|
||||||
|
type LinkAction struct {
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
Rel string `json:"rel,omitempty"`
|
||||||
|
HREF string `json:"href,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentPage is current page of the list
|
||||||
|
func (l *Links) CurrentPage() (int, error) {
|
||||||
|
return l.Pages.current()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pages) current() (int, error) {
|
||||||
|
switch {
|
||||||
|
case p == nil:
|
||||||
|
return 1, nil
|
||||||
|
case p.Prev == "" && p.Next != "":
|
||||||
|
return 1, nil
|
||||||
|
case p.Prev != "":
|
||||||
|
prevPage, err := pageForURL(p.Prev)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return prevPage + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLastPage returns true if the current page is the last
|
||||||
|
func (l *Links) IsLastPage() bool {
|
||||||
|
if l.Pages == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return l.Pages.isLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pages) isLast() bool {
|
||||||
|
return p.Next == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func pageForURL(urlText string) (int, error) {
|
||||||
|
u, err := url.ParseRequestURI(urlText)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pageStr := u.Query().Get("page")
|
||||||
|
page, err := strconv.Atoi(pageStr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return page, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a link action by id.
|
||||||
|
func (la *LinkAction) Get(ctx context.Context, client *Client) (*Action, *Response, error) {
|
||||||
|
return client.Actions.Get(ctx, la.ID)
|
||||||
|
}
|
324
vendor/github.com/digitalocean/godo/load_balancers.go
generated
vendored
Normal file
324
vendor/github.com/digitalocean/godo/load_balancers.go
generated
vendored
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const loadBalancersBasePath = "/v2/load_balancers"
|
||||||
|
const forwardingRulesPath = "forwarding_rules"
|
||||||
|
|
||||||
|
const dropletsPath = "droplets"
|
||||||
|
|
||||||
|
// LoadBalancersService is an interface for managing load balancers with the DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#load-balancers
|
||||||
|
type LoadBalancersService interface {
|
||||||
|
Get(context.Context, string) (*LoadBalancer, *Response, error)
|
||||||
|
List(context.Context, *ListOptions) ([]LoadBalancer, *Response, error)
|
||||||
|
Create(context.Context, *LoadBalancerRequest) (*LoadBalancer, *Response, error)
|
||||||
|
Update(ctx context.Context, lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error)
|
||||||
|
Delete(ctx context.Context, lbID string) (*Response, error)
|
||||||
|
AddDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error)
|
||||||
|
RemoveDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error)
|
||||||
|
AddForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error)
|
||||||
|
RemoveForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancer represents a DigitalOcean load balancer configuration.
|
||||||
|
// Tags can only be provided upon the creation of a Load Balancer.
|
||||||
|
type LoadBalancer struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
IP string `json:"ip,omitempty"`
|
||||||
|
Algorithm string `json:"algorithm,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Created string `json:"created_at,omitempty"`
|
||||||
|
ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"`
|
||||||
|
HealthCheck *HealthCheck `json:"health_check,omitempty"`
|
||||||
|
StickySessions *StickySessions `json:"sticky_sessions,omitempty"`
|
||||||
|
Region *Region `json:"region,omitempty"`
|
||||||
|
DropletIDs []int `json:"droplet_ids,omitempty"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"`
|
||||||
|
EnableProxyProtocol bool `json:"enable_proxy_protocol,omitempty"`
|
||||||
|
EnableBackendKeepalive bool `json:"enable_backend_keepalive,omitempty"`
|
||||||
|
VPCUUID string `json:"vpc_uuid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a human-readable description of a LoadBalancer.
|
||||||
|
func (l LoadBalancer) String() string {
|
||||||
|
return Stringify(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LoadBalancer) URN() string {
|
||||||
|
return ToURN("LoadBalancer", l.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsRequest creates a LoadBalancerRequest that can be submitted to Update with the current values of the LoadBalancer.
|
||||||
|
// Modifying the returned LoadBalancerRequest will not modify the original LoadBalancer.
|
||||||
|
func (l LoadBalancer) AsRequest() *LoadBalancerRequest {
|
||||||
|
r := LoadBalancerRequest{
|
||||||
|
Name: l.Name,
|
||||||
|
Algorithm: l.Algorithm,
|
||||||
|
ForwardingRules: append([]ForwardingRule(nil), l.ForwardingRules...),
|
||||||
|
DropletIDs: append([]int(nil), l.DropletIDs...),
|
||||||
|
Tag: l.Tag,
|
||||||
|
RedirectHttpToHttps: l.RedirectHttpToHttps,
|
||||||
|
EnableProxyProtocol: l.EnableProxyProtocol,
|
||||||
|
EnableBackendKeepalive: l.EnableBackendKeepalive,
|
||||||
|
HealthCheck: l.HealthCheck,
|
||||||
|
VPCUUID: l.VPCUUID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.HealthCheck != nil {
|
||||||
|
r.HealthCheck = &HealthCheck{}
|
||||||
|
*r.HealthCheck = *l.HealthCheck
|
||||||
|
}
|
||||||
|
if l.StickySessions != nil {
|
||||||
|
r.StickySessions = &StickySessions{}
|
||||||
|
*r.StickySessions = *l.StickySessions
|
||||||
|
}
|
||||||
|
if l.Region != nil {
|
||||||
|
r.Region = l.Region.Slug
|
||||||
|
}
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardingRule represents load balancer forwarding rules.
|
||||||
|
type ForwardingRule struct {
|
||||||
|
EntryProtocol string `json:"entry_protocol,omitempty"`
|
||||||
|
EntryPort int `json:"entry_port,omitempty"`
|
||||||
|
TargetProtocol string `json:"target_protocol,omitempty"`
|
||||||
|
TargetPort int `json:"target_port,omitempty"`
|
||||||
|
CertificateID string `json:"certificate_id,omitempty"`
|
||||||
|
TlsPassthrough bool `json:"tls_passthrough,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a human-readable description of a ForwardingRule.
|
||||||
|
func (f ForwardingRule) String() string {
|
||||||
|
return Stringify(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheck represents optional load balancer health check rules.
|
||||||
|
type HealthCheck struct {
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
CheckIntervalSeconds int `json:"check_interval_seconds,omitempty"`
|
||||||
|
ResponseTimeoutSeconds int `json:"response_timeout_seconds,omitempty"`
|
||||||
|
HealthyThreshold int `json:"healthy_threshold,omitempty"`
|
||||||
|
UnhealthyThreshold int `json:"unhealthy_threshold,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a human-readable description of a HealthCheck.
|
||||||
|
func (h HealthCheck) String() string {
|
||||||
|
return Stringify(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StickySessions represents optional load balancer session affinity rules.
|
||||||
|
type StickySessions struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
CookieName string `json:"cookie_name,omitempty"`
|
||||||
|
CookieTtlSeconds int `json:"cookie_ttl_seconds,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a human-readable description of a StickySessions instance.
|
||||||
|
func (s StickySessions) String() string {
|
||||||
|
return Stringify(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancerRequest represents the configuration to be applied to an existing or a new load balancer.
|
||||||
|
type LoadBalancerRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Algorithm string `json:"algorithm,omitempty"`
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
ForwardingRules []ForwardingRule `json:"forwarding_rules,omitempty"`
|
||||||
|
HealthCheck *HealthCheck `json:"health_check,omitempty"`
|
||||||
|
StickySessions *StickySessions `json:"sticky_sessions,omitempty"`
|
||||||
|
DropletIDs []int `json:"droplet_ids,omitempty"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
RedirectHttpToHttps bool `json:"redirect_http_to_https,omitempty"`
|
||||||
|
EnableProxyProtocol bool `json:"enable_proxy_protocol,omitempty"`
|
||||||
|
EnableBackendKeepalive bool `json:"enable_backend_keepalive,omitempty"`
|
||||||
|
VPCUUID string `json:"vpc_uuid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a human-readable description of a LoadBalancerRequest.
|
||||||
|
func (l LoadBalancerRequest) String() string {
|
||||||
|
return Stringify(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
type forwardingRulesRequest struct {
|
||||||
|
Rules []ForwardingRule `json:"forwarding_rules,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l forwardingRulesRequest) String() string {
|
||||||
|
return Stringify(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dropletIDsRequest struct {
|
||||||
|
IDs []int `json:"droplet_ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l dropletIDsRequest) String() string {
|
||||||
|
return Stringify(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loadBalancersRoot struct {
|
||||||
|
LoadBalancers []LoadBalancer `json:"load_balancers"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type loadBalancerRoot struct {
|
||||||
|
LoadBalancer *LoadBalancer `json:"load_balancer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancersServiceOp handles communication with load balancer-related methods of the DigitalOcean API.
|
||||||
|
type LoadBalancersServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ LoadBalancersService = &LoadBalancersServiceOp{}
|
||||||
|
|
||||||
|
// Get an existing load balancer by its identifier.
|
||||||
|
func (l *LoadBalancersServiceOp) Get(ctx context.Context, lbID string) (*LoadBalancer, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID)
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(loadBalancerRoot)
|
||||||
|
resp, err := l.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.LoadBalancer, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List load balancers, with optional pagination.
|
||||||
|
func (l *LoadBalancersServiceOp) List(ctx context.Context, opt *ListOptions) ([]LoadBalancer, *Response, error) {
|
||||||
|
path, err := addOptions(loadBalancersBasePath, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(loadBalancersRoot)
|
||||||
|
resp, err := l.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.LoadBalancers, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new load balancer with a given configuration.
|
||||||
|
func (l *LoadBalancersServiceOp) Create(ctx context.Context, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) {
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodPost, loadBalancersBasePath, lbr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(loadBalancerRoot)
|
||||||
|
resp, err := l.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.LoadBalancer, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update an existing load balancer with new configuration.
|
||||||
|
func (l *LoadBalancersServiceOp) Update(ctx context.Context, lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, lbID)
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, "PUT", path, lbr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(loadBalancerRoot)
|
||||||
|
resp, err := l.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.LoadBalancer, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a load balancer by its identifier.
|
||||||
|
func (l *LoadBalancersServiceOp) Delete(ctx context.Context, ldID string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", loadBalancersBasePath, ldID)
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDroplets adds droplets to a load balancer.
|
||||||
|
func (l *LoadBalancersServiceOp) AddDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath)
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodPost, path, &dropletIDsRequest{IDs: dropletIDs})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveDroplets removes droplets from a load balancer.
|
||||||
|
func (l *LoadBalancersServiceOp) RemoveDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, dropletsPath)
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodDelete, path, &dropletIDsRequest{IDs: dropletIDs})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddForwardingRules adds forwarding rules to a load balancer.
|
||||||
|
func (l *LoadBalancersServiceOp) AddForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath)
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodPost, path, &forwardingRulesRequest{Rules: rules})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveForwardingRules removes forwarding rules from a load balancer.
|
||||||
|
func (l *LoadBalancersServiceOp) RemoveForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, lbID, forwardingRulesPath)
|
||||||
|
|
||||||
|
req, err := l.client.NewRequest(ctx, http.MethodDelete, path, &forwardingRulesRequest{Rules: rules})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.client.Do(ctx, req, nil)
|
||||||
|
}
|
6
vendor/github.com/digitalocean/godo/meta.go
generated
vendored
Normal file
6
vendor/github.com/digitalocean/godo/meta.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
// Meta describes generic information about a response.
|
||||||
|
type Meta struct {
|
||||||
|
Total int `json:"total"`
|
||||||
|
}
|
310
vendor/github.com/digitalocean/godo/projects.go
generated
vendored
Normal file
310
vendor/github.com/digitalocean/godo/projects.go
generated
vendored
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultProject is the ID you should use if you are working with your
|
||||||
|
// default project.
|
||||||
|
DefaultProject = "default"
|
||||||
|
|
||||||
|
projectsBasePath = "/v2/projects"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProjectsService is an interface for creating and managing Projects with the DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#projects
|
||||||
|
type ProjectsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Project, *Response, error)
|
||||||
|
GetDefault(context.Context) (*Project, *Response, error)
|
||||||
|
Get(context.Context, string) (*Project, *Response, error)
|
||||||
|
Create(context.Context, *CreateProjectRequest) (*Project, *Response, error)
|
||||||
|
Update(context.Context, string, *UpdateProjectRequest) (*Project, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
|
||||||
|
ListResources(context.Context, string, *ListOptions) ([]ProjectResource, *Response, error)
|
||||||
|
AssignResources(context.Context, string, ...interface{}) ([]ProjectResource, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectsServiceOp handles communication with Projects methods of the DigitalOcean API.
|
||||||
|
type ProjectsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project represents a DigitalOcean Project configuration.
|
||||||
|
type Project struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
OwnerUUID string `json:"owner_uuid"`
|
||||||
|
OwnerID uint64 `json:"owner_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Purpose string `json:"purpose"`
|
||||||
|
Environment string `json:"environment"`
|
||||||
|
IsDefault bool `json:"is_default"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a human-readable description of a Project.
|
||||||
|
func (p Project) String() string {
|
||||||
|
return Stringify(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProjectRequest represents the request to create a new project.
|
||||||
|
type CreateProjectRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Purpose string `json:"purpose"`
|
||||||
|
Environment string `json:"environment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProjectRequest represents the request to update project information.
|
||||||
|
// This type expects certain attribute types, but is built this way to allow
|
||||||
|
// nil values as well. See `updateProjectRequest` for the "real" types.
|
||||||
|
type UpdateProjectRequest struct {
|
||||||
|
Name interface{}
|
||||||
|
Description interface{}
|
||||||
|
Purpose interface{}
|
||||||
|
Environment interface{}
|
||||||
|
IsDefault interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateProjectRequest struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
Purpose *string `json:"purpose"`
|
||||||
|
Environment *string `json:"environment"`
|
||||||
|
IsDefault *bool `json:"is_default"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON takes an UpdateRequest and converts it to the "typed" request
|
||||||
|
// which is sent to the projects API. This is a PATCH request, which allows
|
||||||
|
// partial attributes, so `null` values are OK.
|
||||||
|
func (upr *UpdateProjectRequest) MarshalJSON() ([]byte, error) {
|
||||||
|
d := &updateProjectRequest{}
|
||||||
|
if str, ok := upr.Name.(string); ok {
|
||||||
|
d.Name = &str
|
||||||
|
}
|
||||||
|
if str, ok := upr.Description.(string); ok {
|
||||||
|
d.Description = &str
|
||||||
|
}
|
||||||
|
if str, ok := upr.Purpose.(string); ok {
|
||||||
|
d.Purpose = &str
|
||||||
|
}
|
||||||
|
if str, ok := upr.Environment.(string); ok {
|
||||||
|
d.Environment = &str
|
||||||
|
}
|
||||||
|
if val, ok := upr.IsDefault.(bool); ok {
|
||||||
|
d.IsDefault = &val
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
type assignResourcesRequest struct {
|
||||||
|
Resources []string `json:"resources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectResource is the projects API's representation of a resource.
|
||||||
|
type ProjectResource struct {
|
||||||
|
URN string `json:"urn"`
|
||||||
|
AssignedAt string `json:"assigned_at"`
|
||||||
|
Links *ProjectResourceLinks `json:"links"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjetResourceLinks specify the link for more information about the resource.
|
||||||
|
type ProjectResourceLinks struct {
|
||||||
|
Self string `json:"self"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectsRoot struct {
|
||||||
|
Projects []Project `json:"projects"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectRoot struct {
|
||||||
|
Project *Project `json:"project"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectResourcesRoot struct {
|
||||||
|
Resources []ProjectResource `json:"resources"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProjectsService = &ProjectsServiceOp{}
|
||||||
|
|
||||||
|
// List Projects.
|
||||||
|
func (p *ProjectsServiceOp) List(ctx context.Context, opts *ListOptions) ([]Project, *Response, error) {
|
||||||
|
path, err := addOptions(projectsBasePath, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := p.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(projectsRoot)
|
||||||
|
resp, err := p.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Projects, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefault project.
|
||||||
|
func (p *ProjectsServiceOp) GetDefault(ctx context.Context) (*Project, *Response, error) {
|
||||||
|
return p.getHelper(ctx, "default")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves a single project by its ID.
|
||||||
|
func (p *ProjectsServiceOp) Get(ctx context.Context, projectID string) (*Project, *Response, error) {
|
||||||
|
return p.getHelper(ctx, projectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new project.
|
||||||
|
func (p *ProjectsServiceOp) Create(ctx context.Context, cr *CreateProjectRequest) (*Project, *Response, error) {
|
||||||
|
req, err := p.client.NewRequest(ctx, http.MethodPost, projectsBasePath, cr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(projectRoot)
|
||||||
|
resp, err := p.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Project, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update an existing project.
|
||||||
|
func (p *ProjectsServiceOp) Update(ctx context.Context, projectID string, ur *UpdateProjectRequest) (*Project, *Response, error) {
|
||||||
|
path := path.Join(projectsBasePath, projectID)
|
||||||
|
req, err := p.client.NewRequest(ctx, http.MethodPatch, path, ur)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(projectRoot)
|
||||||
|
resp, err := p.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Project, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete an existing project. You cannot have any resources in a project
|
||||||
|
// before deleting it. See the API documentation for more details.
|
||||||
|
func (p *ProjectsServiceOp) Delete(ctx context.Context, projectID string) (*Response, error) {
|
||||||
|
path := path.Join(projectsBasePath, projectID)
|
||||||
|
req, err := p.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListResources lists all resources in a project.
|
||||||
|
func (p *ProjectsServiceOp) ListResources(ctx context.Context, projectID string, opts *ListOptions) ([]ProjectResource, *Response, error) {
|
||||||
|
basePath := path.Join(projectsBasePath, projectID, "resources")
|
||||||
|
path, err := addOptions(basePath, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := p.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(projectResourcesRoot)
|
||||||
|
resp, err := p.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Resources, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignResources assigns one or more resources to a project. AssignResources
|
||||||
|
// accepts resources in two possible formats:
|
||||||
|
|
||||||
|
// 1. The resource type, like `&Droplet{ID: 1}` or `&FloatingIP{IP: "1.2.3.4"}`
|
||||||
|
// 2. A valid DO URN as a string, like "do:droplet:1234"
|
||||||
|
//
|
||||||
|
// There is no unassign. To move a resource to another project, just assign
|
||||||
|
// it to that other project.
|
||||||
|
func (p *ProjectsServiceOp) AssignResources(ctx context.Context, projectID string, resources ...interface{}) ([]ProjectResource, *Response, error) {
|
||||||
|
path := path.Join(projectsBasePath, projectID, "resources")
|
||||||
|
|
||||||
|
ar := &assignResourcesRequest{
|
||||||
|
Resources: make([]string, len(resources)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, resource := range resources {
|
||||||
|
switch resource := resource.(type) {
|
||||||
|
case ResourceWithURN:
|
||||||
|
ar.Resources[i] = resource.URN()
|
||||||
|
case string:
|
||||||
|
ar.Resources[i] = resource
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("%T must either be a string or have a valid URN method", resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req, err := p.client.NewRequest(ctx, http.MethodPost, path, ar)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(projectResourcesRoot)
|
||||||
|
resp, err := p.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Resources, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProjectsServiceOp) getHelper(ctx context.Context, projectID string) (*Project, *Response, error) {
|
||||||
|
path := path.Join(projectsBasePath, projectID)
|
||||||
|
|
||||||
|
req, err := p.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(projectRoot)
|
||||||
|
resp, err := p.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Project, resp, err
|
||||||
|
}
|
68
vendor/github.com/digitalocean/godo/regions.go
generated
vendored
Normal file
68
vendor/github.com/digitalocean/godo/regions.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegionsService is an interface for interfacing with the regions
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#regions
|
||||||
|
type RegionsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Region, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegionsServiceOp handles communication with the region related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type RegionsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RegionsService = &RegionsServiceOp{}
|
||||||
|
|
||||||
|
// Region represents a DigitalOcean Region
|
||||||
|
type Region struct {
|
||||||
|
Slug string `json:"slug,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Sizes []string `json:"sizes,omitempty"`
|
||||||
|
Available bool `json:"available,omitempty"`
|
||||||
|
Features []string `json:"features,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type regionsRoot struct {
|
||||||
|
Regions []Region
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Region) String() string {
|
||||||
|
return Stringify(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all regions
|
||||||
|
func (s *RegionsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Region, *Response, error) {
|
||||||
|
path := "v2/regions"
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(regionsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Regions, resp, err
|
||||||
|
}
|
253
vendor/github.com/digitalocean/godo/registry.go
generated
vendored
Normal file
253
vendor/github.com/digitalocean/godo/registry.go
generated
vendored
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
registryPath = "/v2/registry"
|
||||||
|
// RegistryServer is the hostname of the DigitalOcean registry service
|
||||||
|
RegistryServer = "registry.digitalocean.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegistryService is an interface for interfacing with the Registry endpoints
|
||||||
|
// of the DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#registry
|
||||||
|
type RegistryService interface {
|
||||||
|
Create(context.Context, *RegistryCreateRequest) (*Registry, *Response, error)
|
||||||
|
Get(context.Context) (*Registry, *Response, error)
|
||||||
|
Delete(context.Context) (*Response, error)
|
||||||
|
DockerCredentials(context.Context, *RegistryDockerCredentialsRequest) (*DockerCredentials, *Response, error)
|
||||||
|
ListRepositories(context.Context, string, *ListOptions) ([]*Repository, *Response, error)
|
||||||
|
ListRepositoryTags(context.Context, string, string, *ListOptions) ([]*RepositoryTag, *Response, error)
|
||||||
|
DeleteTag(context.Context, string, string, string) (*Response, error)
|
||||||
|
DeleteManifest(context.Context, string, string, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RegistryService = &RegistryServiceOp{}
|
||||||
|
|
||||||
|
// RegistryServiceOp handles communication with Registry methods of the DigitalOcean API.
|
||||||
|
type RegistryServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryCreateRequest represents a request to create a registry.
|
||||||
|
type RegistryCreateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistryDockerCredentialsRequest represents a request to retrieve docker
|
||||||
|
// credentials for a registry.
|
||||||
|
type RegistryDockerCredentialsRequest struct {
|
||||||
|
ReadWrite bool `json:"read_write"`
|
||||||
|
ExpirySeconds *int `json:"expiry_seconds,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registry represents a registry.
|
||||||
|
type Registry struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repository represents a repository
|
||||||
|
type Repository struct {
|
||||||
|
RegistryName string `json:"registry_name,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
LatestTag *RepositoryTag `json:"latest_tag,omitempty"`
|
||||||
|
TagCount uint64 `json:"tag_count,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepositoryTag represents a repository tag
|
||||||
|
type RepositoryTag struct {
|
||||||
|
RegistryName string `json:"registry_name,omitempty"`
|
||||||
|
Repository string `json:"repository,omitempty"`
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
ManifestDigest string `json:"manifest_digest,omitempty"`
|
||||||
|
CompressedSizeBytes uint64 `json:"compressed_size_bytes,omitempty"`
|
||||||
|
SizeBytes uint64 `json:"size_bytes,omitempty"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type registryRoot struct {
|
||||||
|
Registry *Registry `json:"registry,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type repositoriesRoot struct {
|
||||||
|
Repositories []*Repository `json:"repositories,omitempty"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type repositoryTagsRoot struct {
|
||||||
|
Tags []*RepositoryTag `json:"tags,omitempty"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the details of a Registry.
|
||||||
|
func (svc *RegistryServiceOp) Get(ctx context.Context) (*Registry, *Response, error) {
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, registryPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(registryRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Registry, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a registry.
|
||||||
|
func (svc *RegistryServiceOp) Create(ctx context.Context, create *RegistryCreateRequest) (*Registry, *Response, error) {
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, registryPath, create)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(registryRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Registry, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a registry. There is no way to recover a registry once it has
|
||||||
|
// been destroyed.
|
||||||
|
func (svc *RegistryServiceOp) Delete(ctx context.Context) (*Response, error) {
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, registryPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerCredentials is the content of a Docker config file
|
||||||
|
// that is used by the docker CLI
|
||||||
|
// See: https://docs.docker.com/engine/reference/commandline/cli/#configjson-properties
|
||||||
|
type DockerCredentials struct {
|
||||||
|
DockerConfigJSON []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockerCredentials retrieves a Docker config file containing the registry's credentials.
|
||||||
|
func (svc *RegistryServiceOp) DockerCredentials(ctx context.Context, request *RegistryDockerCredentialsRequest) (*DockerCredentials, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", registryPath, "docker-credentials")
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("read_write", strconv.FormatBool(request.ReadWrite))
|
||||||
|
if request.ExpirySeconds != nil {
|
||||||
|
q.Add("expiry_seconds", strconv.Itoa(*request.ExpirySeconds))
|
||||||
|
}
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
resp, err := svc.client.Do(ctx, req, &buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dc := &DockerCredentials{
|
||||||
|
DockerConfigJSON: buf.Bytes(),
|
||||||
|
}
|
||||||
|
return dc, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepositories returns a list of the Repositories visible with the registry's credentials.
|
||||||
|
func (svc *RegistryServiceOp) ListRepositories(ctx context.Context, registry string, opts *ListOptions) ([]*Repository, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/repositories", registryPath, registry)
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(repositoriesRoot)
|
||||||
|
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Repositories, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepositoryTags returns a list of the RepositoryTags available within the given repository.
|
||||||
|
func (svc *RegistryServiceOp) ListRepositoryTags(ctx context.Context, registry, repository string, opts *ListOptions) ([]*RepositoryTag, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/repositories/%s/tags", registryPath, registry, url.PathEscape(repository))
|
||||||
|
path, err := addOptions(path, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
root := new(repositoryTagsRoot)
|
||||||
|
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Tags, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTag deletes a tag within a given repository.
|
||||||
|
func (svc *RegistryServiceOp) DeleteTag(ctx context.Context, registry, repository, tag string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/repositories/%s/tags/%s", registryPath, registry, url.PathEscape(repository), tag)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteManifest deletes a manifest by its digest within a given repository.
|
||||||
|
func (svc *RegistryServiceOp) DeleteManifest(ctx context.Context, registry, repository, digest string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/repositories/%s/digests/%s", registryPath, registry, url.PathEscape(repository), digest)
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := svc.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
72
vendor/github.com/digitalocean/godo/sizes.go
generated
vendored
Normal file
72
vendor/github.com/digitalocean/godo/sizes.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SizesService is an interface for interfacing with the size
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#sizes
|
||||||
|
type SizesService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Size, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizesServiceOp handles communication with the size related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type SizesServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SizesService = &SizesServiceOp{}
|
||||||
|
|
||||||
|
// Size represents a DigitalOcean Size
|
||||||
|
type Size struct {
|
||||||
|
Slug string `json:"slug,omitempty"`
|
||||||
|
Memory int `json:"memory,omitempty"`
|
||||||
|
Vcpus int `json:"vcpus,omitempty"`
|
||||||
|
Disk int `json:"disk,omitempty"`
|
||||||
|
PriceMonthly float64 `json:"price_monthly,omitempty"`
|
||||||
|
PriceHourly float64 `json:"price_hourly,omitempty"`
|
||||||
|
Regions []string `json:"regions,omitempty"`
|
||||||
|
Available bool `json:"available,omitempty"`
|
||||||
|
Transfer float64 `json:"transfer,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Size) String() string {
|
||||||
|
return Stringify(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sizesRoot struct {
|
||||||
|
Sizes []Size
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all images
|
||||||
|
func (s *SizesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Size, *Response, error) {
|
||||||
|
path := "v2/sizes"
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(sizesRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Sizes, resp, err
|
||||||
|
}
|
142
vendor/github.com/digitalocean/godo/snapshots.go
generated
vendored
Normal file
142
vendor/github.com/digitalocean/godo/snapshots.go
generated
vendored
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const snapshotBasePath = "v2/snapshots"
|
||||||
|
|
||||||
|
// SnapshotsService is an interface for interfacing with the snapshots
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#snapshots
|
||||||
|
type SnapshotsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Snapshot, *Response, error)
|
||||||
|
ListVolume(context.Context, *ListOptions) ([]Snapshot, *Response, error)
|
||||||
|
ListDroplet(context.Context, *ListOptions) ([]Snapshot, *Response, error)
|
||||||
|
Get(context.Context, string) (*Snapshot, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotsServiceOp handles communication with the snapshot related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type SnapshotsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SnapshotsService = &SnapshotsServiceOp{}
|
||||||
|
|
||||||
|
// Snapshot represents a DigitalOcean Snapshot
|
||||||
|
type Snapshot struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ResourceID string `json:"resource_id,omitempty"`
|
||||||
|
ResourceType string `json:"resource_type,omitempty"`
|
||||||
|
Regions []string `json:"regions,omitempty"`
|
||||||
|
MinDiskSize int `json:"min_disk_size,omitempty"`
|
||||||
|
SizeGigaBytes float64 `json:"size_gigabytes,omitempty"`
|
||||||
|
Created string `json:"created_at,omitempty"`
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type snapshotRoot struct {
|
||||||
|
Snapshot *Snapshot `json:"snapshot"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type snapshotsRoot struct {
|
||||||
|
Snapshots []Snapshot `json:"snapshots"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
Meta *Meta `json:"meta,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type listSnapshotOptions struct {
|
||||||
|
ResourceType string `url:"resource_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Snapshot) String() string {
|
||||||
|
return Stringify(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all the snapshots available.
|
||||||
|
func (s *SnapshotsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||||
|
return s.list(ctx, opt, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDroplet lists all the Droplet snapshots.
|
||||||
|
func (s *SnapshotsServiceOp) ListDroplet(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||||
|
listOpt := listSnapshotOptions{ResourceType: "droplet"}
|
||||||
|
return s.list(ctx, opt, &listOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVolume lists all the volume snapshots.
|
||||||
|
func (s *SnapshotsServiceOp) ListVolume(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||||
|
listOpt := listSnapshotOptions{ResourceType: "volume"}
|
||||||
|
return s.list(ctx, opt, &listOpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves an snapshot by id.
|
||||||
|
func (s *SnapshotsServiceOp) Get(ctx context.Context, snapshotID string) (*Snapshot, *Response, error) {
|
||||||
|
return s.get(ctx, snapshotID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete an snapshot.
|
||||||
|
func (s *SnapshotsServiceOp) Delete(ctx context.Context, snapshotID string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", snapshotBasePath, snapshotID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method for getting an individual snapshot
|
||||||
|
func (s *SnapshotsServiceOp) get(ctx context.Context, ID string) (*Snapshot, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", snapshotBasePath, ID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(snapshotRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Snapshot, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method for listing snapshots
|
||||||
|
func (s *SnapshotsServiceOp) list(ctx context.Context, opt *ListOptions, listOpt *listSnapshotOptions) ([]Snapshot, *Response, error) {
|
||||||
|
path := snapshotBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
path, err = addOptions(path, listOpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(snapshotsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Snapshots, resp, err
|
||||||
|
}
|
261
vendor/github.com/digitalocean/godo/storage.go
generated
vendored
Normal file
261
vendor/github.com/digitalocean/godo/storage.go
generated
vendored
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
storageBasePath = "v2"
|
||||||
|
storageAllocPath = storageBasePath + "/volumes"
|
||||||
|
storageSnapPath = storageBasePath + "/snapshots"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StorageService is an interface for interfacing with the storage
|
||||||
|
// endpoints of the Digital Ocean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2/#block-storage
|
||||||
|
type StorageService interface {
|
||||||
|
ListVolumes(context.Context, *ListVolumeParams) ([]Volume, *Response, error)
|
||||||
|
GetVolume(context.Context, string) (*Volume, *Response, error)
|
||||||
|
CreateVolume(context.Context, *VolumeCreateRequest) (*Volume, *Response, error)
|
||||||
|
DeleteVolume(context.Context, string) (*Response, error)
|
||||||
|
ListSnapshots(ctx context.Context, volumeID string, opts *ListOptions) ([]Snapshot, *Response, error)
|
||||||
|
GetSnapshot(context.Context, string) (*Snapshot, *Response, error)
|
||||||
|
CreateSnapshot(context.Context, *SnapshotCreateRequest) (*Snapshot, *Response, error)
|
||||||
|
DeleteSnapshot(context.Context, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageServiceOp handles communication with the storage volumes related methods of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type StorageServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVolumeParams stores the options you can set for a ListVolumeCall
|
||||||
|
type ListVolumeParams struct {
|
||||||
|
Region string `json:"region"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ListOptions *ListOptions `json:"list_options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ StorageService = &StorageServiceOp{}
|
||||||
|
|
||||||
|
// Volume represents a Digital Ocean block store volume.
|
||||||
|
type Volume struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Region *Region `json:"region"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SizeGigaBytes int64 `json:"size_gigabytes"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
DropletIDs []int `json:"droplet_ids"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
FilesystemType string `json:"filesystem_type"`
|
||||||
|
FilesystemLabel string `json:"filesystem_label"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Volume) String() string {
|
||||||
|
return Stringify(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Volume) URN() string {
|
||||||
|
return ToURN("Volume", f.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
type storageVolumesRoot struct {
|
||||||
|
Volumes []Volume `json:"volumes"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type storageVolumeRoot struct {
|
||||||
|
Volume *Volume `json:"volume"`
|
||||||
|
Links *Links `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeCreateRequest represents a request to create a block store
|
||||||
|
// volume.
|
||||||
|
type VolumeCreateRequest struct {
|
||||||
|
Region string `json:"region"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
SizeGigaBytes int64 `json:"size_gigabytes"`
|
||||||
|
SnapshotID string `json:"snapshot_id"`
|
||||||
|
FilesystemType string `json:"filesystem_type"`
|
||||||
|
FilesystemLabel string `json:"filesystem_label"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVolumes lists all storage volumes.
|
||||||
|
func (svc *StorageServiceOp) ListVolumes(ctx context.Context, params *ListVolumeParams) ([]Volume, *Response, error) {
|
||||||
|
path := storageAllocPath
|
||||||
|
if params != nil {
|
||||||
|
if params.Region != "" && params.Name != "" {
|
||||||
|
path = fmt.Sprintf("%s?name=%s®ion=%s", path, params.Name, params.Region)
|
||||||
|
} else if params.Region != "" {
|
||||||
|
path = fmt.Sprintf("%s?region=%s", path, params.Region)
|
||||||
|
} else if params.Name != "" {
|
||||||
|
path = fmt.Sprintf("%s?name=%s", path, params.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.ListOptions != nil {
|
||||||
|
var err error
|
||||||
|
path, err = addOptions(path, params.ListOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(storageVolumesRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Volumes, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateVolume creates a storage volume. The name must be unique.
|
||||||
|
func (svc *StorageServiceOp) CreateVolume(ctx context.Context, createRequest *VolumeCreateRequest) (*Volume, *Response, error) {
|
||||||
|
path := storageAllocPath
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(storageVolumeRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Volume, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVolume retrieves an individual storage volume.
|
||||||
|
func (svc *StorageServiceOp) GetVolume(ctx context.Context, id string) (*Volume, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", storageAllocPath, id)
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(storageVolumeRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Volume, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVolume deletes a storage volume.
|
||||||
|
func (svc *StorageServiceOp) DeleteVolume(ctx context.Context, id string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", storageAllocPath, id)
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return svc.client.Do(ctx, req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotCreateRequest represents a request to create a block store
|
||||||
|
// volume.
|
||||||
|
type SnapshotCreateRequest struct {
|
||||||
|
VolumeID string `json:"volume_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSnapshots lists all snapshots related to a storage volume.
|
||||||
|
func (svc *StorageServiceOp) ListSnapshots(ctx context.Context, volumeID string, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, volumeID)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(snapshotsRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Snapshots, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSnapshot creates a snapshot of a storage volume.
|
||||||
|
func (svc *StorageServiceOp) CreateSnapshot(ctx context.Context, createRequest *SnapshotCreateRequest) (*Snapshot, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, createRequest.VolumeID)
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(snapshotRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return root.Snapshot, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSnapshot retrieves an individual snapshot.
|
||||||
|
func (svc *StorageServiceOp) GetSnapshot(ctx context.Context, id string) (*Snapshot, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", storageSnapPath, id)
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(snapshotRoot)
|
||||||
|
resp, err := svc.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Snapshot, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSnapshot deletes a snapshot.
|
||||||
|
func (svc *StorageServiceOp) DeleteSnapshot(ctx context.Context, id string) (*Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", storageSnapPath, id)
|
||||||
|
|
||||||
|
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return svc.client.Do(ctx, req, nil)
|
||||||
|
}
|
132
vendor/github.com/digitalocean/godo/storage_actions.go
generated
vendored
Normal file
132
vendor/github.com/digitalocean/godo/storage_actions.go
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StorageActionsService is an interface for interfacing with the
|
||||||
|
// storage actions endpoints of the Digital Ocean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#storage-actions
|
||||||
|
type StorageActionsService interface {
|
||||||
|
Attach(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error)
|
||||||
|
DetachByDropletID(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error)
|
||||||
|
Get(ctx context.Context, volumeID string, actionID int) (*Action, *Response, error)
|
||||||
|
List(ctx context.Context, volumeID string, opt *ListOptions) ([]Action, *Response, error)
|
||||||
|
Resize(ctx context.Context, volumeID string, sizeGigabytes int, regionSlug string) (*Action, *Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageActionsServiceOp handles communication with the storage volumes
|
||||||
|
// action related methods of the DigitalOcean API.
|
||||||
|
type StorageActionsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageAttachment represents the attachement of a block storage
|
||||||
|
// volume to a specific Droplet under the device name.
|
||||||
|
type StorageAttachment struct {
|
||||||
|
DropletID int `json:"droplet_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach a storage volume to a Droplet.
|
||||||
|
func (s *StorageActionsServiceOp) Attach(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": "attach",
|
||||||
|
"droplet_id": dropletID,
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, volumeID, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachByDropletID a storage volume from a Droplet by Droplet ID.
|
||||||
|
func (s *StorageActionsServiceOp) DetachByDropletID(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": "detach",
|
||||||
|
"droplet_id": dropletID,
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, volumeID, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an action for a particular storage volume by id.
|
||||||
|
func (s *StorageActionsServiceOp) Get(ctx context.Context, volumeID string, actionID int) (*Action, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%d", storageAllocationActionPath(volumeID), actionID)
|
||||||
|
return s.get(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List the actions for a particular storage volume.
|
||||||
|
func (s *StorageActionsServiceOp) List(ctx context.Context, volumeID string, opt *ListOptions) ([]Action, *Response, error) {
|
||||||
|
path := storageAllocationActionPath(volumeID)
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.list(ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize a storage volume.
|
||||||
|
func (s *StorageActionsServiceOp) Resize(ctx context.Context, volumeID string, sizeGigabytes int, regionSlug string) (*Action, *Response, error) {
|
||||||
|
request := &ActionRequest{
|
||||||
|
"type": "resize",
|
||||||
|
"size_gigabytes": sizeGigabytes,
|
||||||
|
"region": regionSlug,
|
||||||
|
}
|
||||||
|
return s.doAction(ctx, volumeID, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageActionsServiceOp) doAction(ctx context.Context, volumeID string, request *ActionRequest) (*Action, *Response, error) {
|
||||||
|
path := storageAllocationActionPath(volumeID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Event, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageActionsServiceOp) list(ctx context.Context, path string) ([]Action, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(actionsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Actions, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func storageAllocationActionPath(volumeID string) string {
|
||||||
|
return fmt.Sprintf("%s/%s/actions", storageAllocPath, volumeID)
|
||||||
|
}
|
102
vendor/github.com/digitalocean/godo/strings.go
generated
vendored
Normal file
102
vendor/github.com/digitalocean/godo/strings.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var timestampType = reflect.TypeOf(Timestamp{})
|
||||||
|
|
||||||
|
type ResourceWithURN interface {
|
||||||
|
URN() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToURN converts the resource type and ID to a valid DO API URN.
|
||||||
|
func ToURN(resourceType string, id interface{}) string {
|
||||||
|
return fmt.Sprintf("%s:%s:%v", "do", strings.ToLower(resourceType), id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stringify attempts to create a string representation of DigitalOcean types
|
||||||
|
func Stringify(message interface{}) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
v := reflect.ValueOf(message)
|
||||||
|
stringifyValue(&buf, v)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringifyValue was graciously cargoculted from the goprotubuf library
|
||||||
|
func stringifyValue(w io.Writer, val reflect.Value) {
|
||||||
|
if val.Kind() == reflect.Ptr && val.IsNil() {
|
||||||
|
_, _ = w.Write([]byte("<nil>"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.Indirect(val)
|
||||||
|
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
fmt.Fprintf(w, `"%s"`, v)
|
||||||
|
case reflect.Slice:
|
||||||
|
stringifySlice(w, v)
|
||||||
|
return
|
||||||
|
case reflect.Struct:
|
||||||
|
stringifyStruct(w, v)
|
||||||
|
default:
|
||||||
|
if v.CanInterface() {
|
||||||
|
fmt.Fprint(w, v.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringifySlice(w io.Writer, v reflect.Value) {
|
||||||
|
_, _ = w.Write([]byte{'['})
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
_, _ = w.Write([]byte{' '})
|
||||||
|
}
|
||||||
|
|
||||||
|
stringifyValue(w, v.Index(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = w.Write([]byte{']'})
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringifyStruct(w io.Writer, v reflect.Value) {
|
||||||
|
if v.Type().Name() != "" {
|
||||||
|
_, _ = w.Write([]byte(v.Type().String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// special handling of Timestamp values
|
||||||
|
if v.Type() == timestampType {
|
||||||
|
fmt.Fprintf(w, "{%s}", v.Interface())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = w.Write([]byte{'{'})
|
||||||
|
|
||||||
|
var sep bool
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
fv := v.Field(i)
|
||||||
|
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sep {
|
||||||
|
_, _ = w.Write([]byte(", "))
|
||||||
|
} else {
|
||||||
|
sep = true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = w.Write([]byte(v.Type().Field(i).Name))
|
||||||
|
_, _ = w.Write([]byte{':'})
|
||||||
|
stringifyValue(w, fv)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = w.Write([]byte{'}'})
|
||||||
|
}
|
247
vendor/github.com/digitalocean/godo/tags.go
generated
vendored
Normal file
247
vendor/github.com/digitalocean/godo/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const tagsBasePath = "v2/tags"
|
||||||
|
|
||||||
|
// TagsService is an interface for interfacing with the tags
|
||||||
|
// endpoints of the DigitalOcean API
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#tags
|
||||||
|
type TagsService interface {
|
||||||
|
List(context.Context, *ListOptions) ([]Tag, *Response, error)
|
||||||
|
Get(context.Context, string) (*Tag, *Response, error)
|
||||||
|
Create(context.Context, *TagCreateRequest) (*Tag, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
|
||||||
|
TagResources(context.Context, string, *TagResourcesRequest) (*Response, error)
|
||||||
|
UntagResources(context.Context, string, *UntagResourcesRequest) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagsServiceOp handles communication with tag related method of the
|
||||||
|
// DigitalOcean API.
|
||||||
|
type TagsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TagsService = &TagsServiceOp{}
|
||||||
|
|
||||||
|
// ResourceType represents a class of resource, currently only droplet are supported
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DropletResourceType holds the string representing our ResourceType of Droplet.
|
||||||
|
DropletResourceType ResourceType = "droplet"
|
||||||
|
// ImageResourceType holds the string representing our ResourceType of Image.
|
||||||
|
ImageResourceType ResourceType = "image"
|
||||||
|
// VolumeResourceType holds the string representing our ResourceType of Volume.
|
||||||
|
VolumeResourceType ResourceType = "volume"
|
||||||
|
// LoadBalancerResourceType holds the string representing our ResourceType of LoadBalancer.
|
||||||
|
LoadBalancerResourceType ResourceType = "load_balancer"
|
||||||
|
// VolumeSnapshotResourceType holds the string representing our ResourceType for storage Snapshots.
|
||||||
|
VolumeSnapshotResourceType ResourceType = "volume_snapshot"
|
||||||
|
// DatabaseResourceType holds the string representing our ResourceType of Database.
|
||||||
|
DatabaseResourceType ResourceType = "database"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Resource represent a single resource for associating/disassociating with tags
|
||||||
|
type Resource struct {
|
||||||
|
ID string `json:"resource_id,omitempty"`
|
||||||
|
Type ResourceType `json:"resource_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaggedResources represent the set of resources a tag is attached to
|
||||||
|
type TaggedResources struct {
|
||||||
|
Count int `json:"count"`
|
||||||
|
LastTaggedURI string `json:"last_tagged_uri,omitempty"`
|
||||||
|
Droplets *TaggedDropletsResources `json:"droplets,omitempty"`
|
||||||
|
Images *TaggedImagesResources `json:"images"`
|
||||||
|
Volumes *TaggedVolumesResources `json:"volumes"`
|
||||||
|
VolumeSnapshots *TaggedVolumeSnapshotsResources `json:"volume_snapshots"`
|
||||||
|
Databases *TaggedDatabasesResources `json:"databases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaggedDropletsResources represent the droplet resources a tag is attached to
|
||||||
|
type TaggedDropletsResources struct {
|
||||||
|
Count int `json:"count,float64,omitempty"`
|
||||||
|
LastTagged *Droplet `json:"last_tagged,omitempty"`
|
||||||
|
LastTaggedURI string `json:"last_tagged_uri,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaggedResourcesData represent the generic resources a tag is attached to
|
||||||
|
type TaggedResourcesData struct {
|
||||||
|
Count int `json:"count,float64,omitempty"`
|
||||||
|
LastTaggedURI string `json:"last_tagged_uri,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaggedImagesResources represent the image resources a tag is attached to
|
||||||
|
type TaggedImagesResources TaggedResourcesData
|
||||||
|
|
||||||
|
// TaggedVolumesResources represent the volume resources a tag is attached to
|
||||||
|
type TaggedVolumesResources TaggedResourcesData
|
||||||
|
|
||||||
|
// TaggedVolumeSnapshotsResources represent the volume snapshot resources a tag is attached to
|
||||||
|
type TaggedVolumeSnapshotsResources TaggedResourcesData
|
||||||
|
|
||||||
|
// TaggedDatabasesResources represent the database resources a tag is attached to
|
||||||
|
type TaggedDatabasesResources TaggedResourcesData
|
||||||
|
|
||||||
|
// Tag represent DigitalOcean tag
|
||||||
|
type Tag struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Resources *TaggedResources `json:"resources,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//TagCreateRequest represents the JSON structure of a request of that type.
|
||||||
|
type TagCreateRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagResourcesRequest represents the JSON structure of a request of that type.
|
||||||
|
type TagResourcesRequest struct {
|
||||||
|
Resources []Resource `json:"resources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UntagResourcesRequest represents the JSON structure of a request of that type.
|
||||||
|
type UntagResourcesRequest struct {
|
||||||
|
Resources []Resource `json:"resources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagsRoot struct {
|
||||||
|
Tags []Tag `json:"tags"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagRoot struct {
|
||||||
|
Tag *Tag `json:"tag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all tags
|
||||||
|
func (s *TagsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Tag, *Response, error) {
|
||||||
|
path := tagsBasePath
|
||||||
|
path, err := addOptions(path, opt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(tagsRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Tags, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a single tag
|
||||||
|
func (s *TagsServiceOp) Get(ctx context.Context, name string) (*Tag, *Response, error) {
|
||||||
|
path := fmt.Sprintf("%s/%s", tagsBasePath, name)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(tagRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Tag, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new tag
|
||||||
|
func (s *TagsServiceOp) Create(ctx context.Context, createRequest *TagCreateRequest) (*Tag, *Response, error) {
|
||||||
|
if createRequest == nil {
|
||||||
|
return nil, nil, NewArgError("createRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, tagsBasePath, createRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(tagRoot)
|
||||||
|
resp, err := s.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.Tag, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete an existing tag
|
||||||
|
func (s *TagsServiceOp) Delete(ctx context.Context, name string) (*Response, error) {
|
||||||
|
if name == "" {
|
||||||
|
return nil, NewArgError("name", "cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", tagsBasePath, name)
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagResources associates resources with a given Tag.
|
||||||
|
func (s *TagsServiceOp) TagResources(ctx context.Context, name string, tagRequest *TagResourcesRequest) (*Response, error) {
|
||||||
|
if name == "" {
|
||||||
|
return nil, NewArgError("name", "cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagRequest == nil {
|
||||||
|
return nil, NewArgError("tagRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/resources", tagsBasePath, name)
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodPost, path, tagRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UntagResources dissociates resources with a given Tag.
|
||||||
|
func (s *TagsServiceOp) UntagResources(ctx context.Context, name string, untagRequest *UntagResourcesRequest) (*Response, error) {
|
||||||
|
if name == "" {
|
||||||
|
return nil, NewArgError("name", "cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if untagRequest == nil {
|
||||||
|
return nil, NewArgError("tagRequest", "cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s/resources", tagsBasePath, name)
|
||||||
|
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, untagRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.client.Do(ctx, req, nil)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
35
vendor/github.com/digitalocean/godo/timestamp.go
generated
vendored
Normal file
35
vendor/github.com/digitalocean/godo/timestamp.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Timestamp represents a time that can be unmarshalled from a JSON string
|
||||||
|
// formatted as either an RFC3339 or Unix timestamp. All
|
||||||
|
// exported methods of time.Time can be called on Timestamp.
|
||||||
|
type Timestamp struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Timestamp) String() string {
|
||||||
|
return t.Time.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
// Time is expected in RFC3339 or Unix format.
|
||||||
|
func (t *Timestamp) UnmarshalJSON(data []byte) error {
|
||||||
|
str := string(data)
|
||||||
|
i, err := strconv.ParseInt(str, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
t.Time = time.Unix(i, 0)
|
||||||
|
} else {
|
||||||
|
t.Time, err = time.Parse(`"`+time.RFC3339+`"`, str)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal reports whether t and u are equal based on time.Equal
|
||||||
|
func (t Timestamp) Equal(u Timestamp) bool {
|
||||||
|
return t.Time.Equal(u.Time)
|
||||||
|
}
|
201
vendor/github.com/digitalocean/godo/vpcs.go
generated
vendored
Normal file
201
vendor/github.com/digitalocean/godo/vpcs.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package godo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const vpcsBasePath = "/v2/vpcs"
|
||||||
|
|
||||||
|
// VPCsService is an interface for managing Virtual Private Cloud configurations with the
|
||||||
|
// DigitalOcean API.
|
||||||
|
// See: https://developers.digitalocean.com/documentation/v2#vpcs
|
||||||
|
type VPCsService interface {
|
||||||
|
Create(context.Context, *VPCCreateRequest) (*VPC, *Response, error)
|
||||||
|
Get(context.Context, string) (*VPC, *Response, error)
|
||||||
|
List(context.Context, *ListOptions) ([]*VPC, *Response, error)
|
||||||
|
Update(context.Context, string, *VPCUpdateRequest) (*VPC, *Response, error)
|
||||||
|
Set(context.Context, string, ...VPCSetField) (*VPC, *Response, error)
|
||||||
|
Delete(context.Context, string) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ VPCsService = &VPCsServiceOp{}
|
||||||
|
|
||||||
|
// VPCsServiceOp interfaces with VPC endpoints in the DigitalOcean API.
|
||||||
|
type VPCsServiceOp struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPCCreateRequest represents a request to create a Virtual Private Cloud.
|
||||||
|
type VPCCreateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
RegionSlug string `json:"region,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
IPRange string `json:"ip_range,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPCUpdateRequest represents a request to update a Virtual Private Cloud.
|
||||||
|
type VPCUpdateRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPCSetField allows one to set individual fields within a VPC configuration.
|
||||||
|
type VPCSetField interface {
|
||||||
|
vpcSetField(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPCSetName is used when one want to set the `name` field of a VPC.
|
||||||
|
// Ex.: VPCs.Set(..., VPCSetName("new-name"))
|
||||||
|
type VPCSetName string
|
||||||
|
|
||||||
|
// VPCSetDescription is used when one want to set the `description` field of a VPC.
|
||||||
|
// Ex.: VPCs.Set(..., VPCSetDescription("vpc description"))
|
||||||
|
type VPCSetDescription string
|
||||||
|
|
||||||
|
// VPC represents a DigitalOcean Virtual Private Cloud configuration.
|
||||||
|
type VPC struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
URN string `json:"urn"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
IPRange string `json:"ip_range,omitempty"`
|
||||||
|
RegionSlug string `json:"region,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
|
Default bool `json:"default,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type vpcRoot struct {
|
||||||
|
VPC *VPC `json:"vpc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type vpcsRoot struct {
|
||||||
|
VPCs []*VPC `json:"vpcs"`
|
||||||
|
Links *Links `json:"links"`
|
||||||
|
Meta *Meta `json:"meta"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the details of a Virtual Private Cloud.
|
||||||
|
func (v *VPCsServiceOp) Get(ctx context.Context, id string) (*VPC, *Response, error) {
|
||||||
|
path := vpcsBasePath + "/" + id
|
||||||
|
req, err := v.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(vpcRoot)
|
||||||
|
resp, err := v.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.VPC, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new Virtual Private Cloud.
|
||||||
|
func (v *VPCsServiceOp) Create(ctx context.Context, create *VPCCreateRequest) (*VPC, *Response, error) {
|
||||||
|
path := vpcsBasePath
|
||||||
|
req, err := v.client.NewRequest(ctx, http.MethodPost, path, create)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(vpcRoot)
|
||||||
|
resp, err := v.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.VPC, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of the caller's VPCs, with optional pagination.
|
||||||
|
func (v *VPCsServiceOp) List(ctx context.Context, opt *ListOptions) ([]*VPC, *Response, error) {
|
||||||
|
path, err := addOptions(vpcsBasePath, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := v.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(vpcsRoot)
|
||||||
|
resp, err := v.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
if l := root.Links; l != nil {
|
||||||
|
resp.Links = l
|
||||||
|
}
|
||||||
|
if m := root.Meta; m != nil {
|
||||||
|
resp.Meta = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.VPCs, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates a Virtual Private Cloud's properties.
|
||||||
|
func (v *VPCsServiceOp) Update(ctx context.Context, id string, update *VPCUpdateRequest) (*VPC, *Response, error) {
|
||||||
|
path := vpcsBasePath + "/" + id
|
||||||
|
req, err := v.client.NewRequest(ctx, http.MethodPut, path, update)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(vpcRoot)
|
||||||
|
resp, err := v.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.VPC, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n VPCSetName) vpcSetField(in map[string]interface{}) {
|
||||||
|
in["name"] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n VPCSetDescription) vpcSetField(in map[string]interface{}) {
|
||||||
|
in["description"] = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set updates specific properties of a Virtual Private Cloud.
|
||||||
|
func (v *VPCsServiceOp) Set(ctx context.Context, id string, fields ...VPCSetField) (*VPC, *Response, error) {
|
||||||
|
path := vpcsBasePath + "/" + id
|
||||||
|
update := make(map[string]interface{}, len(fields))
|
||||||
|
for _, field := range fields {
|
||||||
|
field.vpcSetField(update)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := v.client.NewRequest(ctx, http.MethodPatch, path, update)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
root := new(vpcRoot)
|
||||||
|
resp, err := v.client.Do(ctx, req, root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.VPC, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a Virtual Private Cloud. There is no way to recover a VPC once it has been
|
||||||
|
// destroyed.
|
||||||
|
func (v *VPCsServiceOp) Delete(ctx context.Context, id string) (*Response, error) {
|
||||||
|
path := vpcsBasePath + "/" + id
|
||||||
|
req, err := v.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := v.client.Do(ctx, req, nil)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
27
vendor/github.com/google/go-querystring/LICENSE
generated
vendored
Normal file
27
vendor/github.com/google/go-querystring/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2013 Google. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
320
vendor/github.com/google/go-querystring/query/encode.go
generated
vendored
Normal file
320
vendor/github.com/google/go-querystring/query/encode.go
generated
vendored
Normal file
|
@ -0,0 +1,320 @@
|
||||||
|
// Copyright 2013 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 file.
|
||||||
|
|
||||||
|
// Package query implements encoding of structs into URL query parameters.
|
||||||
|
//
|
||||||
|
// As a simple example:
|
||||||
|
//
|
||||||
|
// type Options struct {
|
||||||
|
// Query string `url:"q"`
|
||||||
|
// ShowAll bool `url:"all"`
|
||||||
|
// Page int `url:"page"`
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// opt := Options{ "foo", true, 2 }
|
||||||
|
// v, _ := query.Values(opt)
|
||||||
|
// fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2"
|
||||||
|
//
|
||||||
|
// The exact mapping between Go values and url.Values is described in the
|
||||||
|
// documentation for the Values() function.
|
||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeType = reflect.TypeOf(time.Time{})
|
||||||
|
|
||||||
|
var encoderType = reflect.TypeOf(new(Encoder)).Elem()
|
||||||
|
|
||||||
|
// Encoder is an interface implemented by any type that wishes to encode
|
||||||
|
// itself into URL values in a non-standard way.
|
||||||
|
type Encoder interface {
|
||||||
|
EncodeValues(key string, v *url.Values) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns the url.Values encoding of v.
|
||||||
|
//
|
||||||
|
// Values expects to be passed a struct, and traverses it recursively using the
|
||||||
|
// following encoding rules.
|
||||||
|
//
|
||||||
|
// Each exported struct field is encoded as a URL parameter unless
|
||||||
|
//
|
||||||
|
// - the field's tag is "-", or
|
||||||
|
// - the field is empty and its tag specifies the "omitempty" option
|
||||||
|
//
|
||||||
|
// The empty values are false, 0, any nil pointer or interface value, any array
|
||||||
|
// slice, map, or string of length zero, and any time.Time that returns true
|
||||||
|
// for IsZero().
|
||||||
|
//
|
||||||
|
// The URL parameter name defaults to the struct field name but can be
|
||||||
|
// specified in the struct field's tag value. The "url" key in the struct
|
||||||
|
// field's tag value is the key name, followed by an optional comma and
|
||||||
|
// options. For example:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field int `url:"-"`
|
||||||
|
//
|
||||||
|
// // Field appears as URL parameter "myName".
|
||||||
|
// Field int `url:"myName"`
|
||||||
|
//
|
||||||
|
// // Field appears as URL parameter "myName" and the field is omitted if
|
||||||
|
// // its value is empty
|
||||||
|
// Field int `url:"myName,omitempty"`
|
||||||
|
//
|
||||||
|
// // Field appears as URL parameter "Field" (the default), but the field
|
||||||
|
// // is skipped if empty. Note the leading comma.
|
||||||
|
// Field int `url:",omitempty"`
|
||||||
|
//
|
||||||
|
// For encoding individual field values, the following type-dependent rules
|
||||||
|
// apply:
|
||||||
|
//
|
||||||
|
// Boolean values default to encoding as the strings "true" or "false".
|
||||||
|
// Including the "int" option signals that the field should be encoded as the
|
||||||
|
// strings "1" or "0".
|
||||||
|
//
|
||||||
|
// time.Time values default to encoding as RFC3339 timestamps. Including the
|
||||||
|
// "unix" option signals that the field should be encoded as a Unix time (see
|
||||||
|
// time.Unix())
|
||||||
|
//
|
||||||
|
// Slice and Array values default to encoding as multiple URL values of the
|
||||||
|
// same name. Including the "comma" option signals that the field should be
|
||||||
|
// encoded as a single comma-delimited value. Including the "space" option
|
||||||
|
// similarly encodes the value as a single space-delimited string. Including
|
||||||
|
// the "semicolon" option will encode the value as a semicolon-delimited string.
|
||||||
|
// Including the "brackets" option signals that the multiple URL values should
|
||||||
|
// have "[]" appended to the value name. "numbered" will append a number to
|
||||||
|
// the end of each incidence of the value name, example:
|
||||||
|
// name0=value0&name1=value1, etc.
|
||||||
|
//
|
||||||
|
// Anonymous struct fields are usually encoded as if their inner exported
|
||||||
|
// fields were fields in the outer struct, subject to the standard Go
|
||||||
|
// visibility rules. An anonymous struct field with a name given in its URL
|
||||||
|
// tag is treated as having that name, rather than being anonymous.
|
||||||
|
//
|
||||||
|
// Non-nil pointer values are encoded as the value pointed to.
|
||||||
|
//
|
||||||
|
// Nested structs are encoded including parent fields in value names for
|
||||||
|
// scoping. e.g:
|
||||||
|
//
|
||||||
|
// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO"
|
||||||
|
//
|
||||||
|
// All other values are encoded using their default string representation.
|
||||||
|
//
|
||||||
|
// Multiple fields that encode to the same URL parameter name will be included
|
||||||
|
// as multiple URL values of the same name.
|
||||||
|
func Values(v interface{}) (url.Values, error) {
|
||||||
|
values := make(url.Values)
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
for val.Kind() == reflect.Ptr {
|
||||||
|
if val.IsNil() {
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == nil {
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if val.Kind() != reflect.Struct {
|
||||||
|
return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
err := reflectValue(values, val, "")
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// reflectValue populates the values parameter from the struct fields in val.
|
||||||
|
// Embedded structs are followed recursively (using the rules defined in the
|
||||||
|
// Values function documentation) breadth-first.
|
||||||
|
func reflectValue(values url.Values, val reflect.Value, scope string) error {
|
||||||
|
var embedded []reflect.Value
|
||||||
|
|
||||||
|
typ := val.Type()
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
sf := typ.Field(i)
|
||||||
|
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sv := val.Field(i)
|
||||||
|
tag := sf.Tag.Get("url")
|
||||||
|
if tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, opts := parseTag(tag)
|
||||||
|
if name == "" {
|
||||||
|
if sf.Anonymous && sv.Kind() == reflect.Struct {
|
||||||
|
// save embedded struct for later processing
|
||||||
|
embedded = append(embedded, sv)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name = sf.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if scope != "" {
|
||||||
|
name = scope + "[" + name + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Contains("omitempty") && isEmptyValue(sv) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sv.Type().Implements(encoderType) {
|
||||||
|
if !reflect.Indirect(sv).IsValid() {
|
||||||
|
sv = reflect.New(sv.Type().Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
m := sv.Interface().(Encoder)
|
||||||
|
if err := m.EncodeValues(name, &values); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array {
|
||||||
|
var del byte
|
||||||
|
if opts.Contains("comma") {
|
||||||
|
del = ','
|
||||||
|
} else if opts.Contains("space") {
|
||||||
|
del = ' '
|
||||||
|
} else if opts.Contains("semicolon") {
|
||||||
|
del = ';'
|
||||||
|
} else if opts.Contains("brackets") {
|
||||||
|
name = name + "[]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if del != 0 {
|
||||||
|
s := new(bytes.Buffer)
|
||||||
|
first := true
|
||||||
|
for i := 0; i < sv.Len(); i++ {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
s.WriteByte(del)
|
||||||
|
}
|
||||||
|
s.WriteString(valueString(sv.Index(i), opts))
|
||||||
|
}
|
||||||
|
values.Add(name, s.String())
|
||||||
|
} else {
|
||||||
|
for i := 0; i < sv.Len(); i++ {
|
||||||
|
k := name
|
||||||
|
if opts.Contains("numbered") {
|
||||||
|
k = fmt.Sprintf("%s%d", name, i)
|
||||||
|
}
|
||||||
|
values.Add(k, valueString(sv.Index(i), opts))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for sv.Kind() == reflect.Ptr {
|
||||||
|
if sv.IsNil() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sv = sv.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if sv.Type() == timeType {
|
||||||
|
values.Add(name, valueString(sv, opts))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sv.Kind() == reflect.Struct {
|
||||||
|
reflectValue(values, sv, name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
values.Add(name, valueString(sv, opts))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range embedded {
|
||||||
|
if err := reflectValue(values, f, scope); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueString returns the string representation of a value.
|
||||||
|
func valueString(v reflect.Value, opts tagOptions) string {
|
||||||
|
for v.Kind() == reflect.Ptr {
|
||||||
|
if v.IsNil() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() == reflect.Bool && opts.Contains("int") {
|
||||||
|
if v.Bool() {
|
||||||
|
return "1"
|
||||||
|
}
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Type() == timeType {
|
||||||
|
t := v.Interface().(time.Time)
|
||||||
|
if opts.Contains("unix") {
|
||||||
|
return strconv.FormatInt(t.Unix(), 10)
|
||||||
|
}
|
||||||
|
return t.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprint(v.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEmptyValue checks if a value should be considered empty for the purposes
|
||||||
|
// of omitting fields with the "omitempty" option.
|
||||||
|
func isEmptyValue(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Interface, reflect.Ptr:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Type() == timeType {
|
||||||
|
return v.Interface().(time.Time).IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagOptions is the string following a comma in a struct field's "url" tag, or
|
||||||
|
// the empty string. It does not include the leading comma.
|
||||||
|
type tagOptions []string
|
||||||
|
|
||||||
|
// parseTag splits a struct field's url tag into its name and comma-separated
|
||||||
|
// options.
|
||||||
|
func parseTag(tag string) (string, tagOptions) {
|
||||||
|
s := strings.Split(tag, ",")
|
||||||
|
return s[0], s[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains checks whether the tagOptions contains the specified option.
|
||||||
|
func (o tagOptions) Contains(option string) bool {
|
||||||
|
for _, s := range o {
|
||||||
|
if s == option {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
|
@ -82,6 +82,8 @@ github.com/davecgh/go-spew/spew
|
||||||
github.com/dgrijalva/jwt-go
|
github.com/dgrijalva/jwt-go
|
||||||
# github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b
|
# github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b
|
||||||
github.com/dgryski/go-sip13
|
github.com/dgryski/go-sip13
|
||||||
|
# github.com/digitalocean/godo v1.37.0
|
||||||
|
github.com/digitalocean/godo
|
||||||
# github.com/edsrzf/mmap-go v1.0.0
|
# github.com/edsrzf/mmap-go v1.0.0
|
||||||
github.com/edsrzf/mmap-go
|
github.com/edsrzf/mmap-go
|
||||||
# github.com/evanphx/json-patch v4.2.0+incompatible
|
# github.com/evanphx/json-patch v4.2.0+incompatible
|
||||||
|
@ -173,6 +175,8 @@ github.com/google/go-cmp/cmp/internal/diff
|
||||||
github.com/google/go-cmp/cmp/internal/flags
|
github.com/google/go-cmp/cmp/internal/flags
|
||||||
github.com/google/go-cmp/cmp/internal/function
|
github.com/google/go-cmp/cmp/internal/function
|
||||||
github.com/google/go-cmp/cmp/internal/value
|
github.com/google/go-cmp/cmp/internal/value
|
||||||
|
# github.com/google/go-querystring v1.0.0
|
||||||
|
github.com/google/go-querystring/query
|
||||||
# github.com/google/gofuzz v1.1.0
|
# github.com/google/gofuzz v1.1.0
|
||||||
github.com/google/gofuzz
|
github.com/google/gofuzz
|
||||||
# github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c
|
# github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c
|
||||||
|
|
Loading…
Reference in a new issue