Add a metadata provider for Vultr

Vultr uses a very similar approach to AWS, including using the
same IP address for serving metadata. In fact, it seems
as though if AWS appears first in the list of providers, that
provider mistakenly believes to be running on AWS (hence the
insertion of `NewVultr` in between GCP and AWS. I don't believe
AWS servers will accidentally try to use the Vultr provider,
as it seems that the `/v1/` endpoint doesn't exist on AWS.

Signed-off-by: Luke Hodkinson <furious.luke@gmail.com>
This commit is contained in:
Luke Hodkinson
2017-06-21 20:14:58 +10:00
parent 8be67983ff
commit 3dc23b96ac
2 changed files with 124 additions and 1 deletions

View File

@@ -47,7 +47,7 @@ var netProviders []Provider
var cdromProviders []Provider
func init() {
netProviders = []Provider{NewGCP(), NewAWS()}
netProviders = []Provider{NewGCP(), NewVultr(), NewAWS()}
cdromProviders = []Provider{NewCDROM()}
}

View File

@@ -0,0 +1,123 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"time"
)
const (
vultrMetaDataURL = "http://169.254.169.254/v1/"
)
// ProviderVultr is the type implementing the Provider interface for Vultr
type ProviderVultr struct {
}
// NewVultr returns a new ProviderVultr
func NewVultr() *ProviderVultr {
return &ProviderVultr{}
}
func (p *ProviderVultr) String() string {
return "Vultr"
}
// Probe checks if we are running on Vultr
func (p *ProviderVultr) Probe() bool {
// Getting the index should always work...
_, err := vultrGet(vultrMetaDataURL)
return (err == nil)
}
// Extract gets both the Vultr specific and generic userdata
func (p *ProviderVultr) Extract() ([]byte, error) {
// Get host name. This must not fail
hostname, err := vultrGet(vultrMetaDataURL + "hostname")
if err != nil {
return nil, err
}
err = ioutil.WriteFile(path.Join(ConfigPath, Hostname), hostname, 0644)
if err != nil {
return nil, fmt.Errorf("Vultr: Failed to write hostname: %s", err)
}
// public ipv4
vultrMetaGet("interfaces/0/ipv4/address", "public_ipv4", 0644)
// private ipv4
vultrMetaGet("interfaces/1/ipv4/address", "private_ipv4", 0644)
// private netmask
vultrMetaGet("interfaces/1/ipv4/netmask", "private_netmask", 0644)
// region code
vultrMetaGet("region/regioncode", "region_code", 0644)
// instance-id
vultrMetaGet("instanceid", "instance_id", 0644)
return nil, nil
}
// lookup a value (lookupName) in Vultr metaservice and store in given fileName
func vultrMetaGet(lookupName string, fileName string, fileMode os.FileMode) {
if lookupValue, err := vultrGet(vultrMetaDataURL + lookupName); err == nil {
// we got a value from the metadata server, now save to filesystem
err = ioutil.WriteFile(path.Join(ConfigPath, fileName), lookupValue, fileMode)
if err != nil {
// we couldn't save the file for some reason
log.Printf("Vultr: Failed to write %s:%s %s", fileName, lookupValue, err)
}
} else {
// we did not get a value back from the metadata server
log.Printf("Vultr: Failed to get %s: %s", lookupName, err)
}
}
// vultrGet requests and extracts the requested URL
func vultrGet(url string) ([]byte, error) {
var client = &http.Client{
Timeout: time.Second * 2,
}
req, err := http.NewRequest("", url, nil)
if err != nil {
return nil, fmt.Errorf("Vultr: http.NewRequest failed: %s", err)
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("Vultr: Could not contact metadata service: %s", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("Vultr: Status not ok: %d", resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Vultr: Failed to read http response: %s", err)
}
return body, nil
}
// SSH keys:
func (p *ProviderVultr) handleSSH() error {
sshKeys, err := vultrGet(vultrMetaDataURL + "public-keys")
if err != nil {
return fmt.Errorf("Failed to get sshKeys: %s", err)
}
if err := os.Mkdir(path.Join(ConfigPath, SSH), 0755); err != nil {
return fmt.Errorf("Failed to create %s: %s", SSH, err)
}
err = ioutil.WriteFile(path.Join(ConfigPath, SSH, "authorized_keys"), sshKeys, 0600)
if err != nil {
return fmt.Errorf("Failed to write ssh keys: %s", err)
}
return nil
}