mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 09:16:29 +00:00
Add Scaleway support for linuxkit command line tool
Signed-off-by: Patrik Cyvoct <patrik@ptrk.io>
This commit is contained in:
parent
f8d399490e
commit
a6783261f3
@ -19,6 +19,7 @@ func pushUsage() {
|
||||
fmt.Printf(" gcp\n")
|
||||
fmt.Printf(" openstack\n")
|
||||
fmt.Printf(" packet\n")
|
||||
fmt.Printf(" scaleway\n")
|
||||
fmt.Printf(" vcenter\n")
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("'options' are the backend specific options.\n")
|
||||
@ -44,6 +45,8 @@ func push(args []string) {
|
||||
pushOpenstack(args[1:])
|
||||
case "packet":
|
||||
pushPacket(args[1:])
|
||||
case "scaleway":
|
||||
pushScaleway(args[1:])
|
||||
case "vcenter":
|
||||
pushVCenter(args[1:])
|
||||
case "help", "-h", "-help", "--help":
|
||||
|
104
src/cmd/linuxkit/push_scaleway.go
Normal file
104
src/cmd/linuxkit/push_scaleway.go
Normal file
@ -0,0 +1,104 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func pushScaleway(args []string) {
|
||||
flags := flag.NewFlagSet("scaleway", flag.ExitOnError)
|
||||
invoked := filepath.Base(os.Args[0])
|
||||
flags.Usage = func() {
|
||||
fmt.Printf("USAGE: %s push scaleway [options] path\n\n", invoked)
|
||||
fmt.Printf("'path' is the full path to an EFI ISO image. It will be copied to a new Scaleway instance in order to create a Scaeway image out of it.\n")
|
||||
fmt.Printf("Options:\n\n")
|
||||
flags.PrintDefaults()
|
||||
}
|
||||
nameFlag := flags.String("img-name", "", "Overrides the name used to identify the image name in Scaleway's images. Defaults to the base of 'path' with the '.iso' suffix removed")
|
||||
tokenFlag := flags.String("token", "", "Token to connet to Scaleway API")
|
||||
sshKeyFlag := flags.String("ssh-key", os.Getenv("HOME")+"/.ssh/id_rsa", "SSH key file")
|
||||
instanceIDFlag := flags.String("instance-id", "", "Instance ID of a running Scaleway instance, with a second volume.")
|
||||
deviceNameFlag := flags.String("device-name", "/dev/vdb", "Device name on which the image will be copied")
|
||||
regionFlag := flags.String("region", defaultScalewayRegion, "Select scaleway region")
|
||||
noCleanFlag := flags.Bool("no-clean", false, "Do not remove temporary instance and volumes")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
log.Fatal("Unable to parse args")
|
||||
}
|
||||
|
||||
remArgs := flags.Args()
|
||||
if len(remArgs) == 0 {
|
||||
fmt.Printf("Please specify the path to the image to push\n")
|
||||
flags.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
path := remArgs[0]
|
||||
|
||||
name := getStringValue(scalewayNameVar, *nameFlag, "")
|
||||
token := getStringValue(tokenVar, *tokenFlag, "")
|
||||
sshKeyFile := getStringValue(sshKeyVar, *sshKeyFlag, "")
|
||||
instanceID := getStringValue(instanceIDVar, *instanceIDFlag, "")
|
||||
deviceName := getStringValue(deviceNameVar, *deviceNameFlag, "")
|
||||
region := getStringValue(regionVar, *regionFlag, defaultScalewayRegion)
|
||||
|
||||
const suffix = ".iso"
|
||||
if name == "" {
|
||||
name = strings.TrimSuffix(path, suffix)
|
||||
name = filepath.Base(name)
|
||||
}
|
||||
|
||||
client, err := NewScalewayClient(token, region)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to connect to Scaleway: %v", err)
|
||||
}
|
||||
|
||||
// if no instanceID is provided, we create the instance
|
||||
if instanceID == "" {
|
||||
instanceID, err = client.CreateInstance()
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating a Scaleway instance: %v", err)
|
||||
}
|
||||
|
||||
err = client.BootInstanceAndWait(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Error booting instance: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
volumeID, err := client.GetSecondVolumeID(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Error retrieving second volume ID: %v", err)
|
||||
}
|
||||
|
||||
err = client.CopyImageToInstance(instanceID, path, sshKeyFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Error copying ISO file to Scaleway's instance: %v", err)
|
||||
}
|
||||
|
||||
err = client.WriteImageToVolume(instanceID, deviceName)
|
||||
if err != nil {
|
||||
log.Fatalf("Error writing ISO file to additional volume: %v", err)
|
||||
}
|
||||
|
||||
err = client.TerminateInstance(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Error terminating Scaleway's instance: %v", err)
|
||||
}
|
||||
|
||||
err = client.CreateScalewayImage(instanceID, volumeID, name)
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating Scaleway image: %v", err)
|
||||
}
|
||||
|
||||
if !*noCleanFlag {
|
||||
err = client.DeleteInstanceAndVolumes(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Error deleting Scaleway instance and volumes: %v")
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ func runUsage() {
|
||||
fmt.Printf(" openstack\n")
|
||||
fmt.Printf(" packet\n")
|
||||
fmt.Printf(" qemu [linux]\n")
|
||||
fmt.Printf(" scaleway\n")
|
||||
fmt.Printf(" vbox\n")
|
||||
fmt.Printf(" vcenter\n")
|
||||
fmt.Printf(" vmware\n")
|
||||
@ -62,6 +63,8 @@ func run(args []string) {
|
||||
runPacket(args[1:])
|
||||
case "qemu":
|
||||
runQemu(args[1:])
|
||||
case "scaleway":
|
||||
runScaleway(args[1:])
|
||||
case "vmware":
|
||||
runVMware(args[1:])
|
||||
case "vbox":
|
||||
|
94
src/cmd/linuxkit/run_scaleway.go
Normal file
94
src/cmd/linuxkit/run_scaleway.go
Normal file
@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultScalewayInstanceType = "VC1S"
|
||||
defaultScalewayRegion = "par1"
|
||||
|
||||
scalewayNameVar = "SCW_IMAGE_NAME" // non-standard
|
||||
tokenVar = "SCW_TOKEN" // non-standard
|
||||
sshKeyVar = "SCW_SSH_KEY_FILE" // non-standard
|
||||
instanceIDVar = "SCW_INSTANCE_ID" // non-standard
|
||||
deviceNameVar = "SCW_DEVICE_NAME" // non-standard
|
||||
regionVar = "SCW_TARGET_REGION"
|
||||
|
||||
instanceTypeVar = "SCW_RUN_TYPE" // non-standard
|
||||
)
|
||||
|
||||
func runScaleway(args []string) {
|
||||
flags := flag.NewFlagSet("scaleway", flag.ExitOnError)
|
||||
invoked := filepath.Base(os.Args[0])
|
||||
flags.Usage = func() {
|
||||
fmt.Printf("USAGE: %s run scaleway [options] [name]\n\n", invoked)
|
||||
fmt.Printf("'name' is the name of a Scaleway image that has alread \n")
|
||||
fmt.Printf("been uploaded using 'linuxkit push'\n\n")
|
||||
fmt.Printf("Options:\n\n")
|
||||
flags.PrintDefaults()
|
||||
}
|
||||
instanceTypeFlag := flags.String("instance-type", defaultScalewayInstanceType, "Scaleway instance type")
|
||||
instanceNameFlag := flags.String("instance-name", "linuxkit", "Name of the create instance, default to the image name")
|
||||
tokenFlag := flags.String("token", "", "Token to connect to Scaleway API")
|
||||
regionFlag := flags.String("region", defaultScalewayRegion, "Select Scaleway region")
|
||||
cleanFlag := flags.Bool("clean", false, "Remove instance")
|
||||
noAttachFlag := flags.Bool("no-attach", false, "Don't attach to serial port, you will have to connect to instance manually")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
log.Fatal("Unable to parse args")
|
||||
}
|
||||
|
||||
remArgs := flags.Args()
|
||||
if len(remArgs) == 0 {
|
||||
fmt.Printf("Please specify the name of the image to boot\n")
|
||||
flags.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
name := remArgs[0]
|
||||
|
||||
instanceType := getStringValue(instanceTypeVar, *instanceTypeFlag, defaultScalewayInstanceType)
|
||||
instanceName := getStringValue("", *instanceNameFlag, name)
|
||||
token := getStringValue(tokenVar, *tokenFlag, "")
|
||||
region := getStringValue(regionVar, *regionFlag, defaultScalewayRegion)
|
||||
|
||||
client, err := NewScalewayClient(token, region)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to connect to Scaleway: %v", err)
|
||||
}
|
||||
|
||||
instanceID, err := client.CreateLinuxkitInstance(instanceName, name, instanceType)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to create Scaleway instance: %v", err)
|
||||
}
|
||||
|
||||
err = client.BootInstance(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to boot Scaleway instance: %v", err)
|
||||
}
|
||||
|
||||
if !*noAttachFlag {
|
||||
err = client.ConnectSerialPort(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to connect to serial port: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *cleanFlag {
|
||||
err = client.TerminateInstance(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to stop instance: %v", err)
|
||||
}
|
||||
|
||||
err = client.DeleteInstanceAndVolumes(instanceID)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to delete instance: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
523
src/cmd/linuxkit/scaleway.go
Normal file
523
src/cmd/linuxkit/scaleway.go
Normal file
@ -0,0 +1,523 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gotty "github.com/moul/gotty-client"
|
||||
scw "github.com/scaleway/go-scaleway"
|
||||
"github.com/scaleway/go-scaleway/logger"
|
||||
"github.com/scaleway/go-scaleway/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// ScalewayClient contains state required for communication with Scaleway as well as the instance
|
||||
type ScalewayClient struct {
|
||||
api *scw.ScalewayAPI
|
||||
fileName string
|
||||
region string
|
||||
sshConfig *ssh.ClientConfig
|
||||
}
|
||||
|
||||
// ScalewayConfig contains required field to read scaleway config file
|
||||
type ScalewayConfig struct {
|
||||
Organization string `json:"organization"`
|
||||
Token string `json:"token"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// NewScalewayClient creates a new scaleway client
|
||||
func NewScalewayClient(token, region string) (*ScalewayClient, error) {
|
||||
log.Debugf("Connecting to Scaleway")
|
||||
organization := ""
|
||||
if token == "" {
|
||||
log.Debugf("Using .scwrc file to get token")
|
||||
homeDir := os.Getenv("HOME")
|
||||
if homeDir == "" {
|
||||
homeDir = os.Getenv("USERPROFILE") // Windows support
|
||||
}
|
||||
if homeDir == "" {
|
||||
return nil, fmt.Errorf("Home directory not found")
|
||||
}
|
||||
swrcPath := filepath.Join(homeDir, ".scwrc")
|
||||
|
||||
file, err := ioutil.ReadFile(swrcPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error reading Scaleway config file: %v", err)
|
||||
}
|
||||
|
||||
var scalewayConfig ScalewayConfig
|
||||
err = json.Unmarshal(file, &scalewayConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error during unmarshal of Scaleway config file: %v", err)
|
||||
}
|
||||
|
||||
token = scalewayConfig.Token
|
||||
organization = scalewayConfig.Organization
|
||||
}
|
||||
|
||||
api, err := scw.NewScalewayAPI(organization, token, "", region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := logger.NewDisableLogger()
|
||||
api.Logger = l
|
||||
|
||||
if organization == "" {
|
||||
organisations, err := api.GetOrganization()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
api.Organization = organisations.Organizations[0].ID
|
||||
}
|
||||
|
||||
client := &ScalewayClient{
|
||||
api: api,
|
||||
fileName: "",
|
||||
region: region,
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// CreateInstance create an instance with one additional volume
|
||||
func (s *ScalewayClient) CreateInstance() (string, error) {
|
||||
// get the Ubuntu Xenial image id
|
||||
image, err := s.api.GetImageID("Ubuntu Xenial", "x86_64") // TODO fix arch and use from args
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
imageID := image.Identifier
|
||||
|
||||
var serverDefinition types.ScalewayServerDefinition
|
||||
|
||||
serverDefinition.Name = "linuxkit-builder"
|
||||
serverDefinition.Image = &imageID
|
||||
serverDefinition.CommercialType = "VC1M" // TODO use args?
|
||||
|
||||
// creation of second volume
|
||||
var volumeDefinition types.ScalewayVolumeDefinition
|
||||
volumeDefinition.Name = "linuxkit-builder-volume"
|
||||
volumeDefinition.Size = 50000000000 // FIX remove hardcoded value
|
||||
volumeDefinition.Type = "l_ssd"
|
||||
|
||||
log.Debugf("Creating volume on Scaleway")
|
||||
volumeID, err := s.api.PostVolume(volumeDefinition)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
serverDefinition.Volumes = make(map[string]string)
|
||||
serverDefinition.Volumes["1"] = volumeID
|
||||
|
||||
serverID, err := s.api.PostServer(serverDefinition)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Debugf("Created server %s on Scaleway", serverID)
|
||||
return serverID, nil
|
||||
}
|
||||
|
||||
// GetSecondVolumeID returns the ID of the second volume of the server
|
||||
func (s *ScalewayClient) GetSecondVolumeID(instanceID string) (string, error) {
|
||||
server, err := s.api.GetServer(instanceID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
secondVolume, ok := server.Volumes["1"]
|
||||
if !ok {
|
||||
return "", errors.New("No second volume found")
|
||||
}
|
||||
|
||||
return secondVolume.Identifier, nil
|
||||
}
|
||||
|
||||
// BootInstanceAndWait boots and wait for instance to be booted
|
||||
func (s *ScalewayClient) BootInstanceAndWait(instanceID string) error {
|
||||
err := s.api.PostServerAction(instanceID, "poweron")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Waiting for server %s to be started", instanceID)
|
||||
|
||||
// code taken from scaleway-cli, could need some changes
|
||||
promise := make(chan bool)
|
||||
var server *types.ScalewayServer
|
||||
var currentState string
|
||||
|
||||
go func() {
|
||||
defer close(promise)
|
||||
|
||||
for {
|
||||
server, err = s.api.GetServer(instanceID)
|
||||
if err != nil {
|
||||
promise <- false
|
||||
return
|
||||
}
|
||||
|
||||
if currentState != server.State {
|
||||
currentState = server.State
|
||||
}
|
||||
|
||||
if server.State == "running" {
|
||||
break
|
||||
}
|
||||
if server.State == "stopped" {
|
||||
promise <- false
|
||||
return
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
ip := server.PublicAddress.IP
|
||||
if ip == "" && server.EnableIPV6 {
|
||||
ip = fmt.Sprintf("[%s]", server.IPV6.Address)
|
||||
}
|
||||
dest := fmt.Sprintf("%s:22", ip)
|
||||
for {
|
||||
conn, err := net.Dial("tcp", dest)
|
||||
if err == nil {
|
||||
defer conn.Close()
|
||||
break
|
||||
} else {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
promise <- true
|
||||
}()
|
||||
|
||||
loop := 0
|
||||
for {
|
||||
select {
|
||||
case done := <-promise:
|
||||
if !done {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Server %s started", instanceID)
|
||||
return nil
|
||||
case <-time.After(time.Millisecond * 100):
|
||||
loop = loop + 1
|
||||
if loop == 5 {
|
||||
loop = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getSSHAuth is uses to get the ssh.Signer needed to connect via SSH
|
||||
func getSSHAuth(sshKeyPath string) (ssh.Signer, error) {
|
||||
f, err := os.Open(sshKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
buf, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signer, err := ssh.ParsePrivateKey(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signer, err
|
||||
}
|
||||
|
||||
// CopyImageToInstance copies the image to the instance via ssh
|
||||
func (s *ScalewayClient) CopyImageToInstance(instanceID, path, sshKeyPath string) error {
|
||||
_, base := filepath.Split(path)
|
||||
s.fileName = base
|
||||
|
||||
server, err := s.api.GetServer(instanceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signer, err := getSSHAuth(sshKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.sshConfig = &ssh.ClientConfig{
|
||||
User: "root",
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // TODO validate server before?
|
||||
}
|
||||
|
||||
client, err := ssh.Dial("tcp", server.PublicAddress.IP+":22", s.sshConfig) // TODO remove hardocoded port?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// code taken from bramvdbogaerde/go-scp
|
||||
contentBytes, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytesReader := bytes.NewReader(contentBytes)
|
||||
|
||||
log.Infof("Starting to upload %s on server", base)
|
||||
|
||||
go func() {
|
||||
w, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer w.Close()
|
||||
fmt.Fprintln(w, "C0600", int64(len(contentBytes)), base)
|
||||
io.Copy(w, bytesReader)
|
||||
fmt.Fprintln(w, "\x00")
|
||||
}()
|
||||
|
||||
session.Run("/usr/bin/scp -t /root/") // TODO remove hardcoded remote path?
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteImageToVolume does a dd command on the remote instance via ssh
|
||||
func (s *ScalewayClient) WriteImageToVolume(instanceID, deviceName string) error {
|
||||
server, err := s.api.GetServer(instanceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := ssh.Dial("tcp", server.PublicAddress.IP+":22", s.sshConfig) // TODO remove hardcoded port + use the same dial as before?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
var ddPathBuf bytes.Buffer
|
||||
session.Stdout = &ddPathBuf
|
||||
|
||||
err = session.Run("which dd") // get the right path
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session, err = client.NewSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
ddCommand := strings.Trim(ddPathBuf.String(), " \n")
|
||||
command := fmt.Sprintf("%s if=%s of=%s", ddCommand, s.fileName, deviceName)
|
||||
|
||||
log.Infof("Starting writing iso to disk")
|
||||
|
||||
err = session.Run(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("ISO image written to disk")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminateInstance terminates the instance and wait for termination
|
||||
func (s *ScalewayClient) TerminateInstance(instanceID string) error {
|
||||
server, err := s.api.GetServer(instanceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Shutting down server %s", instanceID)
|
||||
|
||||
err = s.api.PostServerAction(server.Identifier, "poweroff")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// code taken from scaleway-cli
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
var currentState string
|
||||
|
||||
log.Debugf("Waiting for server to shutdown")
|
||||
|
||||
for {
|
||||
server, err = s.api.GetServer(instanceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentState != server.State {
|
||||
currentState = server.State
|
||||
}
|
||||
if server.State == "stopped" {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateScalewayImage creates the image and delete old image and snapshot if same name
|
||||
func (s *ScalewayClient) CreateScalewayImage(instanceID, volumeID, name string) error {
|
||||
oldImage, err := s.api.GetImageID(name, "x86_64")
|
||||
if err == nil {
|
||||
err = s.api.DeleteImage(oldImage.Identifier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
oldSnapshot, err := s.api.GetSnapshotID(name)
|
||||
if err == nil {
|
||||
err := s.api.DeleteSnapshot(oldSnapshot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
snapshotID, err := s.api.PostSnapshot(volumeID, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imageID, err := s.api.PostImage(snapshotID, name, "", "x86_64") // TODO remove hardcoded arch
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Image %s with ID %s created", name, imageID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteInstanceAndVolumes deletes the instance and the volumes attached
|
||||
func (s *ScalewayClient) DeleteInstanceAndVolumes(instanceID string) error {
|
||||
server, err := s.api.GetServer(instanceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.api.DeleteServer(instanceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, volume := range server.Volumes {
|
||||
err = s.api.DeleteVolume(volume.Identifier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Server %s deleted", instanceID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateLinuxkitInstance creates an instance with the given linuxkit image
|
||||
func (s *ScalewayClient) CreateLinuxkitInstance(instanceName, imageName, instanceType string) (string, error) {
|
||||
// get the image ID
|
||||
image, err := s.api.GetImageID(imageName, "x86_64") // TODO fix arch and use from args
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
imageID := image.Identifier
|
||||
|
||||
var serverDefinition types.ScalewayServerDefinition
|
||||
|
||||
serverDefinition.Name = instanceName
|
||||
serverDefinition.Image = &imageID
|
||||
serverDefinition.CommercialType = instanceType
|
||||
serverDefinition.BootType = "local"
|
||||
|
||||
log.Debugf("Creating volume on Scaleway")
|
||||
|
||||
log.Debugf("Creating server %s on Scaleway", serverDefinition.Name)
|
||||
serverID, err := s.api.PostServer(serverDefinition)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return serverID, nil
|
||||
}
|
||||
|
||||
// BootInstance boots the specified instance, and don't wait
|
||||
func (s *ScalewayClient) BootInstance(instanceID string) error {
|
||||
err := s.api.PostServerAction(instanceID, "poweron")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnectSerialPort connects to the serial port of the instance
|
||||
func (s *ScalewayClient) ConnectSerialPort(instanceID string) error {
|
||||
var gottyURL string
|
||||
switch s.region {
|
||||
case "par1":
|
||||
gottyURL = "https://tty-par1.scaleway.com/v2/"
|
||||
case "ams1":
|
||||
gottyURL = "https://tty-ams1.scaleway.com/"
|
||||
default:
|
||||
return errors.New("Instance have no region")
|
||||
}
|
||||
|
||||
fullURL := fmt.Sprintf("%s?arg=%s&arg=%s", gottyURL, s.api.Token, instanceID)
|
||||
|
||||
log.Debugf("Connection to ", fullURL)
|
||||
gottyClient, err := gotty.NewClient(fullURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gottyClient.SkipTLSVerify = true
|
||||
|
||||
gottyClient.UseProxyFromEnv = true
|
||||
|
||||
err = gottyClient.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
fmt.Println("You are connected, type 'Ctrl+q' to quit.")
|
||||
go func() {
|
||||
err = gottyClient.Loop()
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: " + err.Error())
|
||||
}
|
||||
//gottyClient.Close()
|
||||
done <- true
|
||||
}()
|
||||
<-done
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user