mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-20 09:39:08 +00:00
Add Scaleway provider in metadata package
Signed-off-by: Patrik Cyvoct <patrik@ptrk.io>
This commit is contained in:
parent
3a3b487907
commit
5ab6b88029
@ -13,3 +13,4 @@ config:
|
||||
capabilities:
|
||||
- CAP_SYS_ADMIN
|
||||
- CAP_NET_ADMIN
|
||||
- CAP_NET_BIND_SERVICE
|
||||
|
@ -45,7 +45,7 @@ var netProviders []Provider
|
||||
var cdromProviders []Provider
|
||||
|
||||
func main() {
|
||||
providers := []string{"aws", "gcp", "openstack", "vultr", "packet", "cdrom"}
|
||||
providers := []string{"aws", "gcp", "openstack", "scaleway", "vultr", "packet", "cdrom"}
|
||||
if len(os.Args) > 1 {
|
||||
providers = os.Args[1:]
|
||||
}
|
||||
@ -59,6 +59,8 @@ func main() {
|
||||
netProviders = append(netProviders, NewOpenstack())
|
||||
case "packet":
|
||||
netProviders = append(netProviders, NewPacket())
|
||||
case "scaleway":
|
||||
netProviders = append(netProviders, NewScaleway())
|
||||
case "vultr":
|
||||
netProviders = append(netProviders, NewVultr())
|
||||
case "cdrom":
|
||||
|
263
pkg/metadata/provider_scaleway.go
Normal file
263
pkg/metadata/provider_scaleway.go
Normal file
@ -0,0 +1,263 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
scalewayMetadataURL = "http://169.254.42.42/"
|
||||
scalewayUserdataURL = "169.254.42.42:80"
|
||||
instanceIDFile = "instance_id"
|
||||
instanceLocationFile = "instance_location"
|
||||
publicIPFile = "public_ip"
|
||||
privateIPFile = "private_ip"
|
||||
)
|
||||
|
||||
// ProviderScaleway is the type implementing the Provider interface for Scaleway
|
||||
type ProviderScaleway struct {
|
||||
}
|
||||
|
||||
// NewScaleway returns a new ProviderScaleway
|
||||
func NewScaleway() *ProviderScaleway {
|
||||
return &ProviderScaleway{}
|
||||
}
|
||||
|
||||
func (p *ProviderScaleway) String() string {
|
||||
return "Scaleway"
|
||||
}
|
||||
|
||||
func (p *ProviderScaleway) sendBootSignal() error {
|
||||
var client = &http.Client{
|
||||
Timeout: time.Second * 2,
|
||||
}
|
||||
|
||||
state := []byte(`{"state_detail": "booted"}`)
|
||||
|
||||
req, err := http.NewRequest("PATCH", scalewayMetadataURL+"state", bytes.NewBuffer(state))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Scaleway: http.NewRequest failed: %s", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
_, err = client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Scaleway: Could not contact state service: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Probe checks if we are running on Scaleway
|
||||
func (p *ProviderScaleway) Probe() bool {
|
||||
// Getting the conf should always work...
|
||||
_, err := scalewayGet(scalewayMetadataURL + "conf")
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
// we are on Scaleway so we need to send a request to tell that the instance has correctly booted
|
||||
err = p.sendBootSignal()
|
||||
if err != nil {
|
||||
log.Printf("Scaleway: Could not signal that the instance booted")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Extract gets both the Scaleway specific and generic userdata
|
||||
func (p *ProviderScaleway) Extract() ([]byte, error) {
|
||||
metadata, err := scalewayGet(scalewayMetadataURL + "conf")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to get conf: %s", err)
|
||||
}
|
||||
|
||||
hostname, err := p.extractInformation(metadata, "hostname")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to get hostname: %s", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(ConfigPath, Hostname), hostname, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to write hostname: %s", err)
|
||||
}
|
||||
|
||||
instanceID, err := p.extractInformation(metadata, "id")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to get instanceID: %s", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(ConfigPath, instanceIDFile), instanceID, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to write instance_id: %s", err)
|
||||
}
|
||||
|
||||
instanceLocation, err := p.extractInformation(metadata, "location_zone_id")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to get instanceLocation: %s", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(ConfigPath, instanceLocationFile), instanceLocation, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to write instance_location: %s", err)
|
||||
}
|
||||
|
||||
publicIP, err := p.extractInformation(metadata, "public_ip_address")
|
||||
if err != nil {
|
||||
// not an error
|
||||
log.Printf("Scaleway: Failed to get publicIP: %s", err)
|
||||
} else {
|
||||
err = ioutil.WriteFile(path.Join(ConfigPath, publicIPFile), publicIP, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to write public_ip: %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
privateIP, err := p.extractInformation(metadata, "private_ip")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to get privateIP: %s", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(ConfigPath, privateIPFile), privateIP, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to write private_ip: %s", err)
|
||||
}
|
||||
|
||||
if err := p.handleSSH(metadata); err != nil {
|
||||
log.Printf("Scaleway: Failed to get ssh data: %s", err)
|
||||
}
|
||||
|
||||
// Generic userdata
|
||||
userData, err := scalewayGetUserdata()
|
||||
if err != nil {
|
||||
log.Printf("Scaleway: Failed to get user-data: %s", err)
|
||||
// This is not an error
|
||||
return nil, nil
|
||||
}
|
||||
return userData, nil
|
||||
}
|
||||
|
||||
// exctractInformation returns the extracted information given as parameter from the metadata
|
||||
func (p *ProviderScaleway) extractInformation(metadata []byte, information string) ([]byte, error) {
|
||||
query := strings.ToUpper(information) + "="
|
||||
for _, line := range bytes.Split(metadata, []byte("\n")) {
|
||||
if bytes.HasPrefix(line, []byte(query)) {
|
||||
return bytes.TrimPrefix(line, []byte(query)), nil
|
||||
}
|
||||
}
|
||||
return []byte(""), fmt.Errorf("No %s found", information)
|
||||
}
|
||||
|
||||
// scalewayGet requests and extracts the requested URL
|
||||
func scalewayGet(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("Scaleway: http.NewRequest failed: %s", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Could not contact metadata service: %s", err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Scaleway: Status not ok: %d", resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Scaleway: Failed to read http response: %s", err)
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// scalewayGetUserdata returns the userdata of the server, differs from scalewayGet since the source port has to be below 1024 in order to work
|
||||
func scalewayGetUserdata() ([]byte, error) {
|
||||
server, err := net.ResolveTCPAddr("tcp", scalewayUserdataURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var conn *net.TCPConn
|
||||
foundPort := false
|
||||
for i := 1; i <= 1024; i++ {
|
||||
client, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err = net.DialTCP("tcp", client, server)
|
||||
if err == nil {
|
||||
foundPort = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if foundPort == false {
|
||||
return nil, errors.New("not able to found a free port below 1024")
|
||||
}
|
||||
defer conn.Close()
|
||||
fmt.Fprintf(conn, "GET /user_data HTTP/1.0\r\n\r\n")
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
resp, err := http.ReadResponse(reader, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (p *ProviderScaleway) handleSSH(metadata []byte) error {
|
||||
sshKeysNumberString, err := p.extractInformation(metadata, "ssh_public_keys")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get sshKeys: %s", err)
|
||||
}
|
||||
sshKeysNumber, err := strconv.Atoi(string(sshKeysNumberString))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to convert sshKeysNumber to int: %s", err)
|
||||
}
|
||||
|
||||
rootKeys := ""
|
||||
for i := 0; i < sshKeysNumber; i++ {
|
||||
sshKey, err := p.extractInformation(metadata, "ssh_public_keys_"+strconv.Itoa(i)+"_key")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get ssh_key %d: %s", i, err)
|
||||
}
|
||||
|
||||
line := string(bytes.Trim(sshKey, "'"))
|
||||
parts := strings.SplitN(line, " ", 2)
|
||||
if len(parts) == 2 {
|
||||
rootKeys = rootKeys + parts[1] + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
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"), []byte(rootKeys), 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to write ssh keys: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user