diff --git a/cmd/drone-autoscaler/main.go b/cmd/drone-autoscaler/main.go index fad0bff1..a4b07c25 100644 --- a/cmd/drone-autoscaler/main.go +++ b/cmd/drone-autoscaler/main.go @@ -14,6 +14,7 @@ import ( "github.com/drone/autoscaler" "github.com/drone/autoscaler/config" "github.com/drone/autoscaler/drivers/amazon" + "github.com/drone/autoscaler/drivers/brightbox" "github.com/drone/autoscaler/drivers/digitalocean" "github.com/drone/autoscaler/drivers/google" "github.com/drone/autoscaler/drivers/hetznercloud" @@ -300,6 +301,17 @@ func setupProvider(c config.Config) (autoscaler.Provider, error) { packet.WithHostname(c.Packet.Hostname), packet.WithTags(c.Packet.Tags...), ), nil + case c.Brightbox.ClientID != "": + return brightbox.New( + brightbox.WithApiURL(c.Brightbox.ApiURL), + brightbox.WithClientID(c.Brightbox.ClientID), + brightbox.WithClientSecret(c.Brightbox.ClientSecret), + brightbox.WithImage(c.Brightbox.Image), + brightbox.WithServerType(c.Brightbox.ServerType), + brightbox.WithServerGroups(c.Brightbox.ServerGroups), + brightbox.WithUserData(c.Brightbox.UserData), + brightbox.WithUserDataFile(c.Brightbox.UserDataFile), + ), nil case os.Getenv("AWS_ACCESS_KEY_ID") != "" || os.Getenv("AWS_IAM") != "": return amazon.New( amazon.WithDeviceName(c.Amazon.DeviceName), diff --git a/config/config.go b/config/config.go index bdd34bcb..8daa33a9 100644 --- a/config/config.go +++ b/config/config.go @@ -143,6 +143,17 @@ type ( MarketType string `envconfig:"DRONE_AMAZON_MARKET_TYPE"` } + Brightbox struct { + ApiURL string `envconfig:"DRONE_BRIGHTBOX_API_URL"` + ClientID string `envconfig:"DRONE_BRIGHTBOX_CLIENT_ID"` + ClientSecret string `envconfig:"DRONE_BRIGHTBOX_CLIENT_SECRET"` + Image string `envconfig:"DRONE_BRIGHTBOX_IMAGE"` + ServerType string `envconfig:"DRONE_BRIGHTBOX_SERVER_TYPE"` + ServerGroups []string `envconfig:"DRONE_BRIGHTBOX_SERVER_GROUPS"` + UserData string `envconfig:"DRONE_BRIGHTBOX_USERDATA"` + UserDataFile string `envconfig:"DRONE_BRIGHTBOX_USERDATA_FILE"` + } + DigitalOcean struct { Token string Image string diff --git a/drivers/brightbox/create.go b/drivers/brightbox/create.go new file mode 100644 index 00000000..32838418 --- /dev/null +++ b/drivers/brightbox/create.go @@ -0,0 +1,119 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox + +import ( + "bytes" + "context" + "encoding/base64" + "errors" + "time" + + "github.com/drone/autoscaler" + "github.com/drone/autoscaler/logger" + + "github.com/brightbox/gobrightbox" +) + +func (p *provider) Create(ctx context.Context, opts autoscaler.InstanceCreateOpts) (*autoscaler.Instance, error) { + p.init.Do(func() { + p.setup(ctx) + }) + + buf := new(bytes.Buffer) + err := p.userdata.Execute(buf, &opts) + if err != nil { + return nil, err + } + + var userdata = new(string) + *userdata = base64.StdEncoding.EncodeToString(buf.Bytes()) + + in := &gobrightbox.ServerOptions{ + Image: p.image, + Name: &opts.Name, + ServerType: p.serverType, + UserData: userdata, + ServerGroups: p.serverGroups, + } + + logger := logger.FromContext(ctx). + WithField("name", opts.Name). + WithField("type", p.serverType). + WithField("image", p.image). + WithField("groups", p.serverGroups) + + logger.Debugln("instance create") + + server, err := p.client.CreateServer(in) + if err != nil { + logger.WithError(err). + Errorln("instance create failed") + return nil, err + } + + // wait for the server to become active + interval := time.Second * 10 +poller: + for { + select { + case <-ctx.Done(): + logger.WithField("ID", server.ID). + Debugln("instance create deadline exceeded") + + return nil, ctx.Err() + case <-time.After(interval): + server, err := p.client.Server(server.ID) + if err != nil { + logger.WithError(err). + Errorln("cannot get instance details") + continue + } + + if server.Status == "active" { + break poller + } else if server.Status == "failed" { + err = errors.New("brightbox: new server entered 'failed' state") + logger.WithError(err). + Errorln("instance create failed") + return nil, err + } + } + } + + logger.Infoln("instance create success") + + cip := &gobrightbox.CloudIPOptions{ + Name: &opts.Name, + } + + logger.Debugln("map Cloud IP") + + cloudip, err := p.client.CreateCloudIP(cip) + if err != nil { + logger.WithError(err). + Errorln("failed to provision CloudIP") + return nil, err + } + + err = p.client.MapCloudIPtoServer(cloudip.ID, server.ID) + if err != nil { + logger.WithError(err). + Errorln("failed to map CloudIP") + return nil, err + } + + logger.Infoln("map Cloud IP success") + + return &autoscaler.Instance{ + Provider: autoscaler.ProviderBrightbox, + Region: p.region, + ID: server.ID, + Name: server.Name, + Address: cloudip.PublicIPv4, + Image: in.Image, + Size: in.ServerType, + }, nil +} diff --git a/drivers/brightbox/create_test.go b/drivers/brightbox/create_test.go new file mode 100644 index 00000000..bf73caff --- /dev/null +++ b/drivers/brightbox/create_test.go @@ -0,0 +1,5 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox diff --git a/drivers/brightbox/destroy.go b/drivers/brightbox/destroy.go new file mode 100644 index 00000000..0f27159e --- /dev/null +++ b/drivers/brightbox/destroy.go @@ -0,0 +1,87 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox + +import ( + "context" + "time" + + "github.com/drone/autoscaler" + "github.com/drone/autoscaler/logger" +) + +func (p *provider) Destroy(ctx context.Context, instance *autoscaler.Instance) error { + logger := logger.FromContext(ctx). + WithField("id", instance.ID). + WithField("name", instance.Name). + WithField("ip", instance.Address) + + logger.Debugln("terminate instance") + + server, err := p.client.Server(instance.ID) + if err != nil { + logger.WithError(err). + Errorln("cannot retrive instance details") + return err + } + + if len(server.CloudIPs) > 0 { + + logger.Debugln("unmap Cloud IP") + + cipID := server.CloudIPs[0].ID + + err := p.client.UnMapCloudIP(cipID) + if err != nil { + logger.WithError(err). + Errorln("failed to unmap Cloud IP") + return err + } + + // wait for CIP to become unmapped + interval := time.Second * 2 + poller: + for { + select { + case <-ctx.Done(): + logger.WithField("ID", server.ID). + Debugln("unmap Cloud IP deadline exceeded") + + return err + case <-time.After(interval): + cip, err := p.client.CloudIP(cipID) + if err != nil { + logger.WithError(err). + Errorln("cannot retrive Cloud IP details") + continue + } + + if cip.Status == "unmapped" { + break poller + } + } + } + + err = p.client.DestroyCloudIP(cipID) + if err != nil { + logger.WithError(err). + Errorln("failed to destroy Cloud IP") + return err + } + } + + logger.Infoln("unmap Cloud IP success") + + err = p.client.DestroyServer(instance.ID) + if err != nil { + logger.WithError(err). + Errorln("terminate instance failed") + return err + } + + logger.Infoln("terminate instance success") + + return nil +} diff --git a/drivers/brightbox/destroy_test.go b/drivers/brightbox/destroy_test.go new file mode 100644 index 00000000..bf73caff --- /dev/null +++ b/drivers/brightbox/destroy_test.go @@ -0,0 +1,5 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox diff --git a/drivers/brightbox/option.go b/drivers/brightbox/option.go new file mode 100644 index 00000000..c0cd8c63 --- /dev/null +++ b/drivers/brightbox/option.go @@ -0,0 +1,88 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox + +import ( + "io/ioutil" + + "github.com/drone/autoscaler/drivers/internal/userdata" +) + +// Option configures a Brightbox provider option. +type Option func(*provider) error + +// WithApiURL returns an option to set the API endpoint URL +func WithApiURL(apiURL string) Option { + return func(p *provider) error { + p.apiURL = apiURL + return nil + } +} + +// WithClientID returns an option to set the API client ID +func WithClientID(clientID string) Option { + return func(p *provider) error { + p.clientID = clientID + return nil + } +} + +// WithClientSecret returns an option to set the API client secret +func WithClientSecret(clientSecret string) Option { + return func(p *provider) error { + p.clientSecret = clientSecret + return nil + } +} + +// WithImage returns an option to set the image. +func WithImage(image string) Option { + return func(p *provider) error { + p.image = image + return nil + } +} + +// WithServerType returns an option to set the server type +func WithServerType(serverType string) Option { + return func(p *provider) error { + p.serverType = serverType + return nil + } +} + +// WithServerGroups returns an option to set the server groups +func WithServerGroups(serverGroups []string) Option { + return func(p *provider) error { + p.serverGroups = serverGroups + return nil + } +} + +// WithUserData returns an option to set the cloud-init +// template from text. +func WithUserData(text string) Option { + return func(p *provider) error { + if text != "" { + p.userdata = userdata.Parse(text) + } + return nil + } +} + +// WithUserDataFile returns an option to set the cloud-init +// template from file. +func WithUserDataFile(filepath string) Option { + return func(p *provider) error { + if filepath != "" { + b, err := ioutil.ReadFile(filepath) + if err != nil { + return err + } + p.userdata = userdata.Parse(string(b)) + } + return nil + } +} diff --git a/drivers/brightbox/option_test.go b/drivers/brightbox/option_test.go new file mode 100644 index 00000000..2eb806f1 --- /dev/null +++ b/drivers/brightbox/option_test.go @@ -0,0 +1,37 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox + +import "testing" + +func TestOptions(t *testing.T) { + p := New( + WithApiURL("https://api.gb1.brightbox.com"), + WithClientID("cli-xxxxx"), + WithClientSecret("supersecret"), + WithImage("img-xxxxx"), + WithServerType("typ-xxxxx"), + WithServerGroups([]string{"grp-aaaaa"}), + ).(*provider) + + if got, want := p.apiURL, "https://api.gb1.brightbox.com"; got != want { + t.Errorf("Want API URL %q, got %q", want, got) + } + if got, want := p.clientID, "cli-xxxxx"; got != want { + t.Errorf("Want client ID %q, got %q", want, got) + } + if got, want := p.clientSecret, "supersecret"; got != want { + t.Errorf("Want client secret %q, got %q", want, got) + } + if got, want := p.image, "img-xxxxx"; got != want { + t.Errorf("Want image %q, got %q", want, got) + } + if got, want := p.serverType, "typ-xxxxx"; got != want { + t.Errorf("Want server type %q, got %q", want, got) + } + if got, want := p.serverGroups[0], "grp-aaaaa"; got != want { + t.Errorf("Want server groups %q, got %q", want, got) + } +} diff --git a/drivers/brightbox/provider.go b/drivers/brightbox/provider.go new file mode 100644 index 00000000..b512cbed --- /dev/null +++ b/drivers/brightbox/provider.go @@ -0,0 +1,58 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox + +import ( + "sync" + "text/template" + + "github.com/brightbox/gobrightbox" + "github.com/drone/autoscaler" + "github.com/drone/autoscaler/drivers/internal/userdata" +) + +type provider struct { + init sync.Once + + region string + + apiURL string + clientID string + clientSecret string + + image string + serverType string + serverGroups []string + userdata *template.Template + + client *gobrightbox.Client +} + +func New(opts ...Option) autoscaler.Provider { + p := new(provider) + for _, opt := range opts { + err := opt(p) + if err != nil { + panic(err) + } + } + + p.region = "gb1" + + if p.apiURL == "" { + p.apiURL = "https://api.gb1.brightbox.com" + } + if p.image == "" { + p.image = "img-sm72o" // ubuntu-jammy-22.04-amd64-server + } + if p.serverType == "" { + p.serverType = "typ-1ni84" // 8gb.ssd-ram-opt + } + if p.userdata == nil { + p.userdata = userdata.T + } + + return p +} diff --git a/drivers/brightbox/provider_test.go b/drivers/brightbox/provider_test.go new file mode 100644 index 00000000..778e5471 --- /dev/null +++ b/drivers/brightbox/provider_test.go @@ -0,0 +1,20 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox + +import "testing" + +func TestDefaults(t *testing.T) { + p := New().(*provider) + if got, want := p.apiURL, "https://api.gb1.brightbox.com"; got != want { + t.Errorf("Want API URL %q, got %q", want, got) + } + if got, want := p.image, "img-sm72o"; got != want { + t.Errorf("Want image %q, got %q", want, got) + } + if got, want := p.serverType, "typ-1ni84"; got != want { + t.Errorf("Want server type %q, got %q", want, got) + } +} diff --git a/drivers/brightbox/setup.go b/drivers/brightbox/setup.go new file mode 100644 index 00000000..5e72747a --- /dev/null +++ b/drivers/brightbox/setup.go @@ -0,0 +1,49 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox + +import ( + "context" + + "github.com/drone/autoscaler/logger" + + "github.com/brightbox/gobrightbox" + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" + "golang.org/x/sync/errgroup" +) + +func (p *provider) setup(ctx context.Context) error { + var g errgroup.Group + if p.client == nil { + g.Go(func() error { + return p.newClient(ctx) + }) + } + return g.Wait() +} + +func (p *provider) newClient(ctx context.Context) error { + logger := logger.FromContext(ctx) + + // Setup OAuth2 authentication + conf := clientcredentials.Config{ + ClientID: p.clientID, + ClientSecret: p.clientSecret, + Scopes: []string{}, + TokenURL: p.apiURL + "/token", + } + oc := conf.Client(oauth2.NoContext) + + // Setup API client + client, err := gobrightbox.NewClient(p.apiURL, "", oc) + if err != nil { + logger.WithError(err).Errorln("unable to connect to Brightbox API") + return err + } + + p.client = client + return nil +} diff --git a/drivers/brightbox/setup_test.go b/drivers/brightbox/setup_test.go new file mode 100644 index 00000000..bf73caff --- /dev/null +++ b/drivers/brightbox/setup_test.go @@ -0,0 +1,5 @@ +// Copyright 2018 Drone.IO Inc +// Use of this source code is governed by the Polyform License +// that can be found in the LICENSE file. + +package brightbox diff --git a/go.mod b/go.mod index 1fecd497..90d846a5 100644 --- a/go.mod +++ b/go.mod @@ -5,23 +5,23 @@ go 1.12 replace github.com/docker/docker => github.com/docker/engine v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible require ( - cloud.google.com/go v0.28.0 // indirect github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Microsoft/go-winio v0.4.7 // indirect - github.com/avast/retry-go v3.0.0+incompatible // indirect + github.com/avast/retry-go v3.0.0+incompatible github.com/aws/aws-sdk-go v1.13.5 github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a // indirect github.com/bluele/slack v0.0.0-20171128075526-307046097ee9 + github.com/brightbox/gobrightbox v0.8.2 github.com/containerd/containerd v1.3.4 // indirect github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 github.com/digitalocean/godo v1.1.1 github.com/docker/distribution v0.0.0-20170726174610-edc3ab29cdff // indirect github.com/docker/docker v0.0.0-00010101000000-000000000000 - github.com/docker/go-connections v0.3.0 // indirect + github.com/docker/go-connections v0.3.0 github.com/docker/go-units v0.4.0 // indirect github.com/drone/drone-go v1.0.5-0.20190504210458-4d6116b897ba github.com/drone/envconfig v1.4.1 @@ -33,10 +33,10 @@ require ( github.com/go-sql-driver/mysql v1.3.0 github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506 // indirect github.com/golang/mock v1.3.1 - github.com/google/go-cmp v0.4.0 + github.com/google/go-cmp v0.5.5 github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/gophercloud/gophercloud v0.0.0-20181014043407-c8947f7d1c51 + github.com/gophercloud/gophercloud v0.1.0 github.com/gorilla/mux v1.7.4 // indirect github.com/h2non/gock v1.0.7 github.com/hetznercloud/hcloud-go v1.4.0 @@ -56,7 +56,6 @@ require ( github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/packethost/packngo v0.1.0 - github.com/pkg/errors v0.8.1 // indirect github.com/prometheus/client_golang v0.8.0 github.com/prometheus/common v0.0.0-20180110214958-89604d197083 // indirect github.com/prometheus/procfs v0.0.0-20180212145926-282c8707aa21 // indirect @@ -64,12 +63,11 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 // indirect - golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect - golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be - golang.org/x/sync v0.0.0-20190423024810-112230192c58 - golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect - golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 // indirect + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 + golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 google.golang.org/api v0.0.0-20180921000521-920bb1beccf7 google.golang.org/appengine v1.4.0 // indirect google.golang.org/grpc v1.30.0 // indirect diff --git a/go.sum b/go.sum index 974853e5..d8e63f6e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.28.0 h1:KZ/88LWSw8NxMkjdQyX7LQSGR9PkHr4PaVuNm8zgFq0= cloud.google.com/go v0.28.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d h1:j6oB/WPCigdOkxtuPl1VSIiLpy7Mdsu6phQffbF19Ng= github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d/go.mod h1:3cARGAK9CfW3HoxCy1a0G4TKrdiKke8ftOMEOHyySYs= github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= @@ -23,6 +25,8 @@ github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a h1:BtpsbiV638WQZwhA98 github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bluele/slack v0.0.0-20171128075526-307046097ee9 h1:bnBrnv6TsEcPq7HIJykjAGl8jw/J1FZMYYceCQfLgEI= github.com/bluele/slack v0.0.0-20171128075526-307046097ee9/go.mod h1:W679Ri2W93VLD8cVpEY/zLH1ow4zhJcCyjzrKxfM3QM= +github.com/brightbox/gobrightbox v0.8.2 h1:Bp8P75BljmKVGWbsQdSKstF304lkXhkmthzQm+TMsTs= +github.com/brightbox/gobrightbox v0.8.2/go.mod h1:DAyIIYr1kls2u+YxZDLAJ4zcHpQ3JRqb2gyD7Md1RkQ= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -74,12 +78,16 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gophercloud/gophercloud v0.0.0-20181014043407-c8947f7d1c51 h1:boFYpbhy0scteX2LHVBgOwPfEMaisiv6ShNqvmi16+I= github.com/gophercloud/gophercloud v0.0.0-20181014043407-c8947f7d1c51/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= @@ -132,6 +140,8 @@ github.com/packethost/packngo v0.1.0 h1:G/5zumXb2fbPm5MAM3y8MmugE66Ehpio5qx0Ihdh github.com/packethost/packngo v0.1.0/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8= @@ -150,52 +160,82 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 h1:/Bsw4C+DEdqPjt8vAqaC9LAqpAQnaCQQqmolqq3S1T4= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 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-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180921000521-920bb1beccf7 h1:XKT3Wlpn+o6Car1ot74Z4R+R9CeRfITCLZb0Q9/mpx4= google.golang.org/api v0.0.0-20180921000521-920bb1beccf7/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -219,5 +259,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= +gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/provider.go b/provider.go index 369b6f8a..8e11bec5 100644 --- a/provider.go +++ b/provider.go @@ -22,6 +22,7 @@ func (s ProviderType) Value() (driver.Value, error) { const ( ProviderAmazon = ProviderType("amazon") ProviderAzure = ProviderType("azure") + ProviderBrightbox = ProviderType("brightbox") ProviderDigitalOcean = ProviderType("digitalocean") ProviderGoogle = ProviderType("google") ProviderHetznerCloud = ProviderType("hetznercloud")