Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Equinix Metal provider #177

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ locals:
executors:
go:
docker:
- image: docker.mirror.hashicorp.services/circleci/golang:1.13.15
- image: docker.mirror.hashicorp.services/circleci/golang:1.16.5
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

packngo requires go v1.15+

I can remove this line if #200 (or similar) is merged first.

environment:
- TEST_RESULTS: /tmp/test-results # path to where test results are saved

Expand Down Expand Up @@ -207,7 +207,12 @@ jobs:
- acctest:
provider-test-infra-dir: triton
provider-go-test-dir: triton

equinixmetal-provider:
executor: go
steps:
- acctest:
provider-test-infra-dir: equinixmetal
provider-go-test-dir: equinixmetal
workflows:
version: 2
acceptance:
Expand Down Expand Up @@ -253,3 +258,7 @@ workflows:
requires:
- go-test
filters: *ignore_prs
- equinixmetal-provider:
requires:
- go-test
filters: *ignore_prs
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function.
* Triton [Config options](https://github.com/hashicorp/go-discover/blob/8b3ddf4/provider/triton/triton_discover.go#L17-L27)
* vSphere [Config options](https://github.com/hashicorp/go-discover/blob/8b3ddf4/provider/vsphere/vsphere_discover.go#L145-L157)
* Packet [Config options](https://github.com/hashicorp/go-discover/blob/8b3ddf4/provider/packet/packet_discover.go#L25-L40)
* Equinix Metal [Config options](https://github.com/hashicorp/go-discover/blob/master/provider/equinixmetal/equinixmetal_discover.go#L25-L35)

The following providers are implemented in the go-discover/provider subdirectory
but aren't automatically registered. If you want to support these providers,
Expand All @@ -44,7 +45,7 @@ register them manually:

HashiCorp maintains acceptance tests that regularly allocate and run tests with
real resources to verify the behavior of several of these providers. Those
currently are: Amazon AWS, Microsoft Azure, Google Cloud, DigitalOcean, Triton, Scaleway, AliBaba Cloud, vSphere, and Packet.net.
currently are: Amazon AWS, Microsoft Azure, Google Cloud, DigitalOcean, Triton, Scaleway, AliBaba Cloud, vSphere, and Equinix Metal (Packet).

### Config Example

Expand Down Expand Up @@ -91,6 +92,9 @@ provider=vsphere category_name=consul-role tag_name=consul-server host=... user=
# Packet
provider=packet auth_token=token project=uuid url=... address_type=...

# Equinix Metal
provider=equinixmetal auth_token=token project=uuid url=... address_type=...

# Kubernetes
provider=k8s label_selector="app = consul-server"
```
Expand Down
2 changes: 2 additions & 0 deletions discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/hashicorp/go-discover/provider/aws"
"github.com/hashicorp/go-discover/provider/azure"
"github.com/hashicorp/go-discover/provider/digitalocean"
"github.com/hashicorp/go-discover/provider/equinixmetal"
"github.com/hashicorp/go-discover/provider/gce"
"github.com/hashicorp/go-discover/provider/linode"
"github.com/hashicorp/go-discover/provider/mdns"
Expand Down Expand Up @@ -59,6 +60,7 @@ var Providers = map[string]Provider{
"triton": &triton.Provider{},
"vsphere": &vsphere.Provider{},
"packet": &packet.Provider{},
"equinixmetal": &equinixmetal.Provider{},
}

// Discover looks up metadata in different cloud environments.
Expand Down
7 changes: 3 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ require (
github.com/aws/aws-sdk-go v1.25.41
github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661
github.com/digitalocean/godo v1.7.5
github.com/dnaeon/go-vcr v1.0.1 // indirect
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
github.com/googleapis/gnostic v0.2.0 // indirect
github.com/gophercloud/gophercloud v0.1.0
Expand All @@ -23,13 +22,13 @@ require (
github.com/linode/linodego v0.7.1
github.com/mitchellh/go-homedir v1.1.0
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c
github.com/packethost/packngo v0.15.0
github.com/pkg/errors v0.8.0 // indirect
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/sirupsen/logrus v1.0.6 // indirect
github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d
github.com/stretchr/testify v1.4.0
github.com/stretchr/testify v1.5.1
github.com/tencentcloud/tencentcloud-sdk-go v1.0.162
github.com/vmware/govmomi v0.18.0
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
Expand All @@ -42,4 +41,4 @@ require (
k8s.io/client-go v0.18.2
)

go 1.12
go 1.16
14 changes: 5 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
Expand Down Expand Up @@ -115,7 +114,6 @@ github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 h1:O/pT5C1Q3mVXMyuqg7yuAWUg/jMZR1/0QTzTRdNR6Uw=
github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
Expand Down Expand Up @@ -156,13 +154,11 @@ github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE=
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M=
github.com/packethost/packngo v0.15.0 h1:nyiVHJAhQdt37Vf141vvm83niZHRKbBs9FmFB+QRgd4=
github.com/packethost/packngo v0.15.0/go.mod h1:YrtUNN9IRjjqN6zK+cy2IYoi3EjHfoWTWxJkI1I1Vk0=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand All @@ -182,8 +178,9 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tencentcloud/tencentcloud-sdk-go v1.0.162 h1:8fDzz4GuVg4skjY2B0nMN7h6uN61EDVkuLyI2+qGHhI=
github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI=
github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo=
Expand All @@ -194,6 +191,7 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down Expand Up @@ -262,15 +260,13 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
175 changes: 175 additions & 0 deletions provider/equinixmetal/equinixmetal_discover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package equinixmetal

import (
"fmt"
"log"
"os"
"strings"

"github.com/packethost/packngo"
)

const baseURL = "https://api.equinix.com/metal/v1/"

var (
addressTypes = []string{packngo.PublicIPv4, packngo.PrivateIPv4, packngo.PublicIPv6}
defaultAddressType = packngo.PrivateIPv4
)

// Provider struct
type Provider struct {
userAgent string
}

// SetUserAgent setter
func (p *Provider) SetUserAgent(s string) {
p.userAgent = s
}

// Help function
func (p *Provider) Help() string {
return fmt.Sprintf(`Equinix Metal:
provider: "equinixmetal"
project: UUID of metal project. Required
auth_token: Equinix Metal authentication token. Required
url: Equinix Metal REST URL. Optional
address_type: Address type, one of: %s. Defaults to %q. Optional
facility: Filter for specific facility (Examples: "sv15,ny5")
metro: Filter for specific metro (Examples: "sv,ny")
tag: Filter by tag (Examples: "tag1,tag2")

Variables can also be provided by environmental variables:
export METAL_PROJECT for project
export METAL_URL for url
export METAL_AUTH_TOKEN for auth_token
`, strings.Join(addressTypes, ", "), defaultAddressType)
}

// Addrs function
func (p *Provider) Addrs(args map[string]string, l *log.Logger) ([]string, error) {
authToken := argsOrEnv(args, "auth_token", "METAL_AUTH_TOKEN")
projectID := argsOrEnv(args, "project", "METAL_PROJECT")
metalURL := argsOrEnv(args, "url", "METAL_URL")
addressType := args["address_type"]
metalFacilities := args["facility"]
metalMetros := args["metro"]
metalTags := args["tag"]

if !Include(addressTypes, addressType) {
l.Printf("[INFO] discover-metal: Address type %s is not supported. Valid values are {%s}. Falling back to '%s'", addressType, strings.Join(addressTypes, ","), defaultAddressType)
addressType = defaultAddressType
}

includeMetros := includeArgs(metalMetros)
includeFacilities := includeArgs(metalFacilities)
includeTags := includeArgs(metalTags)

c, err := client(p.userAgent, metalURL, authToken)
if err != nil {
return nil, fmt.Errorf("discover-metal: Initializing Equinix Metal client failed: %s", err)
}

var devices []packngo.Device

if projectID == "" {
return nil, fmt.Errorf("discover-metal: 'project' parameter must be provided")
}

getOpts := &packngo.GetOptions{}
getOpts.Including("facility", "metro", "ip_addresses")
getOpts.Excluding("ssh_keys", "project")
devices, _, err = c.Devices.List(projectID, getOpts)
if err != nil {
return nil, fmt.Errorf("discover-metal: Fetching Equinix Metal devices failed: %s", err)
}

var addrs []string
for _, d := range devices {

if len(includeFacilities) > 0 && !Include(includeFacilities, d.Facility.Code) {
continue
}

if len(includeMetros) > 0 && !Include(includeMetros, d.Metro.Code) {
continue
}

if len(includeTags) > 0 && !Any(d.Tags, func(v string) bool { return Include(includeTags, v) }) {
continue
}

for _, n := range d.Network {
if ipMatchesType(addressType, n) {
addrs = append(addrs, n.Address)
}
}
}
return addrs, nil
}

func ipMatchesType(addressType string, n *packngo.IPAddressAssignment) bool {
switch addressType {
case packngo.PublicIPv4:
return n.Public && n.AddressFamily == 4
case packngo.PublicIPv6:
return n.Public && n.AddressFamily == 6
case packngo.PrivateIPv4:
return !n.Public && n.AddressFamily == 4
default:
return false
}
}

func client(useragent, url, token string) (*packngo.Client, error) {
if url == "" {
url = baseURL
}

client, err := packngo.NewClientWithBaseURL(useragent, token, nil, url)
if err == nil {
client.UserAgent = fmt.Sprintf("%s %s", useragent, client.UserAgent)
}
return client, err
}

func argsOrEnv(args map[string]string, key, env string) string {
if value := args[key]; value != "" {
return value
}
return os.Getenv(env)
}

func includeArgs(s string) []string {
var include []string
for _, localstring := range strings.Split(s, ",") {
if len(localstring) > 0 {
include = append(include, localstring)
}
}
return include
}

// Index returns the first index of the target string t, or -1 if no match is found.
func Index(vs []string, t string) int {
for i, v := range vs {
if v == t {
return i
}
}
return -1
}

// Include returns true if the target string t is in the slice.
func Include(vs []string, t string) bool {
return Index(vs, t) >= 0
}

//Any returns true if one of the strings in the slice satisfies the predicate f.
func Any(vs []string, f func(string) bool) bool {
for _, v := range vs {
if f(v) {
return true
}
}
return false
}
Loading